• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hazendaz / httpunit / #156

20 Aug 2024 11:31PM UTC coverage: 80.537% (+0.003%) from 80.534%
#156

push

github

hazendaz
[format] Auto format the code

3232 of 4121 branches covered (78.43%)

Branch coverage included in aggregate %.

68 of 80 new or added lines in 21 files covered. (85.0%)

4 existing lines in 4 files now uncovered.

8288 of 10183 relevant lines covered (81.39%)

0.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.35
/src/main/java/com/meterware/servletunit/WebApplication.java
1
/*
2
 * MIT License
3
 *
4
 * Copyright 2011-2024 Russell Gold
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
7
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
8
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
9
 * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10
 *
11
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions
12
 * of the Software.
13
 *
14
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
15
 * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
17
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
18
 * DEALINGS IN THE SOFTWARE.
19
 */
20
package com.meterware.servletunit;
21

22
import com.meterware.httpunit.HttpInternalErrorException;
23
import com.meterware.httpunit.HttpNotFoundException;
24
import com.meterware.httpunit.HttpUnitUtils;
25

26
import java.io.File;
27
import java.io.IOException;
28
import java.lang.reflect.InvocationTargetException;
29
import java.net.MalformedURLException;
30
import java.net.URL;
31
import java.util.ArrayList;
32
import java.util.Collection;
33
import java.util.Collections;
34
import java.util.Dictionary;
35
import java.util.HashMap;
36
import java.util.Hashtable;
37
import java.util.Iterator;
38
import java.util.List;
39
import java.util.ListIterator;
40
import java.util.Map;
41

42
import javax.servlet.Filter;
43
import javax.servlet.Servlet;
44
import javax.servlet.ServletContext;
45
import javax.servlet.ServletContextAttributeEvent;
46
import javax.servlet.ServletContextAttributeListener;
47
import javax.servlet.ServletContextEvent;
48
import javax.servlet.ServletContextListener;
49
import javax.servlet.ServletException;
50
import javax.servlet.http.HttpServlet;
51
import javax.servlet.http.HttpServletRequest;
52
import javax.servlet.http.HttpServletResponse;
53
import javax.servlet.http.HttpSession;
54
import javax.servlet.http.HttpSessionAttributeListener;
55
import javax.servlet.http.HttpSessionBindingEvent;
56
import javax.servlet.http.HttpSessionEvent;
57
import javax.servlet.http.HttpSessionListener;
58

59
import org.w3c.dom.Document;
60
import org.w3c.dom.Element;
61
import org.w3c.dom.NodeList;
62
import org.xml.sax.SAXException;
63

64
/**
65
 * This class represents the information recorded about a single web application. It is usually extracted from web.xml.
66
 *
67
 * @author <a href="mailto:russgold@httpunit.org">Russell Gold</a>
68
 * @author <a href="balld@webslingerZ.com">Donald Ball</a>
69
 * @author <a href="jaydunning@users.sourceforge.net">Jay Dunning</a>
70
 **/
71
class WebApplication implements SessionListenerDispatcher {
72

73
    private static final SecurityConstraint NULL_SECURITY_CONSTRAINT = new NullSecurityConstraint();
1✔
74

75
    private final ServletConfiguration SECURITY_CHECK_CONFIGURATION = new ServletConfiguration(
1✔
76
            SecurityCheckServlet.class.getName());
1✔
77

78
    private final WebResourceMapping SECURITY_CHECK_MAPPING = new WebResourceMapping(SECURITY_CHECK_CONFIGURATION);
1✔
79

80
    /** A mapping of resource names to servlet configurations. **/
81
    private WebResourceMap _servletMapping = new WebResourceMap();
1✔
82

83
    /** A mapping of filter names to FilterConfigurations */
84
    private Hashtable _filters = new Hashtable();
1✔
85

86
    /** A mapping of servlet names to ServletConfigurations */
87
    private Hashtable _servlets = new Hashtable();
1✔
88

89
    /** A mapping of resource names to filter configurations. **/
90
    private FilterUrlMap _filterUrlMapping = new FilterUrlMap();
1✔
91

92
    /** A mapping of servlet names to filter configurations. **/
93
    private Hashtable _filterMapping = new Hashtable();
1✔
94

95
    private List<SecurityConstraint> _securityConstraints = new ArrayList<>();
1✔
96

97
    private List<ServletContextListener> _contextListeners = new ArrayList<>();
1✔
98

99
    private List<ServletContextAttributeListener> _contextAttributeListeners = new ArrayList<>();
1✔
100

101
    private List<HttpSessionListener> _sessionListeners = new ArrayList<>();
1✔
102

103
    private List<HttpSessionAttributeListener> _sessionAttributeListeners = new ArrayList<>();
1✔
104

105
    private boolean _useBasicAuthentication;
106

107
    private boolean _useFormAuthentication;
108

109
    private String _authenticationRealm = "";
1✔
110

111
    private URL _loginURL;
112

113
    private URL _errorURL;
114

115
    private Hashtable _contextParameters = new Hashtable();
1✔
116

117
    private File _contextDir = null;
1✔
118

119
    private String _contextPath = null;
1✔
120

121
    private ServletUnitServletContext _servletContext;
122

123
    private String _displayName;
124

125
    /**
126
     * Constructs a default application spec with no information.
127
     */
128
    WebApplication() {
1✔
129
        _contextPath = "";
1✔
130
    }
1✔
131

132
    /**
133
     * Constructs an application spec from an XML document.
134
     */
135
    WebApplication(Document document) throws MalformedURLException, SAXException {
136
        this(document, null, "");
1✔
137
    }
1✔
138

139
    /**
140
     * Constructs an application spec from an XML document.
141
     */
142
    WebApplication(Document document, String contextPath) throws MalformedURLException, SAXException {
143
        this(document, null, contextPath);
1✔
144
    }
1✔
145

146
    /**
147
     * Constructs an application spec from an XML document.
148
     */
149
    WebApplication(Document document, File file, String contextPath) throws MalformedURLException, SAXException {
1✔
150
        if (contextPath != null && contextPath.length() > 0 && !contextPath.startsWith("/")) {
1!
151
            throw new IllegalArgumentException("Context path " + contextPath + " must start with '/'");
×
152
        }
153
        _contextDir = file;
1✔
154
        _contextPath = contextPath == null ? "" : contextPath;
1✔
155
        NodeList nl = document.getElementsByTagName("display-name");
1✔
156
        if (nl.getLength() > 0) {
1✔
157
            _displayName = XMLUtils.getTextValue(nl.item(0)).trim();
1✔
158
        }
159

160
        registerServlets(document);
1✔
161
        registerFilters(document);
1✔
162
        extractSecurityConstraints(document);
1✔
163
        extractContextParameters(document);
1✔
164
        extractLoginConfiguration(document);
1✔
165
        extractListeners(document);
1✔
166
        notifyContextInitialized();
1✔
167
        _servletMapping.autoLoadServlets();
1✔
168
    }
1✔
169

170
    private void extractListeners(Document document) throws SAXException {
171
        NodeList nl = document.getElementsByTagName("listener");
1✔
172
        for (int i = 0; i < nl.getLength(); i++) {
1✔
173
            String listenerName = XMLUtils.getChildNodeValue((Element) nl.item(i), "listener-class").trim();
1✔
174
            try {
175
                Object listener = Class.forName(listenerName).getDeclaredConstructor().newInstance();
1✔
176

177
                if (listener instanceof ServletContextListener) {
1✔
178
                    _contextListeners.add((ServletContextListener) listener);
1✔
179
                }
180
                if (listener instanceof ServletContextAttributeListener) {
1✔
181
                    _contextAttributeListeners.add((ServletContextAttributeListener) listener);
1✔
182
                }
183
                if (listener instanceof HttpSessionListener) {
1✔
184
                    _sessionListeners.add((HttpSessionListener) listener);
1✔
185
                }
186
                if (listener instanceof HttpSessionAttributeListener) {
1✔
187
                    _sessionAttributeListeners.add((HttpSessionAttributeListener) listener);
1✔
188
                }
189
            } catch (Throwable e) {
×
190
                throw new RuntimeException("Unable to load context listener " + listenerName + ": " + e.toString());
×
191
            }
1✔
192
        }
193
    }
1✔
194

195
    private void notifyContextInitialized() {
196
        ServletContextEvent event = new ServletContextEvent(getServletContext());
1✔
197

198
        for (Iterator i = _contextListeners.iterator(); i.hasNext();) {
1✔
199
            ServletContextListener listener = (ServletContextListener) i.next();
1✔
200
            listener.contextInitialized(event);
1✔
201
        }
1✔
202
    }
1✔
203

204
    void shutDown() {
205
        destroyServlets();
1✔
206
        notifyContextDestroyed();
1✔
207
    }
1✔
208

209
    private void notifyContextDestroyed() {
210
        ServletContextEvent event = new ServletContextEvent(getServletContext());
1✔
211

212
        for (ListIterator i = _contextListeners.listIterator(_contextListeners.size()); i.hasPrevious();) {
1✔
213
            ServletContextListener listener = (ServletContextListener) i.previous();
1✔
214
            listener.contextDestroyed(event);
1✔
215
        }
1✔
216
    }
1✔
217

218
    void sendAttributeAdded(String name, Object value) {
219
        ServletContextAttributeEvent event = new ServletContextAttributeEvent(getServletContext(), name, value);
1✔
220

221
        for (Iterator i = _contextAttributeListeners.iterator(); i.hasNext();) {
1✔
222
            ServletContextAttributeListener listener = (ServletContextAttributeListener) i.next();
1✔
223
            listener.attributeAdded(event);
1✔
224
        }
1✔
225
    }
1✔
226

227
    void sendAttributeReplaced(String name, Object value) {
228
        ServletContextAttributeEvent event = new ServletContextAttributeEvent(getServletContext(), name, value);
1✔
229

230
        for (Iterator i = _contextAttributeListeners.iterator(); i.hasNext();) {
1✔
231
            ServletContextAttributeListener listener = (ServletContextAttributeListener) i.next();
1✔
232
            listener.attributeReplaced(event);
1✔
233
        }
1✔
234
    }
1✔
235

236
    void sendAttributeRemoved(String name, Object value) {
237
        ServletContextAttributeEvent event = new ServletContextAttributeEvent(getServletContext(), name, value);
1✔
238

239
        for (Iterator i = _contextAttributeListeners.iterator(); i.hasNext();) {
1✔
240
            ServletContextAttributeListener listener = (ServletContextAttributeListener) i.next();
1✔
241
            listener.attributeRemoved(event);
1✔
242
        }
1✔
243
    }
1✔
244

245
    private void extractSecurityConstraints(Document document) throws SAXException {
246
        NodeList nl = document.getElementsByTagName("security-constraint");
1✔
247
        for (int i = 0; i < nl.getLength(); i++) {
1✔
248
            _securityConstraints.add(new SecurityConstraintImpl((Element) nl.item(i)));
1✔
249
        }
250
    }
1✔
251

252
    String getContextPath() {
253
        return _contextPath;
1✔
254
    }
255

256
    ServletContext getServletContext() {
257
        if (_servletContext == null) {
1✔
258
            _servletContext = new ServletUnitServletContext(this);
1✔
259
        }
260
        return _servletContext;
1✔
261
    }
262

263
    /**
264
     * Registers a servlet class to be run.
265
     **/
266
    void registerServlet(String resourceName, String servletClassName, Hashtable initParams) {
267
        registerServlet(resourceName, new ServletConfiguration(servletClassName, initParams));
1✔
268
    }
1✔
269

270
    /**
271
     * Registers a servlet to be run.
272
     **/
273
    void registerServlet(String resourceName, ServletConfiguration servletConfiguration) {
274
        // FIXME - shouldn't everything start with one or the other?
275
        if (!resourceName.startsWith("/") && !resourceName.startsWith("*")) {
1✔
276
            resourceName = "/" + resourceName;
1✔
277
        }
278
        _servletMapping.put(resourceName, servletConfiguration);
1✔
279
    }
1✔
280

281
    /**
282
     * Calls the destroy method for every active servlet.
283
     */
284
    void destroyServlets() {
285
        _servletMapping.destroyWebResources();
1✔
286
    }
1✔
287

288
    ServletMetaData getServletRequest(URL url) {
289
        return _servletMapping.get(url);
1✔
290
    }
291

292
    /**
293
     * Returns true if this application uses Basic Authentication.
294
     */
295
    boolean usesBasicAuthentication() {
296
        return _useBasicAuthentication;
1✔
297
    }
298

299
    /**
300
     * Returns true if this application uses form-based authentication.
301
     */
302
    boolean usesFormAuthentication() {
303
        return _useFormAuthentication;
1✔
304
    }
305

306
    String getAuthenticationRealm() {
307
        return _authenticationRealm;
1✔
308
    }
309

310
    URL getLoginURL() {
311
        return _loginURL;
1✔
312
    }
313

314
    URL getErrorURL() {
315
        return _errorURL;
1✔
316
    }
317

318
    /**
319
     * Returns true if the specified path may only be accesses by an authorized user.
320
     *
321
     * @param url
322
     *            the application-relative path of the URL
323
     */
324
    boolean requiresAuthorization(URL url) {
325
        String result;
326
        String file = url.getFile();
1✔
327
        if (_contextPath.equals("")) {
1✔
328
            result = file;
1✔
329
        } else if (file.startsWith(_contextPath)) {
1!
330
            result = file.substring(_contextPath.length());
1✔
331
        } else {
332
            result = null;
×
333
        }
334
        return getControllingConstraint(result) != NULL_SECURITY_CONSTRAINT;
1✔
335
    }
336

337
    /**
338
     * Returns an array containing the roles permitted to access the specified URL.
339
     */
340
    String[] getPermittedRoles(URL url) {
341
        String result;
342
        String file = url.getFile();
1✔
343
        if (_contextPath.equals("")) {
1✔
344
            result = file;
1✔
345
        } else if (file.startsWith(_contextPath)) {
1!
346
            result = file.substring(_contextPath.length());
1✔
347
        } else {
348
            result = null;
×
349
        }
350
        return getControllingConstraint(result).getPermittedRoles();
1✔
351
    }
352

353
    private SecurityConstraint getControllingConstraint(String urlPath) {
354
        for (SecurityConstraint sc : _securityConstraints) {
1✔
355
            if (sc.controlsPath(urlPath)) {
1✔
356
                return sc;
1✔
357
            }
358
        }
1✔
359
        return NULL_SECURITY_CONSTRAINT;
1✔
360
    }
361

362
    File getResourceFile(String path) {
363
        String relativePath = path.startsWith("/") ? path.substring(1) : path;
1✔
364
        if (_contextDir == null) {
1✔
365
            return new File(relativePath);
1✔
366
        }
367
        return new File(_contextDir, relativePath);
1✔
368
    }
369

370
    Hashtable getContextParameters() {
371
        return _contextParameters;
1✔
372
    }
373

374
    // ---------------------------------------- SessionListenerDispatcher methods
375
    // -------------------------------------------
376

377
    @Override
378
    public void sendSessionCreated(HttpSession session) {
379
        HttpSessionEvent event = new HttpSessionEvent(session);
1✔
380

381
        for (HttpSessionListener listener : _sessionListeners) {
1✔
382
            listener.sessionCreated(event);
1✔
383
        }
1✔
384
    }
1✔
385

386
    @Override
387
    public void sendSessionDestroyed(HttpSession session) {
388
        HttpSessionEvent event = new HttpSessionEvent(session);
1✔
389

390
        for (HttpSessionListener listener : _sessionListeners) {
1✔
391
            listener.sessionDestroyed(event);
1✔
392
        }
1✔
393
    }
1✔
394

395
    @Override
396
    public void sendAttributeAdded(HttpSession session, String name, Object value) {
397
        HttpSessionBindingEvent event = new HttpSessionBindingEvent(session, name, value);
1✔
398

399
        for (HttpSessionAttributeListener listener : _sessionAttributeListeners) {
1✔
400
            listener.attributeAdded(event);
1✔
401
        }
1✔
402
    }
1✔
403

404
    @Override
405
    public void sendAttributeReplaced(HttpSession session, String name, Object oldValue) {
406
        HttpSessionBindingEvent event = new HttpSessionBindingEvent(session, name, oldValue);
1✔
407

408
        for (HttpSessionAttributeListener listener : _sessionAttributeListeners) {
1✔
409
            listener.attributeReplaced(event);
1✔
410
        }
1✔
411
    }
1✔
412

413
    @Override
414
    public void sendAttributeRemoved(HttpSession session, String name, Object oldValue) {
415
        HttpSessionBindingEvent event = new HttpSessionBindingEvent(session, name, oldValue);
1✔
416

417
        for (HttpSessionAttributeListener listener : _sessionAttributeListeners) {
1✔
418
            listener.attributeRemoved(event);
1✔
419
        }
1✔
420
    }
1✔
421

422
    // --------------------------------------------------- private members
423
    // --------------------------------------------------
424

425
    private void registerFilters(Document document) throws SAXException {
426
        Hashtable nameToClass = new Hashtable();
1✔
427
        NodeList nl = document.getElementsByTagName("filter");
1✔
428
        for (int i = 0; i < nl.getLength(); i++) {
1✔
429
            registerFilterClass(nameToClass, (Element) nl.item(i));
1✔
430
        }
431
        nl = document.getElementsByTagName("filter-mapping");
1✔
432
        for (int i = 0; i < nl.getLength(); i++) {
1✔
433
            registerFilter(nameToClass, (Element) nl.item(i));
1✔
434
        }
435
        this._filters = nameToClass;
1✔
436
    }
1✔
437

438
    private void registerFilterClass(Dictionary mapping, Element filterElement) throws SAXException {
439
        String filterName = XMLUtils.getChildNodeValue(filterElement, "filter-name");
1✔
440
        mapping.put(filterName, new FilterConfiguration(filterName, filterElement));
1✔
441
    }
1✔
442

443
    private void registerFilter(Dictionary mapping, Element filterElement) throws SAXException {
444
        if (XMLUtils.hasChildNode(filterElement, "servlet-name")) {
1✔
445
            registerFilterForServlet(XMLUtils.getChildNodeValue(filterElement, "servlet-name"),
1✔
446
                    (FilterConfiguration) mapping.get(XMLUtils.getChildNodeValue(filterElement, "filter-name")));
1✔
447
        }
448
        if (XMLUtils.hasChildNode(filterElement, "url-pattern")) {
1✔
449
            registerFilterForUrl(XMLUtils.getChildNodeValue(filterElement, "url-pattern"),
1✔
450
                    (FilterConfiguration) mapping.get(XMLUtils.getChildNodeValue(filterElement, "filter-name")));
1✔
451
        }
452
    }
1✔
453

454
    private void registerFilterForUrl(String resourceName, FilterConfiguration filterConfiguration) {
455
        _filterUrlMapping.put(resourceName, filterConfiguration);
1✔
456
    }
1✔
457

458
    private void registerFilterForServlet(String servletName, FilterConfiguration filterConfiguration) {
459
        List list = (List) _filterMapping.get(servletName);
1✔
460
        if (list == null) {
1✔
461
            list = new ArrayList();
1✔
462
            _filterMapping.put(servletName, list);
1✔
463
        }
464
        list.add(filterConfiguration);
1✔
465
    }
1✔
466

467
    private void extractLoginConfiguration(Document document) throws MalformedURLException, SAXException {
468
        NodeList nl = document.getElementsByTagName("login-config");
1✔
469
        if (nl.getLength() == 1) {
1✔
470
            final Element loginConfigElement = (Element) nl.item(0);
1✔
471
            String authenticationMethod = XMLUtils.getChildNodeValue(loginConfigElement, "auth-method", "BASIC");
1✔
472
            _authenticationRealm = XMLUtils.getChildNodeValue(loginConfigElement, "realm-name", "");
1✔
473
            if (authenticationMethod.equalsIgnoreCase("BASIC")) {
1✔
474
                _useBasicAuthentication = true;
1✔
475
                if (_authenticationRealm.length() == 0) {
1!
476
                    throw new SAXException("No realm specified for BASIC Authorization");
×
477
                }
478
            } else if (authenticationMethod.equalsIgnoreCase("FORM")) {
1!
479
                _useFormAuthentication = true;
1✔
480
                if (_authenticationRealm.length() == 0) {
1!
481
                    throw new SAXException("No realm specified for FORM Authorization");
×
482
                }
483
                _loginURL = new URL("http", "localhost",
1✔
484
                        _contextPath + XMLUtils.getChildNodeValue(loginConfigElement, "form-login-page"));
1✔
485
                _errorURL = new URL("http", "localhost",
1✔
486
                        _contextPath + XMLUtils.getChildNodeValue(loginConfigElement, "form-error-page"));
1✔
487
            }
488
        }
489
    }
1✔
490

491
    private void registerServlets(Document document) throws SAXException {
492
        Hashtable nameToClass = new Hashtable();
1✔
493
        NodeList nl = document.getElementsByTagName("servlet");
1✔
494
        for (int i = 0; i < nl.getLength(); i++) {
1✔
495
            registerServletClass(nameToClass, (Element) nl.item(i));
1✔
496
        }
497
        nl = document.getElementsByTagName("servlet-mapping");
1✔
498
        for (int i = 0; i < nl.getLength(); i++) {
1✔
499
            registerServlet(nameToClass, (Element) nl.item(i));
1✔
500
        }
501
        this._servlets = nameToClass;
1✔
502
    }
1✔
503

504
    private void registerServletClass(Dictionary mapping, Element servletElement) throws SAXException {
505
        mapping.put(XMLUtils.getChildNodeValue(servletElement, "servlet-name"),
1✔
506
                new ServletConfiguration(servletElement));
507
    }
1✔
508

509
    private void registerServlet(Dictionary mapping, Element servletElement) throws SAXException {
510
        registerServlet(XMLUtils.getChildNodeValue(servletElement, "url-pattern"),
1✔
511
                (ServletConfiguration) mapping.get(XMLUtils.getChildNodeValue(servletElement, "servlet-name")));
1✔
512
    }
1✔
513

514
    private void extractContextParameters(Document document) throws SAXException {
515
        NodeList nl = document.getElementsByTagName("context-param");
1✔
516
        for (int i = 0; i < nl.getLength(); i++) {
1✔
517
            Element param = (Element) nl.item(i);
1✔
518
            String name = XMLUtils.getChildNodeValue(param, "param-name");
1✔
519
            String value = XMLUtils.getChildNodeValue(param, "param-value");
1✔
520
            _contextParameters.put(name, value);
1✔
521
        }
522
    }
1✔
523

524
    private static boolean patternMatches(String urlPattern, String urlPath) {
525
        return urlPattern.equals(urlPath);
1✔
526
    }
527

528
    String getDisplayName() {
529
        return _displayName;
1✔
530
    }
531

532
    // ============================================= SecurityCheckServlet class
533
    // =============================================
534

535
    static class SecurityCheckServlet extends HttpServlet {
1✔
536

537
        private static final long serialVersionUID = 1L;
538

539
        @Override
540
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
541
            handleLogin(req, resp);
×
542
        }
×
543

544
        @Override
545
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
546
            handleLogin(req, resp);
1✔
547
        }
1✔
548

549
        private void handleLogin(HttpServletRequest req, HttpServletResponse resp) throws IOException {
550
            final String username = req.getParameter("j_username");
1✔
551
            final String roleList = req.getParameter("j_password");
1✔
552
            getServletSession(req).setUserInformation(username, ServletUnitHttpRequest.toArray(roleList));
1✔
553
            resp.sendRedirect(getServletSession(req).getOriginalURL().toExternalForm());
1✔
554
        }
1✔
555

556
        private ServletUnitHttpSession getServletSession(HttpServletRequest req) {
557
            return (ServletUnitHttpSession) req.getSession();
1✔
558
        }
559

560
    }
561

562
    // ============================================= ServletConfiguration class
563
    // =============================================
564

565
    static final int DONT_AUTOLOAD = Integer.MIN_VALUE;
566
    static final int ANY_LOAD_ORDER = Integer.MAX_VALUE;
567

568
    class ServletConfiguration extends WebResourceConfiguration {
569

570
        private Servlet _servlet;
571
        private String _servletName;
572
        private String _jspFile;
573
        private int _loadOrder = DONT_AUTOLOAD;
1✔
574

575
        ServletConfiguration(String className) {
1✔
576
            super(className);
1✔
577
        }
1✔
578

579
        ServletConfiguration(String className, Hashtable initParams) {
1✔
580
            super(className, initParams);
1✔
581
        }
1✔
582

583
        ServletConfiguration(Element servletElement) throws SAXException {
1✔
584
            super(servletElement, "servlet-class", XMLUtils.getChildNodeValue(servletElement, "servlet-class",
1✔
585
                    "org.apache.jasper.servlet.JspServlet"));
586
            _servletName = XMLUtils.getChildNodeValue(servletElement, "servlet-name");
1✔
587
            _jspFile = XMLUtils.getChildNodeValue(servletElement, "jsp-file", "");
1✔
588
            if ("".equals(_jspFile)) {
1!
589
                _jspFile = null;
1✔
590
            }
591
            final NodeList loadOrder = servletElement.getElementsByTagName("load-on-startup");
1✔
592
            for (int i = 0; i < loadOrder.getLength(); i++) {
1✔
593
                String order = XMLUtils.getTextValue(loadOrder.item(i));
1✔
594
                try {
595
                    _loadOrder = Integer.parseInt(order);
1✔
596
                } catch (NumberFormatException e) {
1✔
597
                    _loadOrder = ANY_LOAD_ORDER;
1✔
598
                }
1✔
599
            }
600
        }
1✔
601

602
        synchronized Servlet getServlet()
603
                throws ClassNotFoundException, InstantiationException, IllegalAccessException, ServletException,
604
                IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
605
            if (_servlet == null) {
1✔
606
                Class servletClass = Class.forName(getClassName());
1✔
607
                _servlet = (Servlet) servletClass.getDeclaredConstructor().newInstance();
1✔
608
                String servletName = _servletName != null ? _servletName : _servlet.getClass().getName();
1✔
609
                _servlet.init(new ServletUnitServletConfig(servletName, WebApplication.this, getInitParams()));
1✔
610
            }
611

612
            return _servlet;
1✔
613
        }
614

615
        @Override
616
        synchronized void destroyResource() {
617
            if (_servlet != null) {
1✔
618
                _servlet.destroy();
1✔
619
            }
620
        }
1✔
621

622
        String getServletName() {
623
            return _servletName;
1✔
624
        }
625

626
        @Override
627
        boolean isLoadOnStartup() {
628
            return _loadOrder != DONT_AUTOLOAD;
1✔
629
        }
630

631
        public int getLoadOrder() {
632
            return _loadOrder;
1✔
633
        }
634

635
        public Object getJspFile() {
636
            return this._jspFile;
×
637
        }
638
    }
639

640
    // ============================================= FilterConfiguration class
641
    // =============================================
642

643
    class FilterConfiguration extends WebResourceConfiguration implements FilterMetaData {
644

645
        private Filter _filter;
646
        private String _name;
647

648
        FilterConfiguration(String name, Element filterElement) throws SAXException {
1✔
649
            super(filterElement, "filter-class");
1✔
650
            _name = name;
1✔
651
        }
1✔
652

653
        @Override
654
        public synchronized Filter getFilter() throws ServletException {
655
            try {
656
                if (_filter == null) {
1✔
657
                    Class filterClass = Class.forName(getClassName());
1✔
658
                    _filter = (Filter) filterClass.getDeclaredConstructor().newInstance();
1✔
659
                    _filter.init(new FilterConfigImpl(_name, getServletContext(), getInitParams()));
1✔
660
                }
661

662
                return _filter;
1✔
663
            } catch (ClassNotFoundException e) {
×
664
                throw new ServletException("Did not find filter class: " + getClassName());
×
665
            } catch (IllegalAccessException e) {
×
666
                throw new ServletException("Filter class " + getClassName() + " lacks a public no-arg constructor");
×
NEW
667
            } catch (InstantiationException | IllegalArgumentException | InvocationTargetException
×
668
                    | NoSuchMethodException | SecurityException e) {
669
                throw new ServletException("Filter class " + getClassName() + " could not be instantiated.");
×
670
            } catch (ClassCastException e) {
×
671
                throw new ServletException(
×
672
                        "Filter class " + getClassName() + " does not implement" + Filter.class.getName());
×
673
            }
674
        }
675

676
        @Override
677
        boolean isLoadOnStartup() {
678
            return false;
×
679
        }
680

681
        @Override
682
        synchronized void destroyResource() {
683
            if (_filter != null) {
×
684
                _filter.destroy();
×
685
            }
686
        }
×
687
    }
688

689
    // =================================== SecurityConstract interface and implementations
690
    // ==================================
691

692
    interface SecurityConstraint {
693

694
        boolean controlsPath(String urlPath);
695

696
        String[] getPermittedRoles();
697
    }
698

699
    static class NullSecurityConstraint implements SecurityConstraint {
1✔
700

701
        private static final String[] NO_ROLES = {};
1✔
702

703
        @Override
704
        public boolean controlsPath(String urlPath) {
705
            return false;
×
706
        }
707

708
        @Override
709
        public String[] getPermittedRoles() {
710
            return NO_ROLES;
×
711
        }
712
    }
713

714
    static class SecurityConstraintImpl implements SecurityConstraint {
715

716
        SecurityConstraintImpl(Element root) throws SAXException {
1✔
717
            final NodeList roleNames = root.getElementsByTagName("role-name");
1✔
718
            for (int i = 0; i < roleNames.getLength(); i++) {
1✔
719
                _roleList.add(XMLUtils.getTextValue(roleNames.item(i)));
1✔
720
            }
721

722
            final NodeList resources = root.getElementsByTagName("web-resource-collection");
1✔
723
            for (int i = 0; i < resources.getLength(); i++) {
1✔
724
                _resources.add(new WebResourceCollection((Element) resources.item(i)));
1✔
725
            }
726
        }
1✔
727

728
        @Override
729
        public boolean controlsPath(String urlPath) {
730
            return getMatchingCollection(urlPath) != null;
1✔
731
        }
732

733
        @Override
734
        public String[] getPermittedRoles() {
735
            if (_roles == null) {
1✔
736
                _roles = _roleList.toArray(new String[_roleList.size()]);
1✔
737
            }
738
            return _roles;
1✔
739
        }
740

741
        private String[] _roles;
742
        private List<String> _roleList = new ArrayList<>();
1✔
743
        private List<WebResourceCollection> _resources = new ArrayList<>();
1✔
744

745
        public WebResourceCollection getMatchingCollection(String urlPath) {
746
            for (WebResourceCollection wrc : _resources) {
1✔
747
                if (wrc.controlsPath(urlPath)) {
1✔
748
                    return wrc;
1✔
749
                }
750
            }
1✔
751
            return null;
1✔
752
        }
753

754
        class WebResourceCollection {
755

756
            WebResourceCollection(Element root) throws SAXException {
1✔
757
                final NodeList urlPatterns = root.getElementsByTagName("url-pattern");
1✔
758
                for (int i = 0; i < urlPatterns.getLength(); i++) {
1✔
759
                    _urlPatterns.add(XMLUtils.getTextValue(urlPatterns.item(i)));
1✔
760
                }
761
            }
1✔
762

763
            boolean controlsPath(String urlPath) {
764
                for (String pattern : _urlPatterns) {
1✔
765
                    if (patternMatches(pattern, urlPath)) {
1✔
766
                        return true;
1✔
767
                    }
768
                }
1✔
769
                return false;
1✔
770
            }
771

772
            private List<String> _urlPatterns = new ArrayList<>();
1✔
773
        }
774
    }
775

776
    static final FilterMetaData[] NO_FILTERS = {};
1✔
777

778
    static class ServletRequestImpl implements ServletMetaData {
779

780
        private URL _url;
781
        private String _fullServletPath;
782
        private WebResourceMapping _mapping;
783
        private Hashtable _filtersPerName;
784
        private FilterUrlMap _filtersPerUrl;
785

786
        ServletRequestImpl(URL url, String servletPath, WebResourceMapping mapping, Hashtable filtersPerName,
787
                FilterUrlMap filtersPerUrl) {
1✔
788
            _url = url;
1✔
789
            _fullServletPath = servletPath;
1✔
790
            _mapping = mapping;
1✔
791
            _filtersPerName = filtersPerName;
1✔
792
            _filtersPerUrl = filtersPerUrl;
1✔
793
        }
1✔
794

795
        /**
796
         * get the Servlet
797
         *
798
         * @return the Servlet from the configuration
799
         *
800
         * @throws ServletException
801
         *             - e.g. if no configuration is available
802
         */
803
        @Override
804
        public Servlet getServlet() throws ServletException {
805
            if (getConfiguration() == null) {
1✔
806
                throw new HttpNotFoundException("No servlet mapping defined", _url);
1✔
807
            }
808

809
            try {
810
                return getConfiguration().getServlet();
1✔
811
            } catch (ClassNotFoundException e) {
×
812
                throw new HttpNotFoundException(_url, e);
×
NEW
813
            } catch (IllegalAccessException | InstantiationException | IllegalArgumentException
×
814
                    | InvocationTargetException | NoSuchMethodException | SecurityException e) {
UNCOV
815
                throw new HttpInternalErrorException(_url, e);
×
816
            }
817
        }
818

819
        /**
820
         * get the ServletPath the decoded ServletPath
821
         */
822
        @Override
823
        public String getServletPath() {
824
            return _mapping == null ? null : HttpUnitUtils.decode(_mapping.getServletPath(_fullServletPath));
1!
825
        }
826

827
        /**
828
         * get the Path Information
829
         *
830
         * @return the decode path
831
         */
832
        @Override
833
        public String getPathInfo() {
834
            return _mapping == null ? null : HttpUnitUtils.decode(_mapping.getPathInfo(_fullServletPath));
1!
835
        }
836

837
        @Override
838
        public FilterMetaData[] getFilters() {
839
            if (getConfiguration() == null) {
1✔
840
                return NO_FILTERS;
1✔
841
            }
842

843
            List<FilterMetaData> filters = new ArrayList<>();
1✔
844
            addFiltersForPath(filters, _fullServletPath);
1✔
845
            addFiltersForServletWithName(filters, getConfiguration().getServletName());
1✔
846

847
            return filters.toArray(new FilterMetaData[filters.size()]);
1✔
848
        }
849

850
        private void addFiltersForPath(List<FilterMetaData> filters, String fullServletPath) {
851
            FilterMetaData[] matches = _filtersPerUrl.getMatchingFilters(fullServletPath);
1✔
852
            Collections.addAll(filters, matches);
1✔
853
        }
1✔
854

855
        private void addFiltersForServletWithName(List<FilterMetaData> filters, String servletName) {
856
            if (servletName == null) {
1✔
857
                return;
1✔
858
            }
859
            List<FilterMetaData> matches = (List<FilterMetaData>) _filtersPerName.get(servletName);
1✔
860
            if (matches != null) {
1✔
861
                filters.addAll(matches);
1✔
862
            }
863
        }
1✔
864

865
        private ServletConfiguration getConfiguration() {
866
            return _mapping == null ? null : (ServletConfiguration) _mapping.getConfiguration();
1✔
867
        }
868
    }
869

870
    /**
871
     * mapping for WebResources
872
     */
873
    static class WebResourceMapping {
874

875
        private WebResourceConfiguration _configuration;
876

877
        WebResourceConfiguration getConfiguration() {
878
            return _configuration;
1✔
879
        }
880

881
        WebResourceMapping(WebResourceConfiguration configuration) {
1✔
882
            _configuration = configuration;
1✔
883
        }
1✔
884

885
        /**
886
         * Returns the portion of the request path which was actually used to select the servlet. This default
887
         * implementation returns the full specified path.
888
         *
889
         * @param requestPath
890
         *            the full path of the request, relative to the application root.
891
         */
892
        String getServletPath(String requestPath) {
893
            return requestPath;
1✔
894
        }
895

896
        /**
897
         * Returns the portion of the request path which was not used to select the servlet, and can be used as data by
898
         * the servlet. This default implementation returns null.
899
         *
900
         * @param requestPath
901
         *            the full path of the request, relative to the application root.
902
         */
903
        String getPathInfo(String requestPath) {
904
            return null;
1✔
905
        }
906

907
        public void destroyResource() {
908
            getConfiguration().destroyResource();
1✔
909
        }
1✔
910
    }
911

912
    static class PartialMatchWebResourceMapping extends WebResourceMapping {
913

914
        private String _prefix;
915

916
        public PartialMatchWebResourceMapping(WebResourceConfiguration configuration, String prefix) {
917
            super(configuration);
1✔
918
            if (!prefix.endsWith("/*")) {
1!
919
                throw new IllegalArgumentException(prefix + " does not end with '/*'");
×
920
            }
921
            _prefix = prefix.substring(0, prefix.length() - 2);
1✔
922
        }
1✔
923

924
        @Override
925
        String getServletPath(String requestPath) {
926
            return _prefix;
1✔
927
        }
928

929
        @Override
930
        String getPathInfo(String requestPath) {
931
            return requestPath.length() > _prefix.length() ? requestPath.substring(_prefix.length()) : null;
1✔
932
        }
933
    }
934

935
    /**
936
     * A utility class for mapping web resources to url patterns. This implements the matching algorithm documented in
937
     * section 10 of the JSDK-2.2 reference.
938
     */
939
    class WebResourceMap {
1✔
940

941
        private final Map _exactMatches = new HashMap();
1✔
942
        private final Map _extensions = new HashMap();
1✔
943
        private final Map _urlTree = new HashMap();
1✔
944
        private WebResourceMapping _defaultMapping;
945

946
        void put(String mapping, WebResourceConfiguration configuration) {
947
            if (mapping.equals("/")) {
1✔
948
                _defaultMapping = new WebResourceMapping(configuration);
1✔
949
            } else if (mapping.startsWith("*.")) {
1✔
950
                _extensions.put(mapping.substring(2), new WebResourceMapping(configuration));
1✔
951
            } else if (!mapping.startsWith("/") || !mapping.endsWith("/*")) {
1!
952
                _exactMatches.put(mapping, new WebResourceMapping(configuration));
1✔
953
            } else {
954
                ParsedPath path = new ParsedPath(mapping);
1✔
955
                Map context = _urlTree;
1✔
956
                while (path.hasNext()) {
1!
957
                    String part = path.next();
1✔
958
                    if (part.equals("*")) {
1✔
959
                        context.put("*", new PartialMatchWebResourceMapping(configuration, mapping));
1✔
960
                        return;
1✔
961
                    }
962
                    if (!context.containsKey(part)) {
1!
963
                        context.put(part, new HashMap());
1✔
964
                    }
965
                    context = (Map) context.get(part);
1✔
966
                }
1✔
967
            }
968
        }
1✔
969

970
        ServletMetaData get(URL url) {
971
            String file = url.getFile();
1✔
972
            if (!file.startsWith(_contextPath)) {
1✔
973
                throw new HttpNotFoundException("File path does not begin with '" + _contextPath + "'", url);
1✔
974
            }
975

976
            String servletPath = getServletPath(file.substring(_contextPath.length()));
1✔
977

978
            if (servletPath.endsWith("j_security_check")) {
1✔
979
                return new ServletRequestImpl(url, servletPath, SECURITY_CHECK_MAPPING, _filterMapping,
1✔
980
                        _filterUrlMapping);
1✔
981
            }
982
            return new ServletRequestImpl(url, servletPath, getMapping(servletPath), _filterMapping, _filterUrlMapping);
1✔
983
        }
984

985
        private String getServletPath(String urlFile) {
986
            if (urlFile.indexOf('?') < 0) {
1✔
987
                return urlFile;
1✔
988
            }
989
            return urlFile.substring(0, urlFile.indexOf('?'));
1✔
990
        }
991

992
        public void destroyWebResources() {
993
            if (_defaultMapping != null) {
1!
994
                _defaultMapping.destroyResource();
×
995
            }
996
            destroyWebResources(_exactMatches);
1✔
997
            destroyWebResources(_extensions);
1✔
998
            destroyWebResources(_urlTree);
1✔
999
        }
1✔
1000

1001
        private void destroyWebResources(Map map) {
1002
            for (Object o : map.values()) {
1✔
1003
                if (o instanceof WebResourceMapping) {
1!
1004
                    WebResourceMapping webResourceMapping = (WebResourceMapping) o;
1✔
1005
                    webResourceMapping.destroyResource();
1✔
1006
                } else {
1✔
1007
                    destroyWebResources((Map) o);
×
1008
                }
1009
            }
1✔
1010
        }
1✔
1011

1012
        void autoLoadServlets() {
1013
            ArrayList autoLoadable = new ArrayList();
1✔
1014
            if (_defaultMapping != null && _defaultMapping.getConfiguration().isLoadOnStartup()) {
1!
1015
                autoLoadable.add(_defaultMapping.getConfiguration());
×
1016
            }
1017
            collectAutoLoadableServlets(autoLoadable, _exactMatches);
1✔
1018
            collectAutoLoadableServlets(autoLoadable, _extensions);
1✔
1019
            collectAutoLoadableServlets(autoLoadable, _urlTree);
1✔
1020
            if (autoLoadable.isEmpty()) {
1✔
1021
                return;
1✔
1022
            }
1023

1024
            Collections.sort(autoLoadable, (o1, o2) -> {
1✔
1025
                ServletConfiguration sc1 = (ServletConfiguration) o1;
1✔
1026
                ServletConfiguration sc2 = (ServletConfiguration) o2;
1✔
1027
                return sc1.getLoadOrder() <= sc2.getLoadOrder() ? -1 : +1;
1✔
1028
            });
1029
            for (Iterator iterator = autoLoadable.iterator(); iterator.hasNext();) {
1✔
1030
                ServletConfiguration servletConfiguration = (ServletConfiguration) iterator.next();
1✔
1031
                try {
1032
                    servletConfiguration.getServlet();
1✔
1033
                } catch (Exception e) {
×
1034
                    HttpUnitUtils.handleException(e);
×
1035
                    throw new RuntimeException(
×
1036
                            "Unable to autoload servlet: " + servletConfiguration.getClassName() + ": " + e);
×
1037
                }
1✔
1038
            }
1✔
1039
        }
1✔
1040

1041
        private void collectAutoLoadableServlets(Collection collection, Map map) {
1042
            for (Object o : map.values()) {
1✔
1043
                if (o instanceof WebResourceMapping) {
1✔
1044
                    WebResourceMapping servletMapping = (WebResourceMapping) o;
1✔
1045
                    if (servletMapping.getConfiguration().isLoadOnStartup()) {
1✔
1046
                        collection.add(servletMapping.getConfiguration());
1✔
1047
                    }
1048
                } else {
1✔
1049
                    collectAutoLoadableServlets(collection, (Map) o);
1✔
1050
                }
1051
            }
1✔
1052
        }
1✔
1053

1054
        private WebResourceMapping getMapping(String url) {
1055
            if (_exactMatches.containsKey(url)) {
1✔
1056
                return (WebResourceMapping) _exactMatches.get(url);
1✔
1057
            }
1058

1059
            Map context = getContextForLongestPathPrefix(url);
1✔
1060
            if (context.containsKey("*")) {
1✔
1061
                return (WebResourceMapping) context.get("*");
1✔
1062
            }
1063

1064
            if (_extensions.containsKey(getExtension(url))) {
1✔
1065
                return (WebResourceMapping) _extensions.get(getExtension(url));
1✔
1066
            }
1067

1068
            if (_urlTree.containsKey("/")) {
1!
1069
                return (WebResourceMapping) _urlTree.get("/");
×
1070
            }
1071

1072
            if (_defaultMapping != null) {
1✔
1073
                return _defaultMapping;
1✔
1074
            }
1075

1076
            final String prefix = "/servlet/";
1✔
1077
            if (!url.startsWith(prefix)) {
1✔
1078
                return null;
1✔
1079
            }
1080

1081
            String className = url.substring(prefix.length());
1✔
1082
            try {
1083
                Class.forName(className);
1✔
1084
                return new WebResourceMapping(new ServletConfiguration(className));
1✔
1085
            } catch (ClassNotFoundException e) {
×
1086
                return null;
×
1087
            }
1088
        }
1089

1090
        private Map getContextForLongestPathPrefix(String url) {
1091
            Map context = _urlTree;
1✔
1092

1093
            ParsedPath path = new ParsedPath(url);
1✔
1094
            while (path.hasNext()) {
1✔
1095
                String part = path.next();
1✔
1096
                if (!context.containsKey(part)) {
1✔
1097
                    break;
1✔
1098
                }
1099
                context = (Map) context.get(part);
1✔
1100
            }
1✔
1101
            return context;
1✔
1102
        }
1103

1104
        private String getExtension(String url) {
1105
            int index = url.lastIndexOf('.');
1✔
1106
            if (index == -1 || index >= url.length() - 1) {
1!
1107
                return "";
1✔
1108
            }
1109
            return url.substring(index + 1);
1✔
1110
        }
1111

1112
    }
1113

1114
    /**
1115
     * return the given ServletConfiguration for the given servlet name
1116
     *
1117
     * @param servletName
1118
     *
1119
     * @return the corresponding ServletConfiguration
1120
     */
1121
    public ServletConfiguration getServletByName(String servletName) {
1122
        return (ServletConfiguration) _servlets.get(servletName);
1✔
1123
    }
1124

1125
}
1126

1127
/**
1128
 * A utility class for parsing URLs into paths
1129
 *
1130
 * @author <a href="balld@webslingerZ.com">Donald Ball</a>
1131
 */
1132
class ParsedPath {
1133

1134
    private final String path;
1135
    private int position = 0;
1✔
1136
    static final char seperator_char = '/';
1137

1138
    /**
1139
     * Creates a new parsed path for the given path value
1140
     *
1141
     * @param path
1142
     *            the path
1143
     */
1144
    ParsedPath(String path) {
1✔
1145
        if (path.charAt(0) != seperator_char) {
1!
1146
            throw new IllegalArgumentException("Illegal path '" + path + "', does not begin with " + seperator_char);
×
1147
        }
1148
        this.path = path;
1✔
1149
    }
1✔
1150

1151
    /**
1152
     * Returns true if there are more parts left, otherwise false
1153
     */
1154
    public final boolean hasNext() {
1155
        return position < path.length();
1✔
1156
    }
1157

1158
    /**
1159
     * Returns the next part in the path
1160
     */
1161
    public final String next() {
1162
        int offset = position + 1;
1✔
1163
        while (offset < path.length() && path.charAt(offset) != seperator_char) {
1✔
1164
            offset++;
1✔
1165
        }
1166
        String result = path.substring(position + 1, offset);
1✔
1167
        position = offset;
1✔
1168
        return result;
1✔
1169
    }
1170

1171
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc