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

hazendaz / httpunit / #155

20 Aug 2024 11:42PM UTC coverage: 80.622% (+0.004%) from 80.618%
#155

push

github

hazendaz
[ci] format the code

3231 of 4119 branches covered (78.44%)

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.

8285 of 10165 relevant lines covered (81.51%)

0.82 hits per line

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

91.34
/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 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
 * @author <a href="mailto:russgold@httpunit.org">Russell Gold</a>
54
 * @author <a href="balld@webslingerZ.com">Donald Ball</a>
55
 * @author <a href="jaydunning@users.sourceforge.net">Jay Dunning</a>
56
 **/
57
class WebApplication implements SessionListenerDispatcher {
58

59
    private static final SecurityConstraint NULL_SECURITY_CONSTRAINT = new NullSecurityConstraint();
1✔
60

61
    private final ServletConfiguration SECURITY_CHECK_CONFIGURATION = new ServletConfiguration(
1✔
62
            SecurityCheckServlet.class.getName());
1✔
63

64
    private final WebResourceMapping SECURITY_CHECK_MAPPING = new WebResourceMapping(SECURITY_CHECK_CONFIGURATION);
1✔
65

66
    /** A mapping of resource names to servlet configurations. **/
67
    private WebResourceMap _servletMapping = new WebResourceMap();
1✔
68

69
    /** A mapping of filter names to FilterConfigurations */
70
    private Hashtable _filters = new Hashtable();
1✔
71

72
    /** A mapping of servlet names to ServletConfigurations */
73
    private Hashtable _servlets = new Hashtable();
1✔
74

75
    /** A mapping of resource names to filter configurations. **/
76
    private FilterUrlMap _filterUrlMapping = new FilterUrlMap();
1✔
77

78
    /** A mapping of servlet names to filter configurations. **/
79
    private Hashtable _filterMapping = new Hashtable();
1✔
80

81
    private List<SecurityConstraint> _securityConstraints = new ArrayList<>();
1✔
82

83
    private List<ServletContextListener> _contextListeners = new ArrayList<>();
1✔
84

85
    private List<ServletContextAttributeListener> _contextAttributeListeners = new ArrayList<>();
1✔
86

87
    private List<HttpSessionListener> _sessionListeners = new ArrayList<>();
1✔
88

89
    private List<HttpSessionAttributeListener> _sessionAttributeListeners = new ArrayList<>();
1✔
90

91
    private boolean _useBasicAuthentication;
92

93
    private boolean _useFormAuthentication;
94

95
    private String _authenticationRealm = "";
1✔
96

97
    private URL _loginURL;
98

99
    private URL _errorURL;
100

101
    private Hashtable _contextParameters = new Hashtable();
1✔
102

103
    private File _contextDir = null;
1✔
104

105
    private String _contextPath = null;
1✔
106

107
    private ServletUnitServletContext _servletContext;
108

109
    private String _displayName;
110

111
    /**
112
     * Constructs a default application spec with no information.
113
     */
114
    WebApplication() {
1✔
115
        _contextPath = "";
1✔
116
    }
1✔
117

118
    /**
119
     * Constructs an application spec from an XML document.
120
     */
121
    WebApplication(Document document) throws MalformedURLException, SAXException {
122
        this(document, null, "");
1✔
123
    }
1✔
124

125
    /**
126
     * Constructs an application spec from an XML document.
127
     */
128
    WebApplication(Document document, String contextPath) throws MalformedURLException, SAXException {
129
        this(document, null, contextPath);
1✔
130
    }
1✔
131

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

146
        registerServlets(document);
1✔
147
        registerFilters(document);
1✔
148
        extractSecurityConstraints(document);
1✔
149
        extractContextParameters(document);
1✔
150
        extractLoginConfiguration(document);
1✔
151
        extractListeners(document);
1✔
152
        notifyContextInitialized();
1✔
153
        _servletMapping.autoLoadServlets();
1✔
154
    }
1✔
155

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

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

181
    private void notifyContextInitialized() {
182
        ServletContextEvent event = new ServletContextEvent(getServletContext());
1✔
183

184
        for (Iterator i = _contextListeners.iterator(); i.hasNext();) {
1✔
185
            ServletContextListener listener = (ServletContextListener) i.next();
1✔
186
            listener.contextInitialized(event);
1✔
187
        }
1✔
188
    }
1✔
189

190
    void shutDown() {
191
        destroyServlets();
1✔
192
        notifyContextDestroyed();
1✔
193
    }
1✔
194

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

198
        for (ListIterator i = _contextListeners.listIterator(_contextListeners.size()); i.hasPrevious();) {
1✔
199
            ServletContextListener listener = (ServletContextListener) i.previous();
1✔
200
            listener.contextDestroyed(event);
1✔
201
        }
1✔
202
    }
1✔
203

204
    void sendAttributeAdded(String name, Object value) {
205
        ServletContextAttributeEvent event = new ServletContextAttributeEvent(getServletContext(), name, value);
1✔
206

207
        for (Iterator i = _contextAttributeListeners.iterator(); i.hasNext();) {
1✔
208
            ServletContextAttributeListener listener = (ServletContextAttributeListener) i.next();
1✔
209
            listener.attributeAdded(event);
1✔
210
        }
1✔
211
    }
1✔
212

213
    void sendAttributeReplaced(String name, Object value) {
214
        ServletContextAttributeEvent event = new ServletContextAttributeEvent(getServletContext(), name, value);
1✔
215

216
        for (Iterator i = _contextAttributeListeners.iterator(); i.hasNext();) {
1✔
217
            ServletContextAttributeListener listener = (ServletContextAttributeListener) i.next();
1✔
218
            listener.attributeReplaced(event);
1✔
219
        }
1✔
220
    }
1✔
221

222
    void sendAttributeRemoved(String name, Object value) {
223
        ServletContextAttributeEvent event = new ServletContextAttributeEvent(getServletContext(), name, value);
1✔
224

225
        for (Iterator i = _contextAttributeListeners.iterator(); i.hasNext();) {
1✔
226
            ServletContextAttributeListener listener = (ServletContextAttributeListener) i.next();
1✔
227
            listener.attributeRemoved(event);
1✔
228
        }
1✔
229
    }
1✔
230

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

238
    String getContextPath() {
239
        return _contextPath;
1✔
240
    }
241

242
    ServletContext getServletContext() {
243
        if (_servletContext == null) {
1✔
244
            _servletContext = new ServletUnitServletContext(this);
1✔
245
        }
246
        return _servletContext;
1✔
247
    }
248

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

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

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

274
    ServletMetaData getServletRequest(URL url) {
275
        return _servletMapping.get(url);
1✔
276
    }
277

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

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

292
    String getAuthenticationRealm() {
293
        return _authenticationRealm;
1✔
294
    }
295

296
    URL getLoginURL() {
297
        return _loginURL;
1✔
298
    }
299

300
    URL getErrorURL() {
301
        return _errorURL;
1✔
302
    }
303

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

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

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

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

356
    Hashtable getContextParameters() {
357
        return _contextParameters;
1✔
358
    }
359

360
    // ---------------------------------------- SessionListenerDispatcher methods
361
    // -------------------------------------------
362

363
    @Override
364
    public void sendSessionCreated(HttpSession session) {
365
        HttpSessionEvent event = new HttpSessionEvent(session);
1✔
366

367
        for (HttpSessionListener listener : _sessionListeners) {
1✔
368
            listener.sessionCreated(event);
1✔
369
        }
1✔
370
    }
1✔
371

372
    @Override
373
    public void sendSessionDestroyed(HttpSession session) {
374
        HttpSessionEvent event = new HttpSessionEvent(session);
1✔
375

376
        for (HttpSessionListener listener : _sessionListeners) {
1✔
377
            listener.sessionDestroyed(event);
1✔
378
        }
1✔
379
    }
1✔
380

381
    @Override
382
    public void sendAttributeAdded(HttpSession session, String name, Object value) {
383
        HttpSessionBindingEvent event = new HttpSessionBindingEvent(session, name, value);
1✔
384

385
        for (HttpSessionAttributeListener listener : _sessionAttributeListeners) {
1✔
386
            listener.attributeAdded(event);
1✔
387
        }
1✔
388
    }
1✔
389

390
    @Override
391
    public void sendAttributeReplaced(HttpSession session, String name, Object oldValue) {
392
        HttpSessionBindingEvent event = new HttpSessionBindingEvent(session, name, oldValue);
1✔
393

394
        for (HttpSessionAttributeListener listener : _sessionAttributeListeners) {
1✔
395
            listener.attributeReplaced(event);
1✔
396
        }
1✔
397
    }
1✔
398

399
    @Override
400
    public void sendAttributeRemoved(HttpSession session, String name, Object oldValue) {
401
        HttpSessionBindingEvent event = new HttpSessionBindingEvent(session, name, oldValue);
1✔
402

403
        for (HttpSessionAttributeListener listener : _sessionAttributeListeners) {
1✔
404
            listener.attributeRemoved(event);
1✔
405
        }
1✔
406
    }
1✔
407

408
    // --------------------------------------------------- private members
409
    // --------------------------------------------------
410

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

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

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

440
    private void registerFilterForUrl(String resourceName, FilterConfiguration filterConfiguration) {
441
        _filterUrlMapping.put(resourceName, filterConfiguration);
1✔
442
    }
1✔
443

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

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

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

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

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

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

510
    private static boolean patternMatches(String urlPattern, String urlPath) {
511
        return urlPattern.equals(urlPath);
1✔
512
    }
513

514
    String getDisplayName() {
515
        return _displayName;
1✔
516
    }
517

518
    // ============================================= SecurityCheckServlet class
519
    // =============================================
520

521
    static class SecurityCheckServlet extends HttpServlet {
1✔
522

523
        private static final long serialVersionUID = 1L;
524

525
        @Override
526
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
527
            handleLogin(req, resp);
×
528
        }
×
529

530
        @Override
531
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
532
            handleLogin(req, resp);
1✔
533
        }
1✔
534

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

542
        private ServletUnitHttpSession getServletSession(HttpServletRequest req) {
543
            return (ServletUnitHttpSession) req.getSession();
1✔
544
        }
545

546
    }
547

548
    // ============================================= ServletConfiguration class
549
    // =============================================
550

551
    static final int DONT_AUTOLOAD = Integer.MIN_VALUE;
552
    static final int ANY_LOAD_ORDER = Integer.MAX_VALUE;
553

554
    class ServletConfiguration extends WebResourceConfiguration {
555

556
        private Servlet _servlet;
557
        private String _servletName;
558
        private String _jspFile;
559
        private int _loadOrder = DONT_AUTOLOAD;
1✔
560

561
        ServletConfiguration(String className) {
1✔
562
            super(className);
1✔
563
        }
1✔
564

565
        ServletConfiguration(String className, Hashtable initParams) {
1✔
566
            super(className, initParams);
1✔
567
        }
1✔
568

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

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

598
            return _servlet;
1✔
599
        }
600

601
        @Override
602
        synchronized void destroyResource() {
603
            if (_servlet != null) {
1✔
604
                _servlet.destroy();
1✔
605
            }
606
        }
1✔
607

608
        String getServletName() {
609
            return _servletName;
1✔
610
        }
611

612
        @Override
613
        boolean isLoadOnStartup() {
614
            return _loadOrder != DONT_AUTOLOAD;
1✔
615
        }
616

617
        public int getLoadOrder() {
618
            return _loadOrder;
1✔
619
        }
620

621
        public Object getJspFile() {
622
            return this._jspFile;
×
623
        }
624
    }
625

626
    // ============================================= FilterConfiguration class
627
    // =============================================
628

629
    class FilterConfiguration extends WebResourceConfiguration implements FilterMetaData {
630

631
        private Filter _filter;
632
        private String _name;
633

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

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

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

662
        @Override
663
        boolean isLoadOnStartup() {
664
            return false;
×
665
        }
666

667
        @Override
668
        synchronized void destroyResource() {
669
            if (_filter != null) {
×
670
                _filter.destroy();
×
671
            }
672
        }
×
673
    }
674

675
    // =================================== SecurityConstract interface and implementations
676
    // ==================================
677

678
    interface SecurityConstraint {
679

680
        boolean controlsPath(String urlPath);
681

682
        String[] getPermittedRoles();
683
    }
684

685
    static class NullSecurityConstraint implements SecurityConstraint {
1✔
686

687
        private static final String[] NO_ROLES = {};
1✔
688

689
        @Override
690
        public boolean controlsPath(String urlPath) {
691
            return false;
×
692
        }
693

694
        @Override
695
        public String[] getPermittedRoles() {
696
            return NO_ROLES;
×
697
        }
698
    }
699

700
    static class SecurityConstraintImpl implements SecurityConstraint {
701

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

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

714
        @Override
715
        public boolean controlsPath(String urlPath) {
716
            return getMatchingCollection(urlPath) != null;
1✔
717
        }
718

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

727
        private String[] _roles;
728
        private List<String> _roleList = new ArrayList<>();
1✔
729
        private List<WebResourceCollection> _resources = new ArrayList<>();
1✔
730

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

740
        class WebResourceCollection {
741

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

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

758
            private List<String> _urlPatterns = new ArrayList<>();
1✔
759
        }
760
    }
761

762
    static final FilterMetaData[] NO_FILTERS = {};
1✔
763

764
    static class ServletRequestImpl implements ServletMetaData {
765

766
        private URL _url;
767
        private String _fullServletPath;
768
        private WebResourceMapping _mapping;
769
        private Hashtable _filtersPerName;
770
        private FilterUrlMap _filtersPerUrl;
771

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

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

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

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

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

823
        @Override
824
        public FilterMetaData[] getFilters() {
825
            if (getConfiguration() == null) {
1✔
826
                return NO_FILTERS;
1✔
827
            }
828

829
            List<FilterMetaData> filters = new ArrayList<>();
1✔
830
            addFiltersForPath(filters, _fullServletPath);
1✔
831
            addFiltersForServletWithName(filters, getConfiguration().getServletName());
1✔
832

833
            return filters.toArray(new FilterMetaData[filters.size()]);
1✔
834
        }
835

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

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

851
        private ServletConfiguration getConfiguration() {
852
            return _mapping == null ? null : (ServletConfiguration) _mapping.getConfiguration();
1✔
853
        }
854
    }
855

856
    /**
857
     * mapping for WebResources
858
     */
859
    static class WebResourceMapping {
860

861
        private WebResourceConfiguration _configuration;
862

863
        WebResourceConfiguration getConfiguration() {
864
            return _configuration;
1✔
865
        }
866

867
        WebResourceMapping(WebResourceConfiguration configuration) {
1✔
868
            _configuration = configuration;
1✔
869
        }
1✔
870

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

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

893
        public void destroyResource() {
894
            getConfiguration().destroyResource();
1✔
895
        }
1✔
896
    }
897

898
    static class PartialMatchWebResourceMapping extends WebResourceMapping {
899

900
        private String _prefix;
901

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

910
        @Override
911
        String getServletPath(String requestPath) {
912
            return _prefix;
1✔
913
        }
914

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

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

927
        private final Map _exactMatches = new HashMap();
1✔
928
        private final Map _extensions = new HashMap();
1✔
929
        private final Map _urlTree = new HashMap();
1✔
930
        private WebResourceMapping _defaultMapping;
931

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

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

962
            String servletPath = getServletPath(file.substring(_contextPath.length()));
1✔
963

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

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

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

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

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

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

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

1040
        private WebResourceMapping getMapping(String url) {
1041
            if (_exactMatches.containsKey(url)) {
1✔
1042
                return (WebResourceMapping) _exactMatches.get(url);
1✔
1043
            }
1044

1045
            Map context = getContextForLongestPathPrefix(url);
1✔
1046
            if (context.containsKey("*")) {
1✔
1047
                return (WebResourceMapping) context.get("*");
1✔
1048
            }
1049

1050
            if (_extensions.containsKey(getExtension(url))) {
1✔
1051
                return (WebResourceMapping) _extensions.get(getExtension(url));
1✔
1052
            }
1053

1054
            if (_urlTree.containsKey("/")) {
1!
1055
                return (WebResourceMapping) _urlTree.get("/");
×
1056
            }
1057

1058
            if (_defaultMapping != null) {
1✔
1059
                return _defaultMapping;
1✔
1060
            }
1061

1062
            final String prefix = "/servlet/";
1✔
1063
            if (!url.startsWith(prefix)) {
1✔
1064
                return null;
1✔
1065
            }
1066

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

1076
        private Map getContextForLongestPathPrefix(String url) {
1077
            Map context = _urlTree;
1✔
1078

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

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

1098
    }
1099

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

1111
}
1112

1113
/**
1114
 * A utility class for parsing URLs into paths
1115
 *
1116
 * @author <a href="balld@webslingerZ.com">Donald Ball</a>
1117
 */
1118
class ParsedPath {
1119

1120
    private final String path;
1121
    private int position = 0;
1✔
1122
    static final char seperator_char = '/';
1123

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

1137
    /**
1138
     * Returns true if there are more parts left, otherwise false
1139
     */
1140
    public final boolean hasNext() {
1141
        return position < path.length();
1✔
1142
    }
1143

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

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