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

hazendaz / httpunit / 636

05 Dec 2025 03:27AM UTC coverage: 80.509%. Remained the same
636

push

github

hazendaz
Cleanup more old since tags

you guessed it, at this point going to jautodoc the rest so the warnings on builds go away ;)

3213 of 4105 branches covered (78.27%)

Branch coverage included in aggregate %.

8249 of 10132 relevant lines covered (81.42%)

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-2025 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 jakarta.servlet.*;
27
import jakarta.servlet.http.*;
28

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

45
import org.w3c.dom.Document;
46
import org.w3c.dom.Element;
47
import org.w3c.dom.NodeList;
48
import org.xml.sax.SAXException;
49

50
/**
51
 * This class represents the information recorded about a single web application. It is usually extracted from web.xml.
52
 **/
53
class WebApplication implements SessionListenerDispatcher {
54

55
    private static final SecurityConstraint NULL_SECURITY_CONSTRAINT = new NullSecurityConstraint();
1✔
56

57
    private final ServletConfiguration SECURITY_CHECK_CONFIGURATION = new ServletConfiguration(
1✔
58
            SecurityCheckServlet.class.getName());
1✔
59

60
    private final WebResourceMapping SECURITY_CHECK_MAPPING = new WebResourceMapping(SECURITY_CHECK_CONFIGURATION);
1✔
61

62
    /** A mapping of resource names to servlet configurations. **/
63
    private WebResourceMap _servletMapping = new WebResourceMap();
1✔
64

65
    /** A mapping of filter names to FilterConfigurations */
66
    private Hashtable _filters = new Hashtable<>();
1✔
67

68
    /** A mapping of servlet names to ServletConfigurations */
69
    private Hashtable _servlets = new Hashtable<>();
1✔
70

71
    /** A mapping of resource names to filter configurations. **/
72
    private FilterUrlMap _filterUrlMapping = new FilterUrlMap();
1✔
73

74
    /** A mapping of servlet names to filter configurations. **/
75
    private Hashtable _filterMapping = new Hashtable<>();
1✔
76

77
    private List<SecurityConstraint> _securityConstraints = new ArrayList<>();
1✔
78

79
    private List<ServletContextListener> _contextListeners = new ArrayList<>();
1✔
80

81
    private List<ServletContextAttributeListener> _contextAttributeListeners = new ArrayList<>();
1✔
82

83
    private List<HttpSessionListener> _sessionListeners = new ArrayList<>();
1✔
84

85
    private List<HttpSessionAttributeListener> _sessionAttributeListeners = new ArrayList<>();
1✔
86

87
    private boolean _useBasicAuthentication;
88

89
    private boolean _useFormAuthentication;
90

91
    private String _authenticationRealm = "";
1✔
92

93
    private URL _loginURL;
94

95
    private URL _errorURL;
96

97
    private Hashtable _contextParameters = new Hashtable<>();
1✔
98

99
    private File _contextDir = null;
1✔
100

101
    private String _contextPath = null;
1✔
102

103
    private ServletUnitServletContext _servletContext;
104

105
    private String _displayName;
106

107
    /**
108
     * Constructs a default application spec with no information.
109
     */
110
    WebApplication() {
1✔
111
        _contextPath = "";
1✔
112
    }
1✔
113

114
    /**
115
     * Constructs an application spec from an XML document.
116
     */
117
    WebApplication(Document document) throws MalformedURLException, SAXException {
118
        this(document, null, "");
1✔
119
    }
1✔
120

121
    /**
122
     * Constructs an application spec from an XML document.
123
     */
124
    WebApplication(Document document, String contextPath) throws MalformedURLException, SAXException {
125
        this(document, null, contextPath);
1✔
126
    }
1✔
127

128
    /**
129
     * Constructs an application spec from an XML document.
130
     */
131
    WebApplication(Document document, File file, String contextPath) throws MalformedURLException, SAXException {
1✔
132
        if (contextPath != null && !contextPath.isEmpty() && !contextPath.startsWith("/")) {
1!
133
            throw new IllegalArgumentException("Context path " + contextPath + " must start with '/'");
×
134
        }
135
        _contextDir = file;
1✔
136
        _contextPath = contextPath == null ? "" : contextPath;
1✔
137
        NodeList nl = document.getElementsByTagName("display-name");
1✔
138
        if (nl.getLength() > 0) {
1✔
139
            _displayName = XMLUtils.getTextValue(nl.item(0)).trim();
1✔
140
        }
141

142
        registerServlets(document);
1✔
143
        registerFilters(document);
1✔
144
        extractSecurityConstraints(document);
1✔
145
        extractContextParameters(document);
1✔
146
        extractLoginConfiguration(document);
1✔
147
        extractListeners(document);
1✔
148
        notifyContextInitialized();
1✔
149
        _servletMapping.autoLoadServlets();
1✔
150
    }
1✔
151

152
    private void extractListeners(Document document) throws SAXException {
153
        NodeList nl = document.getElementsByTagName("listener");
1✔
154
        for (int i = 0; i < nl.getLength(); i++) {
1✔
155
            String listenerName = XMLUtils.getChildNodeValue((Element) nl.item(i), "listener-class").trim();
1✔
156
            try {
157
                Object listener = Class.forName(listenerName).getDeclaredConstructor().newInstance();
1✔
158

159
                if (listener instanceof ServletContextListener) {
1✔
160
                    _contextListeners.add((ServletContextListener) listener);
1✔
161
                }
162
                if (listener instanceof ServletContextAttributeListener) {
1✔
163
                    _contextAttributeListeners.add((ServletContextAttributeListener) listener);
1✔
164
                }
165
                if (listener instanceof HttpSessionListener) {
1✔
166
                    _sessionListeners.add((HttpSessionListener) listener);
1✔
167
                }
168
                if (listener instanceof HttpSessionAttributeListener) {
1✔
169
                    _sessionAttributeListeners.add((HttpSessionAttributeListener) listener);
1✔
170
                }
171
            } catch (Throwable e) {
×
172
                throw new RuntimeException("Unable to load context listener " + listenerName + ": " + e.toString());
×
173
            }
1✔
174
        }
175
    }
1✔
176

177
    private void notifyContextInitialized() {
178
        ServletContextEvent event = new ServletContextEvent(getServletContext());
1✔
179

180
        for (Iterator<ServletContextListener> i = _contextListeners.iterator(); i.hasNext();) {
1✔
181
            ServletContextListener listener = i.next();
1✔
182
            listener.contextInitialized(event);
1✔
183
        }
1✔
184
    }
1✔
185

186
    void shutDown() {
187
        destroyServlets();
1✔
188
        notifyContextDestroyed();
1✔
189
    }
1✔
190

191
    private void notifyContextDestroyed() {
192
        ServletContextEvent event = new ServletContextEvent(getServletContext());
1✔
193

194
        for (ListIterator<ServletContextListener> i = _contextListeners.listIterator(_contextListeners.size()); i
1✔
195
                .hasPrevious();) {
1✔
196
            ServletContextListener listener = i.previous();
1✔
197
            listener.contextDestroyed(event);
1✔
198
        }
1✔
199
    }
1✔
200

201
    void sendAttributeAdded(String name, Object value) {
202
        ServletContextAttributeEvent event = new ServletContextAttributeEvent(getServletContext(), name, value);
1✔
203

204
        for (Iterator<ServletContextAttributeListener> i = _contextAttributeListeners.iterator(); i.hasNext();) {
1✔
205
            ServletContextAttributeListener listener = i.next();
1✔
206
            listener.attributeAdded(event);
1✔
207
        }
1✔
208
    }
1✔
209

210
    void sendAttributeReplaced(String name, Object value) {
211
        ServletContextAttributeEvent event = new ServletContextAttributeEvent(getServletContext(), name, value);
1✔
212

213
        for (Iterator<ServletContextAttributeListener> i = _contextAttributeListeners.iterator(); i.hasNext();) {
1✔
214
            ServletContextAttributeListener listener = i.next();
1✔
215
            listener.attributeReplaced(event);
1✔
216
        }
1✔
217
    }
1✔
218

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

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

228
    private void extractSecurityConstraints(Document document) throws SAXException {
229
        NodeList nl = document.getElementsByTagName("security-constraint");
1✔
230
        for (int i = 0; i < nl.getLength(); i++) {
1✔
231
            _securityConstraints.add(new SecurityConstraintImpl((Element) nl.item(i)));
1✔
232
        }
233
    }
1✔
234

235
    String getContextPath() {
236
        return _contextPath;
1✔
237
    }
238

239
    ServletContext getServletContext() {
240
        if (_servletContext == null) {
1✔
241
            _servletContext = new ServletUnitServletContext(this);
1✔
242
        }
243
        return _servletContext;
1✔
244
    }
245

246
    /**
247
     * Registers a servlet class to be run.
248
     **/
249
    void registerServlet(String resourceName, String servletClassName, Hashtable initParams) {
250
        registerServlet(resourceName, new ServletConfiguration(servletClassName, initParams));
1✔
251
    }
1✔
252

253
    /**
254
     * Registers a servlet to be run.
255
     **/
256
    void registerServlet(String resourceName, ServletConfiguration servletConfiguration) {
257
        // FIXME - shouldn't everything start with one or the other?
258
        if (!resourceName.startsWith("/") && !resourceName.startsWith("*")) {
1✔
259
            resourceName = "/" + resourceName;
1✔
260
        }
261
        _servletMapping.put(resourceName, servletConfiguration);
1✔
262
    }
1✔
263

264
    /**
265
     * Calls the destroy method for every active servlet.
266
     */
267
    void destroyServlets() {
268
        _servletMapping.destroyWebResources();
1✔
269
    }
1✔
270

271
    ServletMetaData getServletRequest(URL url) {
272
        return _servletMapping.get(url);
1✔
273
    }
274

275
    /**
276
     * Returns true if this application uses Basic Authentication.
277
     */
278
    boolean usesBasicAuthentication() {
279
        return _useBasicAuthentication;
1✔
280
    }
281

282
    /**
283
     * Returns true if this application uses form-based authentication.
284
     */
285
    boolean usesFormAuthentication() {
286
        return _useFormAuthentication;
1✔
287
    }
288

289
    String getAuthenticationRealm() {
290
        return _authenticationRealm;
1✔
291
    }
292

293
    URL getLoginURL() {
294
        return _loginURL;
1✔
295
    }
296

297
    URL getErrorURL() {
298
        return _errorURL;
1✔
299
    }
300

301
    /**
302
     * Returns true if the specified path may only be accesses by an authorized user.
303
     *
304
     * @param url
305
     *            the application-relative path of the URL
306
     */
307
    boolean requiresAuthorization(URL url) {
308
        String result;
309
        String file = url.getFile();
1✔
310
        if (_contextPath.equals("")) {
1✔
311
            result = file;
1✔
312
        } else if (file.startsWith(_contextPath)) {
1!
313
            result = file.substring(_contextPath.length());
1✔
314
        } else {
315
            result = null;
×
316
        }
317
        return getControllingConstraint(result) != NULL_SECURITY_CONSTRAINT;
1✔
318
    }
319

320
    /**
321
     * Returns an array containing the roles permitted to access the specified URL.
322
     */
323
    String[] getPermittedRoles(URL url) {
324
        String result;
325
        String file = url.getFile();
1✔
326
        if (_contextPath.equals("")) {
1✔
327
            result = file;
1✔
328
        } else if (file.startsWith(_contextPath)) {
1!
329
            result = file.substring(_contextPath.length());
1✔
330
        } else {
331
            result = null;
×
332
        }
333
        return getControllingConstraint(result).getPermittedRoles();
1✔
334
    }
335

336
    private SecurityConstraint getControllingConstraint(String urlPath) {
337
        for (SecurityConstraint sc : _securityConstraints) {
1✔
338
            if (sc.controlsPath(urlPath)) {
1✔
339
                return sc;
1✔
340
            }
341
        }
1✔
342
        return NULL_SECURITY_CONSTRAINT;
1✔
343
    }
344

345
    File getResourceFile(String path) {
346
        String relativePath = path.startsWith("/") ? path.substring(1) : path;
1✔
347
        if (_contextDir == null) {
1✔
348
            return new File(relativePath);
1✔
349
        }
350
        return new File(_contextDir, relativePath);
1✔
351
    }
352

353
    Hashtable getContextParameters() {
354
        return _contextParameters;
1✔
355
    }
356

357
    // ---------------------------------------- SessionListenerDispatcher methods
358
    // -------------------------------------------
359

360
    @Override
361
    public void sendSessionCreated(HttpSession session) {
362
        HttpSessionEvent event = new HttpSessionEvent(session);
1✔
363

364
        for (HttpSessionListener listener : _sessionListeners) {
1✔
365
            listener.sessionCreated(event);
1✔
366
        }
1✔
367
    }
1✔
368

369
    @Override
370
    public void sendSessionDestroyed(HttpSession session) {
371
        HttpSessionEvent event = new HttpSessionEvent(session);
1✔
372

373
        for (HttpSessionListener listener : _sessionListeners) {
1✔
374
            listener.sessionDestroyed(event);
1✔
375
        }
1✔
376
    }
1✔
377

378
    @Override
379
    public void sendAttributeAdded(HttpSession session, String name, Object value) {
380
        HttpSessionBindingEvent event = new HttpSessionBindingEvent(session, name, value);
1✔
381

382
        for (HttpSessionAttributeListener listener : _sessionAttributeListeners) {
1✔
383
            listener.attributeAdded(event);
1✔
384
        }
1✔
385
    }
1✔
386

387
    @Override
388
    public void sendAttributeReplaced(HttpSession session, String name, Object oldValue) {
389
        HttpSessionBindingEvent event = new HttpSessionBindingEvent(session, name, oldValue);
1✔
390

391
        for (HttpSessionAttributeListener listener : _sessionAttributeListeners) {
1✔
392
            listener.attributeReplaced(event);
1✔
393
        }
1✔
394
    }
1✔
395

396
    @Override
397
    public void sendAttributeRemoved(HttpSession session, String name, Object oldValue) {
398
        HttpSessionBindingEvent event = new HttpSessionBindingEvent(session, name, oldValue);
1✔
399

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

405
    // --------------------------------------------------- private members
406
    // --------------------------------------------------
407

408
    private void registerFilters(Document document) throws SAXException {
409
        Hashtable nameToClass = new Hashtable<>();
1✔
410
        NodeList nl = document.getElementsByTagName("filter");
1✔
411
        for (int i = 0; i < nl.getLength(); i++) {
1✔
412
            registerFilterClass(nameToClass, (Element) nl.item(i));
1✔
413
        }
414
        nl = document.getElementsByTagName("filter-mapping");
1✔
415
        for (int i = 0; i < nl.getLength(); i++) {
1✔
416
            registerFilter(nameToClass, (Element) nl.item(i));
1✔
417
        }
418
        this._filters = nameToClass;
1✔
419
    }
1✔
420

421
    private void registerFilterClass(Dictionary mapping, Element filterElement) throws SAXException {
422
        String filterName = XMLUtils.getChildNodeValue(filterElement, "filter-name");
1✔
423
        mapping.put(filterName, new FilterConfiguration(filterName, filterElement));
1✔
424
    }
1✔
425

426
    private void registerFilter(Dictionary mapping, Element filterElement) throws SAXException {
427
        if (XMLUtils.hasChildNode(filterElement, "servlet-name")) {
1✔
428
            registerFilterForServlet(XMLUtils.getChildNodeValue(filterElement, "servlet-name"),
1✔
429
                    (FilterConfiguration) mapping.get(XMLUtils.getChildNodeValue(filterElement, "filter-name")));
1✔
430
        }
431
        if (XMLUtils.hasChildNode(filterElement, "url-pattern")) {
1✔
432
            registerFilterForUrl(XMLUtils.getChildNodeValue(filterElement, "url-pattern"),
1✔
433
                    (FilterConfiguration) mapping.get(XMLUtils.getChildNodeValue(filterElement, "filter-name")));
1✔
434
        }
435
    }
1✔
436

437
    private void registerFilterForUrl(String resourceName, FilterConfiguration filterConfiguration) {
438
        _filterUrlMapping.put(resourceName, filterConfiguration);
1✔
439
    }
1✔
440

441
    private void registerFilterForServlet(String servletName, FilterConfiguration filterConfiguration) {
442
        List list = (List) _filterMapping.get(servletName);
1✔
443
        if (list == null) {
1✔
444
            list = new ArrayList<>();
1✔
445
            _filterMapping.put(servletName, list);
1✔
446
        }
447
        list.add(filterConfiguration);
1✔
448
    }
1✔
449

450
    private void extractLoginConfiguration(Document document) throws MalformedURLException, SAXException {
451
        NodeList nl = document.getElementsByTagName("login-config");
1✔
452
        if (nl.getLength() == 1) {
1✔
453
            final Element loginConfigElement = (Element) nl.item(0);
1✔
454
            String authenticationMethod = XMLUtils.getChildNodeValue(loginConfigElement, "auth-method", "BASIC");
1✔
455
            _authenticationRealm = XMLUtils.getChildNodeValue(loginConfigElement, "realm-name", "");
1✔
456
            if (authenticationMethod.equalsIgnoreCase("BASIC")) {
1✔
457
                _useBasicAuthentication = true;
1✔
458
                if (_authenticationRealm.isEmpty()) {
1!
459
                    throw new SAXException("No realm specified for BASIC Authorization");
×
460
                }
461
            } else if (authenticationMethod.equalsIgnoreCase("FORM")) {
1!
462
                _useFormAuthentication = true;
1✔
463
                if (_authenticationRealm.isEmpty()) {
1!
464
                    throw new SAXException("No realm specified for FORM Authorization");
×
465
                }
466
                _loginURL = new URL("http", "localhost",
1✔
467
                        _contextPath + XMLUtils.getChildNodeValue(loginConfigElement, "form-login-page"));
1✔
468
                _errorURL = new URL("http", "localhost",
1✔
469
                        _contextPath + XMLUtils.getChildNodeValue(loginConfigElement, "form-error-page"));
1✔
470
            }
471
        }
472
    }
1✔
473

474
    private void registerServlets(Document document) throws SAXException {
475
        Hashtable nameToClass = new Hashtable<>();
1✔
476
        NodeList nl = document.getElementsByTagName("servlet");
1✔
477
        for (int i = 0; i < nl.getLength(); i++) {
1✔
478
            registerServletClass(nameToClass, (Element) nl.item(i));
1✔
479
        }
480
        nl = document.getElementsByTagName("servlet-mapping");
1✔
481
        for (int i = 0; i < nl.getLength(); i++) {
1✔
482
            registerServlet(nameToClass, (Element) nl.item(i));
1✔
483
        }
484
        this._servlets = nameToClass;
1✔
485
    }
1✔
486

487
    private void registerServletClass(Dictionary mapping, Element servletElement) throws SAXException {
488
        mapping.put(XMLUtils.getChildNodeValue(servletElement, "servlet-name"),
1✔
489
                new ServletConfiguration(servletElement));
490
    }
1✔
491

492
    private void registerServlet(Dictionary mapping, Element servletElement) throws SAXException {
493
        registerServlet(XMLUtils.getChildNodeValue(servletElement, "url-pattern"),
1✔
494
                (ServletConfiguration) mapping.get(XMLUtils.getChildNodeValue(servletElement, "servlet-name")));
1✔
495
    }
1✔
496

497
    private void extractContextParameters(Document document) throws SAXException {
498
        NodeList nl = document.getElementsByTagName("context-param");
1✔
499
        for (int i = 0; i < nl.getLength(); i++) {
1✔
500
            Element param = (Element) nl.item(i);
1✔
501
            String name = XMLUtils.getChildNodeValue(param, "param-name");
1✔
502
            String value = XMLUtils.getChildNodeValue(param, "param-value");
1✔
503
            _contextParameters.put(name, value);
1✔
504
        }
505
    }
1✔
506

507
    private static boolean patternMatches(String urlPattern, String urlPath) {
508
        return urlPattern.equals(urlPath);
1✔
509
    }
510

511
    String getDisplayName() {
512
        return _displayName;
1✔
513
    }
514

515
    // ============================================= SecurityCheckServlet class
516
    // =============================================
517

518
    static class SecurityCheckServlet extends HttpServlet {
1✔
519

520
        private static final long serialVersionUID = 1L;
521

522
        @Override
523
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
524
            handleLogin(req, resp);
×
525
        }
×
526

527
        @Override
528
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
529
            handleLogin(req, resp);
1✔
530
        }
1✔
531

532
        private void handleLogin(HttpServletRequest req, HttpServletResponse resp) throws IOException {
533
            final String username = req.getParameter("j_username");
1✔
534
            final String roleList = req.getParameter("j_password");
1✔
535
            getServletSession(req).setUserInformation(username, ServletUnitHttpRequest.toArray(roleList));
1✔
536
            resp.sendRedirect(getServletSession(req).getOriginalURL().toExternalForm());
1✔
537
        }
1✔
538

539
        private ServletUnitHttpSession getServletSession(HttpServletRequest req) {
540
            return (ServletUnitHttpSession) req.getSession();
1✔
541
        }
542

543
    }
544

545
    // ============================================= ServletConfiguration class
546
    // =============================================
547

548
    static final int DONT_AUTOLOAD = Integer.MIN_VALUE;
549
    static final int ANY_LOAD_ORDER = Integer.MAX_VALUE;
550

551
    class ServletConfiguration extends WebResourceConfiguration {
552

553
        private Servlet _servlet;
554
        private String _servletName;
555
        private String _jspFile;
556
        private int _loadOrder = DONT_AUTOLOAD;
1✔
557

558
        ServletConfiguration(String className) {
1✔
559
            super(className);
1✔
560
        }
1✔
561

562
        ServletConfiguration(String className, Hashtable initParams) {
1✔
563
            super(className, initParams);
1✔
564
        }
1✔
565

566
        ServletConfiguration(Element servletElement) throws SAXException {
1✔
567
            super(servletElement, "servlet-class", XMLUtils.getChildNodeValue(servletElement, "servlet-class",
1✔
568
                    "org.apache.jasper.servlet.JspServlet"));
569
            _servletName = XMLUtils.getChildNodeValue(servletElement, "servlet-name");
1✔
570
            _jspFile = XMLUtils.getChildNodeValue(servletElement, "jsp-file", "");
1✔
571
            if ("".equals(_jspFile)) {
1!
572
                _jspFile = null;
1✔
573
            }
574
            final NodeList loadOrder = servletElement.getElementsByTagName("load-on-startup");
1✔
575
            for (int i = 0; i < loadOrder.getLength(); i++) {
1✔
576
                String order = XMLUtils.getTextValue(loadOrder.item(i));
1✔
577
                try {
578
                    _loadOrder = Integer.parseInt(order);
1✔
579
                } catch (NumberFormatException e) {
1✔
580
                    _loadOrder = ANY_LOAD_ORDER;
1✔
581
                }
1✔
582
            }
583
        }
1✔
584

585
        synchronized Servlet getServlet()
586
                throws ClassNotFoundException, InstantiationException, IllegalAccessException, ServletException,
587
                IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
588
            if (_servlet == null) {
1✔
589
                Class servletClass = Class.forName(getClassName());
1✔
590
                _servlet = (Servlet) servletClass.getDeclaredConstructor().newInstance();
1✔
591
                String servletName = _servletName != null ? _servletName : _servlet.getClass().getName();
1✔
592
                _servlet.init(new ServletUnitServletConfig(servletName, WebApplication.this, getInitParams()));
1✔
593
            }
594

595
            return _servlet;
1✔
596
        }
597

598
        @Override
599
        synchronized void destroyResource() {
600
            if (_servlet != null) {
1✔
601
                _servlet.destroy();
1✔
602
            }
603
        }
1✔
604

605
        String getServletName() {
606
            return _servletName;
1✔
607
        }
608

609
        @Override
610
        boolean isLoadOnStartup() {
611
            return _loadOrder != DONT_AUTOLOAD;
1✔
612
        }
613

614
        public int getLoadOrder() {
615
            return _loadOrder;
1✔
616
        }
617

618
        public Object getJspFile() {
619
            return this._jspFile;
×
620
        }
621
    }
622

623
    // ============================================= FilterConfiguration class
624
    // =============================================
625

626
    class FilterConfiguration extends WebResourceConfiguration implements FilterMetaData {
627

628
        private Filter _filter;
629
        private String _name;
630

631
        FilterConfiguration(String name, Element filterElement) throws SAXException {
1✔
632
            super(filterElement, "filter-class");
1✔
633
            _name = name;
1✔
634
        }
1✔
635

636
        @Override
637
        public synchronized Filter getFilter() throws ServletException {
638
            try {
639
                if (_filter == null) {
1✔
640
                    Class filterClass = Class.forName(getClassName());
1✔
641
                    _filter = (Filter) filterClass.getDeclaredConstructor().newInstance();
1✔
642
                    _filter.init(new FilterConfigImpl(_name, getServletContext(), getInitParams()));
1✔
643
                }
644

645
                return _filter;
1✔
646
            } catch (ClassNotFoundException e) {
×
647
                throw new ServletException("Did not find filter class: " + getClassName());
×
648
            } catch (IllegalAccessException e) {
×
649
                throw new ServletException("Filter class " + getClassName() + " lacks a public no-arg constructor");
×
650
            } catch (InstantiationException | IllegalArgumentException | InvocationTargetException
×
651
                    | NoSuchMethodException | SecurityException e) {
652
                throw new ServletException("Filter class " + getClassName() + " could not be instantiated.");
×
653
            } catch (ClassCastException e) {
×
654
                throw new ServletException(
×
655
                        "Filter class " + getClassName() + " does not implement" + Filter.class.getName());
×
656
            }
657
        }
658

659
        @Override
660
        boolean isLoadOnStartup() {
661
            return false;
×
662
        }
663

664
        @Override
665
        synchronized void destroyResource() {
666
            if (_filter != null) {
×
667
                _filter.destroy();
×
668
            }
669
        }
×
670
    }
671

672
    // =================================== SecurityConstract interface and implementations
673
    // ==================================
674

675
    interface SecurityConstraint {
676

677
        boolean controlsPath(String urlPath);
678

679
        String[] getPermittedRoles();
680
    }
681

682
    static class NullSecurityConstraint implements SecurityConstraint {
1✔
683

684
        private static final String[] NO_ROLES = {};
1✔
685

686
        @Override
687
        public boolean controlsPath(String urlPath) {
688
            return false;
×
689
        }
690

691
        @Override
692
        public String[] getPermittedRoles() {
693
            return NO_ROLES;
×
694
        }
695
    }
696

697
    static class SecurityConstraintImpl implements SecurityConstraint {
698

699
        SecurityConstraintImpl(Element root) throws SAXException {
1✔
700
            final NodeList roleNames = root.getElementsByTagName("role-name");
1✔
701
            for (int i = 0; i < roleNames.getLength(); i++) {
1✔
702
                _roleList.add(XMLUtils.getTextValue(roleNames.item(i)));
1✔
703
            }
704

705
            final NodeList resources = root.getElementsByTagName("web-resource-collection");
1✔
706
            for (int i = 0; i < resources.getLength(); i++) {
1✔
707
                _resources.add(new WebResourceCollection((Element) resources.item(i)));
1✔
708
            }
709
        }
1✔
710

711
        @Override
712
        public boolean controlsPath(String urlPath) {
713
            return getMatchingCollection(urlPath) != null;
1✔
714
        }
715

716
        @Override
717
        public String[] getPermittedRoles() {
718
            if (_roles == null) {
1✔
719
                _roles = _roleList.toArray(new String[_roleList.size()]);
1✔
720
            }
721
            return _roles;
1✔
722
        }
723

724
        private String[] _roles;
725
        private List<String> _roleList = new ArrayList<>();
1✔
726
        private List<WebResourceCollection> _resources = new ArrayList<>();
1✔
727

728
        public WebResourceCollection getMatchingCollection(String urlPath) {
729
            for (WebResourceCollection wrc : _resources) {
1✔
730
                if (wrc.controlsPath(urlPath)) {
1✔
731
                    return wrc;
1✔
732
                }
733
            }
1✔
734
            return null;
1✔
735
        }
736

737
        class WebResourceCollection {
738

739
            WebResourceCollection(Element root) throws SAXException {
1✔
740
                final NodeList urlPatterns = root.getElementsByTagName("url-pattern");
1✔
741
                for (int i = 0; i < urlPatterns.getLength(); i++) {
1✔
742
                    _urlPatterns.add(XMLUtils.getTextValue(urlPatterns.item(i)));
1✔
743
                }
744
            }
1✔
745

746
            boolean controlsPath(String urlPath) {
747
                for (String pattern : _urlPatterns) {
1✔
748
                    if (patternMatches(pattern, urlPath)) {
1✔
749
                        return true;
1✔
750
                    }
751
                }
1✔
752
                return false;
1✔
753
            }
754

755
            private List<String> _urlPatterns = new ArrayList<>();
1✔
756
        }
757
    }
758

759
    static final FilterMetaData[] NO_FILTERS = {};
1✔
760

761
    static class ServletRequestImpl implements ServletMetaData {
762

763
        private URL _url;
764
        private String _fullServletPath;
765
        private WebResourceMapping _mapping;
766
        private Hashtable _filtersPerName;
767
        private FilterUrlMap _filtersPerUrl;
768

769
        ServletRequestImpl(URL url, String servletPath, WebResourceMapping mapping, Hashtable filtersPerName,
770
                FilterUrlMap filtersPerUrl) {
1✔
771
            _url = url;
1✔
772
            _fullServletPath = servletPath;
1✔
773
            _mapping = mapping;
1✔
774
            _filtersPerName = filtersPerName;
1✔
775
            _filtersPerUrl = filtersPerUrl;
1✔
776
        }
1✔
777

778
        /**
779
         * get the Servlet
780
         *
781
         * @return the Servlet from the configuration
782
         *
783
         * @throws ServletException
784
         *             - e.g. if no configuration is available
785
         */
786
        @Override
787
        public Servlet getServlet() throws ServletException {
788
            if (getConfiguration() == null) {
1✔
789
                throw new HttpNotFoundException("No servlet mapping defined", _url);
1✔
790
            }
791

792
            try {
793
                return getConfiguration().getServlet();
1✔
794
            } catch (ClassNotFoundException e) {
×
795
                throw new HttpNotFoundException(_url, e);
×
796
            } catch (IllegalAccessException | InstantiationException | IllegalArgumentException
×
797
                    | InvocationTargetException | NoSuchMethodException | SecurityException e) {
798
                throw new HttpInternalErrorException(_url, e);
×
799
            }
800
        }
801

802
        /**
803
         * get the ServletPath the decoded ServletPath
804
         */
805
        @Override
806
        public String getServletPath() {
807
            return _mapping == null ? null : HttpUnitUtils.decode(_mapping.getServletPath(_fullServletPath));
1!
808
        }
809

810
        /**
811
         * get the Path Information
812
         *
813
         * @return the decode path
814
         */
815
        @Override
816
        public String getPathInfo() {
817
            return _mapping == null ? null : HttpUnitUtils.decode(_mapping.getPathInfo(_fullServletPath));
1!
818
        }
819

820
        @Override
821
        public FilterMetaData[] getFilters() {
822
            if (getConfiguration() == null) {
1✔
823
                return NO_FILTERS;
1✔
824
            }
825

826
            List<FilterMetaData> filters = new ArrayList<>();
1✔
827
            addFiltersForPath(filters, _fullServletPath);
1✔
828
            addFiltersForServletWithName(filters, getConfiguration().getServletName());
1✔
829

830
            return filters.toArray(new FilterMetaData[filters.size()]);
1✔
831
        }
832

833
        private void addFiltersForPath(List<FilterMetaData> filters, String fullServletPath) {
834
            FilterMetaData[] matches = _filtersPerUrl.getMatchingFilters(fullServletPath);
1✔
835
            Collections.addAll(filters, matches);
1✔
836
        }
1✔
837

838
        private void addFiltersForServletWithName(List<FilterMetaData> filters, String servletName) {
839
            if (servletName == null) {
1✔
840
                return;
1✔
841
            }
842
            List<FilterMetaData> matches = (List<FilterMetaData>) _filtersPerName.get(servletName);
1✔
843
            if (matches != null) {
1✔
844
                filters.addAll(matches);
1✔
845
            }
846
        }
1✔
847

848
        private ServletConfiguration getConfiguration() {
849
            return _mapping == null ? null : (ServletConfiguration) _mapping.getConfiguration();
1✔
850
        }
851
    }
852

853
    /**
854
     * mapping for WebResources
855
     */
856
    static class WebResourceMapping {
857

858
        private WebResourceConfiguration _configuration;
859

860
        WebResourceConfiguration getConfiguration() {
861
            return _configuration;
1✔
862
        }
863

864
        WebResourceMapping(WebResourceConfiguration configuration) {
1✔
865
            _configuration = configuration;
1✔
866
        }
1✔
867

868
        /**
869
         * Returns the portion of the request path which was actually used to select the servlet. This default
870
         * implementation returns the full specified path.
871
         *
872
         * @param requestPath
873
         *            the full path of the request, relative to the application root.
874
         */
875
        String getServletPath(String requestPath) {
876
            return requestPath;
1✔
877
        }
878

879
        /**
880
         * Returns the portion of the request path which was not used to select the servlet, and can be used as data by
881
         * the servlet. This default implementation returns null.
882
         *
883
         * @param requestPath
884
         *            the full path of the request, relative to the application root.
885
         */
886
        String getPathInfo(String requestPath) {
887
            return null;
1✔
888
        }
889

890
        public void destroyResource() {
891
            getConfiguration().destroyResource();
1✔
892
        }
1✔
893
    }
894

895
    static class PartialMatchWebResourceMapping extends WebResourceMapping {
896

897
        private String _prefix;
898

899
        public PartialMatchWebResourceMapping(WebResourceConfiguration configuration, String prefix) {
900
            super(configuration);
1✔
901
            if (!prefix.endsWith("/*")) {
1!
902
                throw new IllegalArgumentException(prefix + " does not end with '/*'");
×
903
            }
904
            _prefix = prefix.substring(0, prefix.length() - 2);
1✔
905
        }
1✔
906

907
        @Override
908
        String getServletPath(String requestPath) {
909
            return _prefix;
1✔
910
        }
911

912
        @Override
913
        String getPathInfo(String requestPath) {
914
            return requestPath.length() > _prefix.length() ? requestPath.substring(_prefix.length()) : null;
1✔
915
        }
916
    }
917

918
    /**
919
     * A utility class for mapping web resources to url patterns. This implements the matching algorithm documented in
920
     * section 10 of the JSDK-2.2 reference.
921
     */
922
    class WebResourceMap {
1✔
923

924
        private final Map _exactMatches = new HashMap<>();
1✔
925
        private final Map _extensions = new HashMap<>();
1✔
926
        private final Map _urlTree = new HashMap<>();
1✔
927
        private WebResourceMapping _defaultMapping;
928

929
        void put(String mapping, WebResourceConfiguration configuration) {
930
            if (mapping.equals("/")) {
1✔
931
                _defaultMapping = new WebResourceMapping(configuration);
1✔
932
            } else if (mapping.startsWith("*.")) {
1✔
933
                _extensions.put(mapping.substring(2), new WebResourceMapping(configuration));
1✔
934
            } else if (!mapping.startsWith("/") || !mapping.endsWith("/*")) {
1!
935
                _exactMatches.put(mapping, new WebResourceMapping(configuration));
1✔
936
            } else {
937
                ParsedPath path = new ParsedPath(mapping);
1✔
938
                Map context = _urlTree;
1✔
939
                while (path.hasNext()) {
1!
940
                    String part = path.next();
1✔
941
                    if (part.equals("*")) {
1✔
942
                        context.put("*", new PartialMatchWebResourceMapping(configuration, mapping));
1✔
943
                        return;
1✔
944
                    }
945
                    if (!context.containsKey(part)) {
1!
946
                        context.put(part, new HashMap<>());
1✔
947
                    }
948
                    context = (Map) context.get(part);
1✔
949
                }
1✔
950
            }
951
        }
1✔
952

953
        ServletMetaData get(URL url) {
954
            String file = url.getFile();
1✔
955
            if (!file.startsWith(_contextPath)) {
1✔
956
                throw new HttpNotFoundException("File path does not begin with '" + _contextPath + "'", url);
1✔
957
            }
958

959
            String servletPath = getServletPath(file.substring(_contextPath.length()));
1✔
960

961
            if (servletPath.endsWith("j_security_check")) {
1✔
962
                return new ServletRequestImpl(url, servletPath, SECURITY_CHECK_MAPPING, _filterMapping,
1✔
963
                        _filterUrlMapping);
964
            }
965
            return new ServletRequestImpl(url, servletPath, getMapping(servletPath), _filterMapping, _filterUrlMapping);
1✔
966
        }
967

968
        private String getServletPath(String urlFile) {
969
            if (urlFile.indexOf('?') < 0) {
1✔
970
                return urlFile;
1✔
971
            }
972
            return urlFile.substring(0, urlFile.indexOf('?'));
1✔
973
        }
974

975
        public void destroyWebResources() {
976
            if (_defaultMapping != null) {
1!
977
                _defaultMapping.destroyResource();
×
978
            }
979
            destroyWebResources(_exactMatches);
1✔
980
            destroyWebResources(_extensions);
1✔
981
            destroyWebResources(_urlTree);
1✔
982
        }
1✔
983

984
        private void destroyWebResources(Map map) {
985
            for (Object o : map.values()) {
1✔
986
                if (o instanceof WebResourceMapping) {
1!
987
                    WebResourceMapping webResourceMapping = (WebResourceMapping) o;
1✔
988
                    webResourceMapping.destroyResource();
1✔
989
                } else {
1✔
990
                    destroyWebResources((Map) o);
×
991
                }
992
            }
1✔
993
        }
1✔
994

995
        void autoLoadServlets() {
996
            ArrayList autoLoadable = new ArrayList<>();
1✔
997
            if (_defaultMapping != null && _defaultMapping.getConfiguration().isLoadOnStartup()) {
1!
998
                autoLoadable.add(_defaultMapping.getConfiguration());
×
999
            }
1000
            collectAutoLoadableServlets(autoLoadable, _exactMatches);
1✔
1001
            collectAutoLoadableServlets(autoLoadable, _extensions);
1✔
1002
            collectAutoLoadableServlets(autoLoadable, _urlTree);
1✔
1003
            if (autoLoadable.isEmpty()) {
1✔
1004
                return;
1✔
1005
            }
1006

1007
            Collections.sort(autoLoadable, (o1, o2) -> {
1✔
1008
                ServletConfiguration sc1 = (ServletConfiguration) o1;
1✔
1009
                ServletConfiguration sc2 = (ServletConfiguration) o2;
1✔
1010
                return sc1.getLoadOrder() <= sc2.getLoadOrder() ? -1 : +1;
1✔
1011
            });
1012
            for (Iterator iterator = autoLoadable.iterator(); iterator.hasNext();) {
1✔
1013
                ServletConfiguration servletConfiguration = (ServletConfiguration) iterator.next();
1✔
1014
                try {
1015
                    servletConfiguration.getServlet();
1✔
1016
                } catch (Exception e) {
×
1017
                    HttpUnitUtils.handleException(e);
×
1018
                    throw new RuntimeException(
×
1019
                            "Unable to autoload servlet: " + servletConfiguration.getClassName() + ": " + e);
×
1020
                }
1✔
1021
            }
1✔
1022
        }
1✔
1023

1024
        private void collectAutoLoadableServlets(Collection collection, Map map) {
1025
            for (Object o : map.values()) {
1✔
1026
                if (o instanceof WebResourceMapping) {
1✔
1027
                    WebResourceMapping servletMapping = (WebResourceMapping) o;
1✔
1028
                    if (servletMapping.getConfiguration().isLoadOnStartup()) {
1✔
1029
                        collection.add(servletMapping.getConfiguration());
1✔
1030
                    }
1031
                } else {
1✔
1032
                    collectAutoLoadableServlets(collection, (Map) o);
1✔
1033
                }
1034
            }
1✔
1035
        }
1✔
1036

1037
        private WebResourceMapping getMapping(String url) {
1038
            if (_exactMatches.containsKey(url)) {
1✔
1039
                return (WebResourceMapping) _exactMatches.get(url);
1✔
1040
            }
1041

1042
            Map context = getContextForLongestPathPrefix(url);
1✔
1043
            if (context.containsKey("*")) {
1✔
1044
                return (WebResourceMapping) context.get("*");
1✔
1045
            }
1046

1047
            if (_extensions.containsKey(getExtension(url))) {
1✔
1048
                return (WebResourceMapping) _extensions.get(getExtension(url));
1✔
1049
            }
1050

1051
            if (_urlTree.containsKey("/")) {
1!
1052
                return (WebResourceMapping) _urlTree.get("/");
×
1053
            }
1054

1055
            if (_defaultMapping != null) {
1✔
1056
                return _defaultMapping;
1✔
1057
            }
1058

1059
            final String prefix = "/servlet/";
1✔
1060
            if (!url.startsWith(prefix)) {
1✔
1061
                return null;
1✔
1062
            }
1063

1064
            String className = url.substring(prefix.length());
1✔
1065
            try {
1066
                Class.forName(className);
1✔
1067
                return new WebResourceMapping(new ServletConfiguration(className));
1✔
1068
            } catch (ClassNotFoundException e) {
×
1069
                return null;
×
1070
            }
1071
        }
1072

1073
        private Map getContextForLongestPathPrefix(String url) {
1074
            Map context = _urlTree;
1✔
1075

1076
            ParsedPath path = new ParsedPath(url);
1✔
1077
            while (path.hasNext()) {
1✔
1078
                String part = path.next();
1✔
1079
                if (!context.containsKey(part)) {
1✔
1080
                    break;
1✔
1081
                }
1082
                context = (Map) context.get(part);
1✔
1083
            }
1✔
1084
            return context;
1✔
1085
        }
1086

1087
        private String getExtension(String url) {
1088
            int index = url.lastIndexOf('.');
1✔
1089
            if (index == -1 || index >= url.length() - 1) {
1!
1090
                return "";
1✔
1091
            }
1092
            return url.substring(index + 1);
1✔
1093
        }
1094

1095
    }
1096

1097
    /**
1098
     * return the given ServletConfiguration for the given servlet name
1099
     *
1100
     * @param servletName
1101
     *
1102
     * @return the corresponding ServletConfiguration
1103
     */
1104
    public ServletConfiguration getServletByName(String servletName) {
1105
        return (ServletConfiguration) _servlets.get(servletName);
1✔
1106
    }
1107

1108
}
1109

1110
/**
1111
 * A utility class for parsing URLs into paths
1112
 */
1113
class ParsedPath {
1114

1115
    private final String path;
1116
    private int position = 0;
1✔
1117
    static final char seperator_char = '/';
1118

1119
    /**
1120
     * Creates a new parsed path for the given path value
1121
     *
1122
     * @param path
1123
     *            the path
1124
     */
1125
    ParsedPath(String path) {
1✔
1126
        if (path.charAt(0) != seperator_char) {
1!
1127
            throw new IllegalArgumentException("Illegal path '" + path + "', does not begin with " + seperator_char);
×
1128
        }
1129
        this.path = path;
1✔
1130
    }
1✔
1131

1132
    /**
1133
     * Returns true if there are more parts left, otherwise false
1134
     */
1135
    public final boolean hasNext() {
1136
        return position < path.length();
1✔
1137
    }
1138

1139
    /**
1140
     * Returns the next part in the path
1141
     */
1142
    public final String next() {
1143
        int offset = position + 1;
1✔
1144
        while (offset < path.length() && path.charAt(offset) != seperator_char) {
1✔
1145
            offset++;
1✔
1146
        }
1147
        String result = path.substring(position + 1, offset);
1✔
1148
        position = offset;
1✔
1149
        return result;
1✔
1150
    }
1151

1152
}
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