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

hazendaz / httpunit / 656

06 Dec 2025 09:11PM UTC coverage: 80.452% (+0.02%) from 80.435%
656

push

github

hazendaz
[maven-release-plugin] prepare for next development iteration

3213 of 4105 branches covered (78.27%)

Branch coverage included in aggregate %.

8245 of 10137 relevant lines covered (81.34%)

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 java.io.File;
27
import java.io.IOException;
28
import java.lang.reflect.InvocationTargetException;
29
import java.net.MalformedURLException;
30
import java.net.URL;
31
import java.nio.file.Path;
32
import java.util.ArrayList;
33
import java.util.Collection;
34
import java.util.Collections;
35
import java.util.Dictionary;
36
import java.util.HashMap;
37
import java.util.Hashtable;
38
import java.util.Iterator;
39
import java.util.List;
40
import java.util.ListIterator;
41
import java.util.Map;
42

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

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

65
/**
66
 * This class represents the information recorded about a single web application. It is usually extracted from web.xml.
67
 **/
68
class WebApplication implements SessionListenerDispatcher {
69

70
    /** The Constant NULL_SECURITY_CONSTRAINT. */
71
    private static final SecurityConstraint NULL_SECURITY_CONSTRAINT = new NullSecurityConstraint();
1✔
72

73
    /** The security check configuration. */
74
    private final ServletConfiguration SECURITY_CHECK_CONFIGURATION = new ServletConfiguration(
1✔
75
            SecurityCheckServlet.class.getName());
1✔
76

77
    /** The security check mapping. */
78
    private final WebResourceMapping SECURITY_CHECK_MAPPING = new WebResourceMapping(SECURITY_CHECK_CONFIGURATION);
1✔
79

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

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

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

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

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

95
    /** The security constraints. */
96
    private List<SecurityConstraint> _securityConstraints = new ArrayList<>();
1✔
97

98
    /** The context listeners. */
99
    private List<ServletContextListener> _contextListeners = new ArrayList<>();
1✔
100

101
    /** The context attribute listeners. */
102
    private List<ServletContextAttributeListener> _contextAttributeListeners = new ArrayList<>();
1✔
103

104
    /** The session listeners. */
105
    private List<HttpSessionListener> _sessionListeners = new ArrayList<>();
1✔
106

107
    /** The session attribute listeners. */
108
    private List<HttpSessionAttributeListener> _sessionAttributeListeners = new ArrayList<>();
1✔
109

110
    /** The use basic authentication. */
111
    private boolean _useBasicAuthentication;
112

113
    /** The use form authentication. */
114
    private boolean _useFormAuthentication;
115

116
    /** The authentication realm. */
117
    private String _authenticationRealm = "";
1✔
118

119
    /** The login URL. */
120
    private URL _loginURL;
121

122
    /** The error URL. */
123
    private URL _errorURL;
124

125
    /** The context parameters. */
126
    private Hashtable _contextParameters = new Hashtable<>();
1✔
127

128
    /** The context dir. */
129
    private File _contextDir = null;
1✔
130

131
    /** The context path. */
132
    private String _contextPath = null;
1✔
133

134
    /** The servlet context. */
135
    private ServletUnitServletContext _servletContext;
136

137
    /** The display name. */
138
    private String _displayName;
139

140
    /**
141
     * Constructs a default application spec with no information.
142
     */
143
    WebApplication() {
1✔
144
        _contextPath = "";
1✔
145
    }
1✔
146

147
    /**
148
     * Constructs an application spec from an XML document.
149
     *
150
     * @param document
151
     *            the document
152
     *
153
     * @throws MalformedURLException
154
     *             the malformed URL exception
155
     * @throws SAXException
156
     *             the SAX exception
157
     */
158
    WebApplication(Document document) throws MalformedURLException, SAXException {
159
        this(document, null, "");
1✔
160
    }
1✔
161

162
    /**
163
     * Constructs an application spec from an XML document.
164
     *
165
     * @param document
166
     *            the document
167
     * @param contextPath
168
     *            the context path
169
     *
170
     * @throws MalformedURLException
171
     *             the malformed URL exception
172
     * @throws SAXException
173
     *             the SAX exception
174
     */
175
    WebApplication(Document document, String contextPath) throws MalformedURLException, SAXException {
176
        this(document, null, contextPath);
1✔
177
    }
1✔
178

179
    /**
180
     * Constructs an application spec from an XML document.
181
     *
182
     * @param document
183
     *            the document
184
     * @param file
185
     *            the file
186
     * @param contextPath
187
     *            the context path
188
     *
189
     * @throws MalformedURLException
190
     *             the malformed URL exception
191
     * @throws SAXException
192
     *             the SAX exception
193
     */
194
    WebApplication(Document document, File file, String contextPath) throws MalformedURLException, SAXException {
1✔
195
        if (contextPath != null && !contextPath.isEmpty() && !contextPath.startsWith("/")) {
1!
196
            throw new IllegalArgumentException("Context path " + contextPath + " must start with '/'");
×
197
        }
198
        _contextDir = file;
1✔
199
        _contextPath = contextPath == null ? "" : contextPath;
1✔
200
        NodeList nl = document.getElementsByTagName("display-name");
1✔
201
        if (nl.getLength() > 0) {
1✔
202
            _displayName = XMLUtils.getTextValue(nl.item(0)).trim();
1✔
203
        }
204

205
        registerServlets(document);
1✔
206
        registerFilters(document);
1✔
207
        extractSecurityConstraints(document);
1✔
208
        extractContextParameters(document);
1✔
209
        extractLoginConfiguration(document);
1✔
210
        extractListeners(document);
1✔
211
        notifyContextInitialized();
1✔
212
        _servletMapping.autoLoadServlets();
1✔
213
    }
1✔
214

215
    /**
216
     * Extract listeners.
217
     *
218
     * @param document
219
     *            the document
220
     *
221
     * @throws SAXException
222
     *             the SAX exception
223
     */
224
    private void extractListeners(Document document) throws SAXException {
225
        NodeList nl = document.getElementsByTagName("listener");
1✔
226
        for (int i = 0; i < nl.getLength(); i++) {
1✔
227
            String listenerName = XMLUtils.getChildNodeValue((Element) nl.item(i), "listener-class").trim();
1✔
228
            try {
229
                Object listener = Class.forName(listenerName).getDeclaredConstructor().newInstance();
1✔
230

231
                if (listener instanceof ServletContextListener) {
1✔
232
                    _contextListeners.add((ServletContextListener) listener);
1✔
233
                }
234
                if (listener instanceof ServletContextAttributeListener) {
1✔
235
                    _contextAttributeListeners.add((ServletContextAttributeListener) listener);
1✔
236
                }
237
                if (listener instanceof HttpSessionListener) {
1✔
238
                    _sessionListeners.add((HttpSessionListener) listener);
1✔
239
                }
240
                if (listener instanceof HttpSessionAttributeListener) {
1✔
241
                    _sessionAttributeListeners.add((HttpSessionAttributeListener) listener);
1✔
242
                }
243
            } catch (Throwable e) {
×
244
                throw new RuntimeException("Unable to load context listener " + listenerName + ": " + e.toString());
×
245
            }
1✔
246
        }
247
    }
1✔
248

249
    /**
250
     * Notify context initialized.
251
     */
252
    private void notifyContextInitialized() {
253
        ServletContextEvent event = new ServletContextEvent(getServletContext());
1✔
254

255
        for (Iterator<ServletContextListener> i = _contextListeners.iterator(); i.hasNext();) {
1✔
256
            ServletContextListener listener = i.next();
1✔
257
            listener.contextInitialized(event);
1✔
258
        }
1✔
259
    }
1✔
260

261
    /**
262
     * Shut down.
263
     */
264
    void shutDown() {
265
        destroyServlets();
1✔
266
        notifyContextDestroyed();
1✔
267
    }
1✔
268

269
    /**
270
     * Notify context destroyed.
271
     */
272
    private void notifyContextDestroyed() {
273
        ServletContextEvent event = new ServletContextEvent(getServletContext());
1✔
274

275
        for (ListIterator<ServletContextListener> i = _contextListeners.listIterator(_contextListeners.size()); i
1✔
276
                .hasPrevious();) {
1✔
277
            ServletContextListener listener = i.previous();
1✔
278
            listener.contextDestroyed(event);
1✔
279
        }
1✔
280
    }
1✔
281

282
    /**
283
     * Send attribute added.
284
     *
285
     * @param name
286
     *            the name
287
     * @param value
288
     *            the value
289
     */
290
    void sendAttributeAdded(String name, Object value) {
291
        ServletContextAttributeEvent event = new ServletContextAttributeEvent(getServletContext(), name, value);
1✔
292

293
        for (Iterator<ServletContextAttributeListener> i = _contextAttributeListeners.iterator(); i.hasNext();) {
1✔
294
            ServletContextAttributeListener listener = i.next();
1✔
295
            listener.attributeAdded(event);
1✔
296
        }
1✔
297
    }
1✔
298

299
    /**
300
     * Send attribute replaced.
301
     *
302
     * @param name
303
     *            the name
304
     * @param value
305
     *            the value
306
     */
307
    void sendAttributeReplaced(String name, Object value) {
308
        ServletContextAttributeEvent event = new ServletContextAttributeEvent(getServletContext(), name, value);
1✔
309

310
        for (Iterator<ServletContextAttributeListener> i = _contextAttributeListeners.iterator(); i.hasNext();) {
1✔
311
            ServletContextAttributeListener listener = i.next();
1✔
312
            listener.attributeReplaced(event);
1✔
313
        }
1✔
314
    }
1✔
315

316
    /**
317
     * Send attribute removed.
318
     *
319
     * @param name
320
     *            the name
321
     * @param value
322
     *            the value
323
     */
324
    void sendAttributeRemoved(String name, Object value) {
325
        ServletContextAttributeEvent event = new ServletContextAttributeEvent(getServletContext(), name, value);
1✔
326

327
        for (Iterator<ServletContextAttributeListener> i = _contextAttributeListeners.iterator(); i.hasNext();) {
1✔
328
            ServletContextAttributeListener listener = i.next();
1✔
329
            listener.attributeRemoved(event);
1✔
330
        }
1✔
331
    }
1✔
332

333
    /**
334
     * Extract security constraints.
335
     *
336
     * @param document
337
     *            the document
338
     *
339
     * @throws SAXException
340
     *             the SAX exception
341
     */
342
    private void extractSecurityConstraints(Document document) throws SAXException {
343
        NodeList nl = document.getElementsByTagName("security-constraint");
1✔
344
        for (int i = 0; i < nl.getLength(); i++) {
1✔
345
            _securityConstraints.add(new SecurityConstraintImpl((Element) nl.item(i)));
1✔
346
        }
347
    }
1✔
348

349
    /**
350
     * Gets the context path.
351
     *
352
     * @return the context path
353
     */
354
    String getContextPath() {
355
        return _contextPath;
1✔
356
    }
357

358
    /**
359
     * Gets the servlet context.
360
     *
361
     * @return the servlet context
362
     */
363
    ServletContext getServletContext() {
364
        if (_servletContext == null) {
1✔
365
            _servletContext = new ServletUnitServletContext(this);
1✔
366
        }
367
        return _servletContext;
1✔
368
    }
369

370
    /**
371
     * Registers a servlet class to be run.
372
     *
373
     * @param resourceName
374
     *            the resource name
375
     * @param servletClassName
376
     *            the servlet class name
377
     * @param initParams
378
     *            the init params
379
     */
380
    void registerServlet(String resourceName, String servletClassName, Hashtable initParams) {
381
        registerServlet(resourceName, new ServletConfiguration(servletClassName, initParams));
1✔
382
    }
1✔
383

384
    /**
385
     * Registers a servlet to be run.
386
     *
387
     * @param resourceName
388
     *            the resource name
389
     * @param servletConfiguration
390
     *            the servlet configuration
391
     */
392
    void registerServlet(String resourceName, ServletConfiguration servletConfiguration) {
393
        // FIXME - shouldn't everything start with one or the other?
394
        if (!resourceName.startsWith("/") && !resourceName.startsWith("*")) {
1✔
395
            resourceName = "/" + resourceName;
1✔
396
        }
397
        _servletMapping.put(resourceName, servletConfiguration);
1✔
398
    }
1✔
399

400
    /**
401
     * Calls the destroy method for every active servlet.
402
     */
403
    void destroyServlets() {
404
        _servletMapping.destroyWebResources();
1✔
405
    }
1✔
406

407
    /**
408
     * Gets the servlet request.
409
     *
410
     * @param url
411
     *            the url
412
     *
413
     * @return the servlet request
414
     */
415
    ServletMetaData getServletRequest(URL url) {
416
        return _servletMapping.get(url);
1✔
417
    }
418

419
    /**
420
     * Returns true if this application uses Basic Authentication.
421
     *
422
     * @return true, if successful
423
     */
424
    boolean usesBasicAuthentication() {
425
        return _useBasicAuthentication;
1✔
426
    }
427

428
    /**
429
     * Returns true if this application uses form-based authentication.
430
     *
431
     * @return true, if successful
432
     */
433
    boolean usesFormAuthentication() {
434
        return _useFormAuthentication;
1✔
435
    }
436

437
    /**
438
     * Gets the authentication realm.
439
     *
440
     * @return the authentication realm
441
     */
442
    String getAuthenticationRealm() {
443
        return _authenticationRealm;
1✔
444
    }
445

446
    /**
447
     * Gets the login URL.
448
     *
449
     * @return the login URL
450
     */
451
    URL getLoginURL() {
452
        return _loginURL;
1✔
453
    }
454

455
    /**
456
     * Gets the error URL.
457
     *
458
     * @return the error URL
459
     */
460
    URL getErrorURL() {
461
        return _errorURL;
1✔
462
    }
463

464
    /**
465
     * Returns true if the specified path may only be accesses by an authorized user.
466
     *
467
     * @param url
468
     *            the application-relative path of the URL
469
     *
470
     * @return true, if successful
471
     */
472
    boolean requiresAuthorization(URL url) {
473
        String result;
474
        String file = url.getFile();
1✔
475
        if (_contextPath.equals("")) {
1✔
476
            result = file;
1✔
477
        } else if (file.startsWith(_contextPath)) {
1!
478
            result = file.substring(_contextPath.length());
1✔
479
        } else {
480
            result = null;
×
481
        }
482
        return getControllingConstraint(result) != NULL_SECURITY_CONSTRAINT;
1✔
483
    }
484

485
    /**
486
     * Returns an array containing the roles permitted to access the specified URL.
487
     *
488
     * @param url
489
     *            the url
490
     *
491
     * @return the permitted roles
492
     */
493
    String[] getPermittedRoles(URL url) {
494
        String result;
495
        String file = url.getFile();
1✔
496
        if (_contextPath.equals("")) {
1✔
497
            result = file;
1✔
498
        } else if (file.startsWith(_contextPath)) {
1!
499
            result = file.substring(_contextPath.length());
1✔
500
        } else {
501
            result = null;
×
502
        }
503
        return getControllingConstraint(result).getPermittedRoles();
1✔
504
    }
505

506
    /**
507
     * Gets the controlling constraint.
508
     *
509
     * @param urlPath
510
     *            the url path
511
     *
512
     * @return the controlling constraint
513
     */
514
    private SecurityConstraint getControllingConstraint(String urlPath) {
515
        for (SecurityConstraint sc : _securityConstraints) {
1✔
516
            if (sc.controlsPath(urlPath)) {
1✔
517
                return sc;
1✔
518
            }
519
        }
1✔
520
        return NULL_SECURITY_CONSTRAINT;
1✔
521
    }
522

523
    /**
524
     * Gets the resource file.
525
     *
526
     * @param path
527
     *            the path
528
     *
529
     * @return the resource file
530
     */
531
    File getResourceFile(String path) {
532
        String relativePath = path.startsWith("/") ? path.substring(1) : path;
1✔
533
        if (_contextDir == null) {
1✔
534
            return Path.of(relativePath).toFile();
1✔
535
        }
536
        return _contextDir.toPath().resolve(relativePath).toFile();
1✔
537
    }
538

539
    /**
540
     * Gets the context parameters.
541
     *
542
     * @return the context parameters
543
     */
544
    Hashtable getContextParameters() {
545
        return _contextParameters;
1✔
546
    }
547

548
    // ---------------------------------------- SessionListenerDispatcher methods
549
    // -------------------------------------------
550

551
    @Override
552
    public void sendSessionCreated(HttpSession session) {
553
        HttpSessionEvent event = new HttpSessionEvent(session);
1✔
554

555
        for (HttpSessionListener listener : _sessionListeners) {
1✔
556
            listener.sessionCreated(event);
1✔
557
        }
1✔
558
    }
1✔
559

560
    @Override
561
    public void sendSessionDestroyed(HttpSession session) {
562
        HttpSessionEvent event = new HttpSessionEvent(session);
1✔
563

564
        for (HttpSessionListener listener : _sessionListeners) {
1✔
565
            listener.sessionDestroyed(event);
1✔
566
        }
1✔
567
    }
1✔
568

569
    @Override
570
    public void sendAttributeAdded(HttpSession session, String name, Object value) {
571
        HttpSessionBindingEvent event = new HttpSessionBindingEvent(session, name, value);
1✔
572

573
        for (HttpSessionAttributeListener listener : _sessionAttributeListeners) {
1✔
574
            listener.attributeAdded(event);
1✔
575
        }
1✔
576
    }
1✔
577

578
    @Override
579
    public void sendAttributeReplaced(HttpSession session, String name, Object oldValue) {
580
        HttpSessionBindingEvent event = new HttpSessionBindingEvent(session, name, oldValue);
1✔
581

582
        for (HttpSessionAttributeListener listener : _sessionAttributeListeners) {
1✔
583
            listener.attributeReplaced(event);
1✔
584
        }
1✔
585
    }
1✔
586

587
    @Override
588
    public void sendAttributeRemoved(HttpSession session, String name, Object oldValue) {
589
        HttpSessionBindingEvent event = new HttpSessionBindingEvent(session, name, oldValue);
1✔
590

591
        for (HttpSessionAttributeListener listener : _sessionAttributeListeners) {
1✔
592
            listener.attributeRemoved(event);
1✔
593
        }
1✔
594
    }
1✔
595

596
    // --------------------------------------------------- private members
597
    // --------------------------------------------------
598

599
    /**
600
     * Register filters.
601
     *
602
     * @param document
603
     *            the document
604
     *
605
     * @throws SAXException
606
     *             the SAX exception
607
     */
608
    private void registerFilters(Document document) throws SAXException {
609
        Hashtable nameToClass = new Hashtable<>();
1✔
610
        NodeList nl = document.getElementsByTagName("filter");
1✔
611
        for (int i = 0; i < nl.getLength(); i++) {
1✔
612
            registerFilterClass(nameToClass, (Element) nl.item(i));
1✔
613
        }
614
        nl = document.getElementsByTagName("filter-mapping");
1✔
615
        for (int i = 0; i < nl.getLength(); i++) {
1✔
616
            registerFilter(nameToClass, (Element) nl.item(i));
1✔
617
        }
618
        this._filters = nameToClass;
1✔
619
    }
1✔
620

621
    /**
622
     * Register filter class.
623
     *
624
     * @param mapping
625
     *            the mapping
626
     * @param filterElement
627
     *            the filter element
628
     *
629
     * @throws SAXException
630
     *             the SAX exception
631
     */
632
    private void registerFilterClass(Dictionary mapping, Element filterElement) throws SAXException {
633
        String filterName = XMLUtils.getChildNodeValue(filterElement, "filter-name");
1✔
634
        mapping.put(filterName, new FilterConfiguration(filterName, filterElement));
1✔
635
    }
1✔
636

637
    /**
638
     * Register filter.
639
     *
640
     * @param mapping
641
     *            the mapping
642
     * @param filterElement
643
     *            the filter element
644
     *
645
     * @throws SAXException
646
     *             the SAX exception
647
     */
648
    private void registerFilter(Dictionary mapping, Element filterElement) throws SAXException {
649
        if (XMLUtils.hasChildNode(filterElement, "servlet-name")) {
1✔
650
            registerFilterForServlet(XMLUtils.getChildNodeValue(filterElement, "servlet-name"),
1✔
651
                    (FilterConfiguration) mapping.get(XMLUtils.getChildNodeValue(filterElement, "filter-name")));
1✔
652
        }
653
        if (XMLUtils.hasChildNode(filterElement, "url-pattern")) {
1✔
654
            registerFilterForUrl(XMLUtils.getChildNodeValue(filterElement, "url-pattern"),
1✔
655
                    (FilterConfiguration) mapping.get(XMLUtils.getChildNodeValue(filterElement, "filter-name")));
1✔
656
        }
657
    }
1✔
658

659
    /**
660
     * Register filter for url.
661
     *
662
     * @param resourceName
663
     *            the resource name
664
     * @param filterConfiguration
665
     *            the filter configuration
666
     */
667
    private void registerFilterForUrl(String resourceName, FilterConfiguration filterConfiguration) {
668
        _filterUrlMapping.put(resourceName, filterConfiguration);
1✔
669
    }
1✔
670

671
    /**
672
     * Register filter for servlet.
673
     *
674
     * @param servletName
675
     *            the servlet name
676
     * @param filterConfiguration
677
     *            the filter configuration
678
     */
679
    private void registerFilterForServlet(String servletName, FilterConfiguration filterConfiguration) {
680
        List list = (List) _filterMapping.get(servletName);
1✔
681
        if (list == null) {
1✔
682
            list = new ArrayList<>();
1✔
683
            _filterMapping.put(servletName, list);
1✔
684
        }
685
        list.add(filterConfiguration);
1✔
686
    }
1✔
687

688
    /**
689
     * Extract login configuration.
690
     *
691
     * @param document
692
     *            the document
693
     *
694
     * @throws MalformedURLException
695
     *             the malformed URL exception
696
     * @throws SAXException
697
     *             the SAX exception
698
     */
699
    private void extractLoginConfiguration(Document document) throws MalformedURLException, SAXException {
700
        NodeList nl = document.getElementsByTagName("login-config");
1✔
701
        if (nl.getLength() == 1) {
1✔
702
            final Element loginConfigElement = (Element) nl.item(0);
1✔
703
            String authenticationMethod = XMLUtils.getChildNodeValue(loginConfigElement, "auth-method", "BASIC");
1✔
704
            _authenticationRealm = XMLUtils.getChildNodeValue(loginConfigElement, "realm-name", "");
1✔
705
            if (authenticationMethod.equalsIgnoreCase("BASIC")) {
1✔
706
                _useBasicAuthentication = true;
1✔
707
                if (_authenticationRealm.isEmpty()) {
1!
708
                    throw new SAXException("No realm specified for BASIC Authorization");
×
709
                }
710
            } else if (authenticationMethod.equalsIgnoreCase("FORM")) {
1!
711
                _useFormAuthentication = true;
1✔
712
                if (_authenticationRealm.isEmpty()) {
1!
713
                    throw new SAXException("No realm specified for FORM Authorization");
×
714
                }
715
                _loginURL = new URL("http", "localhost",
1✔
716
                        _contextPath + XMLUtils.getChildNodeValue(loginConfigElement, "form-login-page"));
1✔
717
                _errorURL = new URL("http", "localhost",
1✔
718
                        _contextPath + XMLUtils.getChildNodeValue(loginConfigElement, "form-error-page"));
1✔
719
            }
720
        }
721
    }
1✔
722

723
    /**
724
     * Register servlets.
725
     *
726
     * @param document
727
     *            the document
728
     *
729
     * @throws SAXException
730
     *             the SAX exception
731
     */
732
    private void registerServlets(Document document) throws SAXException {
733
        Hashtable nameToClass = new Hashtable<>();
1✔
734
        NodeList nl = document.getElementsByTagName("servlet");
1✔
735
        for (int i = 0; i < nl.getLength(); i++) {
1✔
736
            registerServletClass(nameToClass, (Element) nl.item(i));
1✔
737
        }
738
        nl = document.getElementsByTagName("servlet-mapping");
1✔
739
        for (int i = 0; i < nl.getLength(); i++) {
1✔
740
            registerServlet(nameToClass, (Element) nl.item(i));
1✔
741
        }
742
        this._servlets = nameToClass;
1✔
743
    }
1✔
744

745
    /**
746
     * Register servlet class.
747
     *
748
     * @param mapping
749
     *            the mapping
750
     * @param servletElement
751
     *            the servlet element
752
     *
753
     * @throws SAXException
754
     *             the SAX exception
755
     */
756
    private void registerServletClass(Dictionary mapping, Element servletElement) throws SAXException {
757
        mapping.put(XMLUtils.getChildNodeValue(servletElement, "servlet-name"),
1✔
758
                new ServletConfiguration(servletElement));
759
    }
1✔
760

761
    /**
762
     * Register servlet.
763
     *
764
     * @param mapping
765
     *            the mapping
766
     * @param servletElement
767
     *            the servlet element
768
     *
769
     * @throws SAXException
770
     *             the SAX exception
771
     */
772
    private void registerServlet(Dictionary mapping, Element servletElement) throws SAXException {
773
        registerServlet(XMLUtils.getChildNodeValue(servletElement, "url-pattern"),
1✔
774
                (ServletConfiguration) mapping.get(XMLUtils.getChildNodeValue(servletElement, "servlet-name")));
1✔
775
    }
1✔
776

777
    /**
778
     * Extract context parameters.
779
     *
780
     * @param document
781
     *            the document
782
     *
783
     * @throws SAXException
784
     *             the SAX exception
785
     */
786
    private void extractContextParameters(Document document) throws SAXException {
787
        NodeList nl = document.getElementsByTagName("context-param");
1✔
788
        for (int i = 0; i < nl.getLength(); i++) {
1✔
789
            Element param = (Element) nl.item(i);
1✔
790
            String name = XMLUtils.getChildNodeValue(param, "param-name");
1✔
791
            String value = XMLUtils.getChildNodeValue(param, "param-value");
1✔
792
            _contextParameters.put(name, value);
1✔
793
        }
794
    }
1✔
795

796
    /**
797
     * Pattern matches.
798
     *
799
     * @param urlPattern
800
     *            the url pattern
801
     * @param urlPath
802
     *            the url path
803
     *
804
     * @return true, if successful
805
     */
806
    private static boolean patternMatches(String urlPattern, String urlPath) {
807
        return urlPattern.equals(urlPath);
1✔
808
    }
809

810
    /**
811
     * Gets the display name.
812
     *
813
     * @return the display name
814
     */
815
    String getDisplayName() {
816
        return _displayName;
1✔
817
    }
818

819
    // ============================================= SecurityCheckServlet class
820
    // =============================================
821

822
    /**
823
     * The Class SecurityCheckServlet.
824
     */
825
    static class SecurityCheckServlet extends HttpServlet {
1✔
826

827
        /** The Constant serialVersionUID. */
828
        private static final long serialVersionUID = 1L;
829

830
        @Override
831
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
832
            handleLogin(req, resp);
×
833
        }
×
834

835
        @Override
836
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
837
            handleLogin(req, resp);
1✔
838
        }
1✔
839

840
        /**
841
         * Handle login.
842
         *
843
         * @param req
844
         *            the req
845
         * @param resp
846
         *            the resp
847
         *
848
         * @throws IOException
849
         *             Signals that an I/O exception has occurred.
850
         */
851
        private void handleLogin(HttpServletRequest req, HttpServletResponse resp) throws IOException {
852
            final String username = req.getParameter("j_username");
1✔
853
            final String roleList = req.getParameter("j_password");
1✔
854
            getServletSession(req).setUserInformation(username, ServletUnitHttpRequest.toArray(roleList));
1✔
855
            resp.sendRedirect(getServletSession(req).getOriginalURL().toExternalForm());
1✔
856
        }
1✔
857

858
        /**
859
         * Gets the servlet session.
860
         *
861
         * @param req
862
         *            the req
863
         *
864
         * @return the servlet session
865
         */
866
        private ServletUnitHttpSession getServletSession(HttpServletRequest req) {
867
            return (ServletUnitHttpSession) req.getSession();
1✔
868
        }
869

870
    }
871

872
    // ============================================= ServletConfiguration class
873
    // =============================================
874

875
    /** The Constant DONT_AUTOLOAD. */
876
    static final int DONT_AUTOLOAD = Integer.MIN_VALUE;
877

878
    /** The Constant ANY_LOAD_ORDER. */
879
    static final int ANY_LOAD_ORDER = Integer.MAX_VALUE;
880

881
    /**
882
     * The Class ServletConfiguration.
883
     */
884
    class ServletConfiguration extends WebResourceConfiguration {
885

886
        /** The servlet. */
887
        private Servlet _servlet;
888

889
        /** The servlet name. */
890
        private String _servletName;
891

892
        /** The jsp file. */
893
        private String _jspFile;
894

895
        /** The load order. */
896
        private int _loadOrder = DONT_AUTOLOAD;
1✔
897

898
        /**
899
         * Instantiates a new servlet configuration.
900
         *
901
         * @param className
902
         *            the class name
903
         */
904
        ServletConfiguration(String className) {
1✔
905
            super(className);
1✔
906
        }
1✔
907

908
        /**
909
         * Instantiates a new servlet configuration.
910
         *
911
         * @param className
912
         *            the class name
913
         * @param initParams
914
         *            the init params
915
         */
916
        ServletConfiguration(String className, Hashtable initParams) {
1✔
917
            super(className, initParams);
1✔
918
        }
1✔
919

920
        /**
921
         * Instantiates a new servlet configuration.
922
         *
923
         * @param servletElement
924
         *            the servlet element
925
         *
926
         * @throws SAXException
927
         *             the SAX exception
928
         */
929
        ServletConfiguration(Element servletElement) throws SAXException {
1✔
930
            super(servletElement, "servlet-class", XMLUtils.getChildNodeValue(servletElement, "servlet-class",
1✔
931
                    "org.apache.jasper.servlet.JspServlet"));
932
            _servletName = XMLUtils.getChildNodeValue(servletElement, "servlet-name");
1✔
933
            _jspFile = XMLUtils.getChildNodeValue(servletElement, "jsp-file", "");
1✔
934
            if ("".equals(_jspFile)) {
1!
935
                _jspFile = null;
1✔
936
            }
937
            final NodeList loadOrder = servletElement.getElementsByTagName("load-on-startup");
1✔
938
            for (int i = 0; i < loadOrder.getLength(); i++) {
1✔
939
                String order = XMLUtils.getTextValue(loadOrder.item(i));
1✔
940
                try {
941
                    _loadOrder = Integer.parseInt(order);
1✔
942
                } catch (NumberFormatException e) {
1✔
943
                    _loadOrder = ANY_LOAD_ORDER;
1✔
944
                }
1✔
945
            }
946
        }
1✔
947

948
        /**
949
         * Gets the servlet.
950
         *
951
         * @return the servlet
952
         *
953
         * @throws ClassNotFoundException
954
         *             the class not found exception
955
         * @throws InstantiationException
956
         *             the instantiation exception
957
         * @throws IllegalAccessException
958
         *             the illegal access exception
959
         * @throws ServletException
960
         *             the servlet exception
961
         * @throws IllegalArgumentException
962
         *             the illegal argument exception
963
         * @throws InvocationTargetException
964
         *             the invocation target exception
965
         * @throws NoSuchMethodException
966
         *             the no such method exception
967
         * @throws SecurityException
968
         *             the security exception
969
         */
970
        synchronized Servlet getServlet()
971
                throws ClassNotFoundException, InstantiationException, IllegalAccessException, ServletException,
972
                IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
973
            if (_servlet == null) {
1✔
974
                Class servletClass = Class.forName(getClassName());
1✔
975
                _servlet = (Servlet) servletClass.getDeclaredConstructor().newInstance();
1✔
976
                String servletName = _servletName != null ? _servletName : _servlet.getClass().getName();
1✔
977
                _servlet.init(new ServletUnitServletConfig(servletName, WebApplication.this, getInitParams()));
1✔
978
            }
979

980
            return _servlet;
1✔
981
        }
982

983
        @Override
984
        synchronized void destroyResource() {
985
            if (_servlet != null) {
1✔
986
                _servlet.destroy();
1✔
987
            }
988
        }
1✔
989

990
        /**
991
         * Gets the servlet name.
992
         *
993
         * @return the servlet name
994
         */
995
        String getServletName() {
996
            return _servletName;
1✔
997
        }
998

999
        @Override
1000
        boolean isLoadOnStartup() {
1001
            return _loadOrder != DONT_AUTOLOAD;
1✔
1002
        }
1003

1004
        /**
1005
         * Gets the load order.
1006
         *
1007
         * @return the load order
1008
         */
1009
        public int getLoadOrder() {
1010
            return _loadOrder;
1✔
1011
        }
1012

1013
        /**
1014
         * Gets the jsp file.
1015
         *
1016
         * @return the jsp file
1017
         */
1018
        public Object getJspFile() {
1019
            return this._jspFile;
×
1020
        }
1021
    }
1022

1023
    // ============================================= FilterConfiguration class
1024
    // =============================================
1025

1026
    /**
1027
     * The Class FilterConfiguration.
1028
     */
1029
    class FilterConfiguration extends WebResourceConfiguration implements FilterMetaData {
1030

1031
        /** The filter. */
1032
        private Filter _filter;
1033

1034
        /** The name. */
1035
        private String _name;
1036

1037
        /**
1038
         * Instantiates a new filter configuration.
1039
         *
1040
         * @param name
1041
         *            the name
1042
         * @param filterElement
1043
         *            the filter element
1044
         *
1045
         * @throws SAXException
1046
         *             the SAX exception
1047
         */
1048
        FilterConfiguration(String name, Element filterElement) throws SAXException {
1✔
1049
            super(filterElement, "filter-class");
1✔
1050
            _name = name;
1✔
1051
        }
1✔
1052

1053
        @Override
1054
        public synchronized Filter getFilter() throws ServletException {
1055
            try {
1056
                if (_filter == null) {
1✔
1057
                    Class filterClass = Class.forName(getClassName());
1✔
1058
                    _filter = (Filter) filterClass.getDeclaredConstructor().newInstance();
1✔
1059
                    _filter.init(new FilterConfigImpl(_name, getServletContext(), getInitParams()));
1✔
1060
                }
1061

1062
                return _filter;
1✔
1063
            } catch (ClassNotFoundException e) {
×
1064
                throw new ServletException("Did not find filter class: " + getClassName());
×
1065
            } catch (IllegalAccessException e) {
×
1066
                throw new ServletException("Filter class " + getClassName() + " lacks a public no-arg constructor");
×
1067
            } catch (InstantiationException | IllegalArgumentException | InvocationTargetException
×
1068
                    | NoSuchMethodException | SecurityException e) {
1069
                throw new ServletException("Filter class " + getClassName() + " could not be instantiated.");
×
1070
            } catch (ClassCastException e) {
×
1071
                throw new ServletException(
×
1072
                        "Filter class " + getClassName() + " does not implement" + Filter.class.getName());
×
1073
            }
1074
        }
1075

1076
        @Override
1077
        boolean isLoadOnStartup() {
1078
            return false;
×
1079
        }
1080

1081
        @Override
1082
        synchronized void destroyResource() {
1083
            if (_filter != null) {
×
1084
                _filter.destroy();
×
1085
            }
1086
        }
×
1087
    }
1088

1089
    // =================================== SecurityConstract interface and implementations
1090
    // ==================================
1091

1092
    /**
1093
     * The Interface SecurityConstraint.
1094
     */
1095
    interface SecurityConstraint {
1096

1097
        /**
1098
         * Controls path.
1099
         *
1100
         * @param urlPath
1101
         *            the url path
1102
         *
1103
         * @return true, if successful
1104
         */
1105
        boolean controlsPath(String urlPath);
1106

1107
        /**
1108
         * Gets the permitted roles.
1109
         *
1110
         * @return the permitted roles
1111
         */
1112
        String[] getPermittedRoles();
1113
    }
1114

1115
    /**
1116
     * The Class NullSecurityConstraint.
1117
     */
1118
    static class NullSecurityConstraint implements SecurityConstraint {
1✔
1119

1120
        /** The Constant NO_ROLES. */
1121
        private static final String[] NO_ROLES = {};
1✔
1122

1123
        @Override
1124
        public boolean controlsPath(String urlPath) {
1125
            return false;
×
1126
        }
1127

1128
        @Override
1129
        public String[] getPermittedRoles() {
1130
            return NO_ROLES;
×
1131
        }
1132
    }
1133

1134
    /**
1135
     * The Class SecurityConstraintImpl.
1136
     */
1137
    static class SecurityConstraintImpl implements SecurityConstraint {
1138

1139
        /**
1140
         * Instantiates a new security constraint impl.
1141
         *
1142
         * @param root
1143
         *            the root
1144
         *
1145
         * @throws SAXException
1146
         *             the SAX exception
1147
         */
1148
        SecurityConstraintImpl(Element root) throws SAXException {
1✔
1149
            final NodeList roleNames = root.getElementsByTagName("role-name");
1✔
1150
            for (int i = 0; i < roleNames.getLength(); i++) {
1✔
1151
                _roleList.add(XMLUtils.getTextValue(roleNames.item(i)));
1✔
1152
            }
1153

1154
            final NodeList resources = root.getElementsByTagName("web-resource-collection");
1✔
1155
            for (int i = 0; i < resources.getLength(); i++) {
1✔
1156
                _resources.add(new WebResourceCollection((Element) resources.item(i)));
1✔
1157
            }
1158
        }
1✔
1159

1160
        @Override
1161
        public boolean controlsPath(String urlPath) {
1162
            return getMatchingCollection(urlPath) != null;
1✔
1163
        }
1164

1165
        @Override
1166
        public String[] getPermittedRoles() {
1167
            if (_roles == null) {
1✔
1168
                _roles = _roleList.toArray(new String[_roleList.size()]);
1✔
1169
            }
1170
            return _roles;
1✔
1171
        }
1172

1173
        /** The roles. */
1174
        private String[] _roles;
1175

1176
        /** The role list. */
1177
        private List<String> _roleList = new ArrayList<>();
1✔
1178

1179
        /** The resources. */
1180
        private List<WebResourceCollection> _resources = new ArrayList<>();
1✔
1181

1182
        /**
1183
         * Gets the matching collection.
1184
         *
1185
         * @param urlPath
1186
         *            the url path
1187
         *
1188
         * @return the matching collection
1189
         */
1190
        public WebResourceCollection getMatchingCollection(String urlPath) {
1191
            for (WebResourceCollection wrc : _resources) {
1✔
1192
                if (wrc.controlsPath(urlPath)) {
1✔
1193
                    return wrc;
1✔
1194
                }
1195
            }
1✔
1196
            return null;
1✔
1197
        }
1198

1199
        /**
1200
         * The Class WebResourceCollection.
1201
         */
1202
        class WebResourceCollection {
1203

1204
            /**
1205
             * Instantiates a new web resource collection.
1206
             *
1207
             * @param root
1208
             *            the root
1209
             *
1210
             * @throws SAXException
1211
             *             the SAX exception
1212
             */
1213
            WebResourceCollection(Element root) throws SAXException {
1✔
1214
                final NodeList urlPatterns = root.getElementsByTagName("url-pattern");
1✔
1215
                for (int i = 0; i < urlPatterns.getLength(); i++) {
1✔
1216
                    _urlPatterns.add(XMLUtils.getTextValue(urlPatterns.item(i)));
1✔
1217
                }
1218
            }
1✔
1219

1220
            /**
1221
             * Controls path.
1222
             *
1223
             * @param urlPath
1224
             *            the url path
1225
             *
1226
             * @return true, if successful
1227
             */
1228
            boolean controlsPath(String urlPath) {
1229
                for (String pattern : _urlPatterns) {
1✔
1230
                    if (patternMatches(pattern, urlPath)) {
1✔
1231
                        return true;
1✔
1232
                    }
1233
                }
1✔
1234
                return false;
1✔
1235
            }
1236

1237
            /** The url patterns. */
1238
            private List<String> _urlPatterns = new ArrayList<>();
1✔
1239
        }
1240
    }
1241

1242
    /** The Constant NO_FILTERS. */
1243
    static final FilterMetaData[] NO_FILTERS = {};
1✔
1244

1245
    /**
1246
     * The Class ServletRequestImpl.
1247
     */
1248
    static class ServletRequestImpl implements ServletMetaData {
1249

1250
        /** The url. */
1251
        private URL _url;
1252

1253
        /** The full servlet path. */
1254
        private String _fullServletPath;
1255

1256
        /** The mapping. */
1257
        private WebResourceMapping _mapping;
1258

1259
        /** The filters per name. */
1260
        private Hashtable _filtersPerName;
1261

1262
        /** The filters per url. */
1263
        private FilterUrlMap _filtersPerUrl;
1264

1265
        /**
1266
         * Instantiates a new servlet request impl.
1267
         *
1268
         * @param url
1269
         *            the url
1270
         * @param servletPath
1271
         *            the servlet path
1272
         * @param mapping
1273
         *            the mapping
1274
         * @param filtersPerName
1275
         *            the filters per name
1276
         * @param filtersPerUrl
1277
         *            the filters per url
1278
         */
1279
        ServletRequestImpl(URL url, String servletPath, WebResourceMapping mapping, Hashtable filtersPerName,
1280
                FilterUrlMap filtersPerUrl) {
1✔
1281
            _url = url;
1✔
1282
            _fullServletPath = servletPath;
1✔
1283
            _mapping = mapping;
1✔
1284
            _filtersPerName = filtersPerName;
1✔
1285
            _filtersPerUrl = filtersPerUrl;
1✔
1286
        }
1✔
1287

1288
        /**
1289
         * get the Servlet
1290
         *
1291
         * @return the Servlet from the configuration
1292
         *
1293
         * @throws ServletException
1294
         *             - e.g. if no configuration is available
1295
         */
1296
        @Override
1297
        public Servlet getServlet() throws ServletException {
1298
            if (getConfiguration() == null) {
1✔
1299
                throw new HttpNotFoundException("No servlet mapping defined", _url);
1✔
1300
            }
1301

1302
            try {
1303
                return getConfiguration().getServlet();
1✔
1304
            } catch (ClassNotFoundException e) {
×
1305
                throw new HttpNotFoundException(_url, e);
×
1306
            } catch (IllegalAccessException | InstantiationException | IllegalArgumentException
×
1307
                    | InvocationTargetException | NoSuchMethodException | SecurityException e) {
1308
                throw new HttpInternalErrorException(_url, e);
×
1309
            }
1310
        }
1311

1312
        /**
1313
         * get the ServletPath the decoded ServletPath
1314
         */
1315
        @Override
1316
        public String getServletPath() {
1317
            return _mapping == null ? null : HttpUnitUtils.decode(_mapping.getServletPath(_fullServletPath));
1!
1318
        }
1319

1320
        /**
1321
         * get the Path Information
1322
         *
1323
         * @return the decode path
1324
         */
1325
        @Override
1326
        public String getPathInfo() {
1327
            return _mapping == null ? null : HttpUnitUtils.decode(_mapping.getPathInfo(_fullServletPath));
1!
1328
        }
1329

1330
        @Override
1331
        public FilterMetaData[] getFilters() {
1332
            if (getConfiguration() == null) {
1✔
1333
                return NO_FILTERS;
1✔
1334
            }
1335

1336
            List<FilterMetaData> filters = new ArrayList<>();
1✔
1337
            addFiltersForPath(filters, _fullServletPath);
1✔
1338
            addFiltersForServletWithName(filters, getConfiguration().getServletName());
1✔
1339

1340
            return filters.toArray(new FilterMetaData[filters.size()]);
1✔
1341
        }
1342

1343
        /**
1344
         * Adds the filters for path.
1345
         *
1346
         * @param filters
1347
         *            the filters
1348
         * @param fullServletPath
1349
         *            the full servlet path
1350
         */
1351
        private void addFiltersForPath(List<FilterMetaData> filters, String fullServletPath) {
1352
            FilterMetaData[] matches = _filtersPerUrl.getMatchingFilters(fullServletPath);
1✔
1353
            Collections.addAll(filters, matches);
1✔
1354
        }
1✔
1355

1356
        /**
1357
         * Adds the filters for servlet with name.
1358
         *
1359
         * @param filters
1360
         *            the filters
1361
         * @param servletName
1362
         *            the servlet name
1363
         */
1364
        private void addFiltersForServletWithName(List<FilterMetaData> filters, String servletName) {
1365
            if (servletName == null) {
1✔
1366
                return;
1✔
1367
            }
1368
            List<FilterMetaData> matches = (List<FilterMetaData>) _filtersPerName.get(servletName);
1✔
1369
            if (matches != null) {
1✔
1370
                filters.addAll(matches);
1✔
1371
            }
1372
        }
1✔
1373

1374
        /**
1375
         * Gets the configuration.
1376
         *
1377
         * @return the configuration
1378
         */
1379
        private ServletConfiguration getConfiguration() {
1380
            return _mapping == null ? null : (ServletConfiguration) _mapping.getConfiguration();
1✔
1381
        }
1382
    }
1383

1384
    /**
1385
     * mapping for WebResources.
1386
     */
1387
    static class WebResourceMapping {
1388

1389
        /** The configuration. */
1390
        private WebResourceConfiguration _configuration;
1391

1392
        /**
1393
         * Gets the configuration.
1394
         *
1395
         * @return the configuration
1396
         */
1397
        WebResourceConfiguration getConfiguration() {
1398
            return _configuration;
1✔
1399
        }
1400

1401
        /**
1402
         * Instantiates a new web resource mapping.
1403
         *
1404
         * @param configuration
1405
         *            the configuration
1406
         */
1407
        WebResourceMapping(WebResourceConfiguration configuration) {
1✔
1408
            _configuration = configuration;
1✔
1409
        }
1✔
1410

1411
        /**
1412
         * Returns the portion of the request path which was actually used to select the servlet. This default
1413
         * implementation returns the full specified path.
1414
         *
1415
         * @param requestPath
1416
         *            the full path of the request, relative to the application root.
1417
         *
1418
         * @return the servlet path
1419
         */
1420
        String getServletPath(String requestPath) {
1421
            return requestPath;
1✔
1422
        }
1423

1424
        /**
1425
         * Returns the portion of the request path which was not used to select the servlet, and can be used as data by
1426
         * the servlet. This default implementation returns null.
1427
         *
1428
         * @param requestPath
1429
         *            the full path of the request, relative to the application root.
1430
         *
1431
         * @return the path info
1432
         */
1433
        String getPathInfo(String requestPath) {
1434
            return null;
1✔
1435
        }
1436

1437
        /**
1438
         * Destroy resource.
1439
         */
1440
        public void destroyResource() {
1441
            getConfiguration().destroyResource();
1✔
1442
        }
1✔
1443
    }
1444

1445
    /**
1446
     * The Class PartialMatchWebResourceMapping.
1447
     */
1448
    static class PartialMatchWebResourceMapping extends WebResourceMapping {
1449

1450
        /** The prefix. */
1451
        private String _prefix;
1452

1453
        /**
1454
         * Instantiates a new partial match web resource mapping.
1455
         *
1456
         * @param configuration
1457
         *            the configuration
1458
         * @param prefix
1459
         *            the prefix
1460
         */
1461
        public PartialMatchWebResourceMapping(WebResourceConfiguration configuration, String prefix) {
1462
            super(configuration);
1✔
1463
            if (!prefix.endsWith("/*")) {
1!
1464
                throw new IllegalArgumentException(prefix + " does not end with '/*'");
×
1465
            }
1466
            _prefix = prefix.substring(0, prefix.length() - 2);
1✔
1467
        }
1✔
1468

1469
        @Override
1470
        String getServletPath(String requestPath) {
1471
            return _prefix;
1✔
1472
        }
1473

1474
        @Override
1475
        String getPathInfo(String requestPath) {
1476
            return requestPath.length() > _prefix.length() ? requestPath.substring(_prefix.length()) : null;
1✔
1477
        }
1478
    }
1479

1480
    /**
1481
     * A utility class for mapping web resources to url patterns. This implements the matching algorithm documented in
1482
     * section 10 of the JSDK-2.2 reference.
1483
     */
1484
    class WebResourceMap {
1✔
1485

1486
        /** The exact matches. */
1487
        private final Map _exactMatches = new HashMap<>();
1✔
1488

1489
        /** The extensions. */
1490
        private final Map _extensions = new HashMap<>();
1✔
1491

1492
        /** The url tree. */
1493
        private final Map _urlTree = new HashMap<>();
1✔
1494

1495
        /** The default mapping. */
1496
        private WebResourceMapping _defaultMapping;
1497

1498
        /**
1499
         * Put.
1500
         *
1501
         * @param mapping
1502
         *            the mapping
1503
         * @param configuration
1504
         *            the configuration
1505
         */
1506
        void put(String mapping, WebResourceConfiguration configuration) {
1507
            if (mapping.equals("/")) {
1✔
1508
                _defaultMapping = new WebResourceMapping(configuration);
1✔
1509
            } else if (mapping.startsWith("*.")) {
1✔
1510
                _extensions.put(mapping.substring(2), new WebResourceMapping(configuration));
1✔
1511
            } else if (!mapping.startsWith("/") || !mapping.endsWith("/*")) {
1!
1512
                _exactMatches.put(mapping, new WebResourceMapping(configuration));
1✔
1513
            } else {
1514
                ParsedPath path = new ParsedPath(mapping);
1✔
1515
                Map context = _urlTree;
1✔
1516
                while (path.hasNext()) {
1!
1517
                    String part = path.next();
1✔
1518
                    if (part.equals("*")) {
1✔
1519
                        context.put("*", new PartialMatchWebResourceMapping(configuration, mapping));
1✔
1520
                        return;
1✔
1521
                    }
1522
                    if (!context.containsKey(part)) {
1!
1523
                        context.put(part, new HashMap<>());
1✔
1524
                    }
1525
                    context = (Map) context.get(part);
1✔
1526
                }
1✔
1527
            }
1528
        }
1✔
1529

1530
        /**
1531
         * Gets the.
1532
         *
1533
         * @param url
1534
         *            the url
1535
         *
1536
         * @return the servlet meta data
1537
         */
1538
        ServletMetaData get(URL url) {
1539
            String file = url.getFile();
1✔
1540
            if (!file.startsWith(_contextPath)) {
1✔
1541
                throw new HttpNotFoundException("File path does not begin with '" + _contextPath + "'", url);
1✔
1542
            }
1543

1544
            String servletPath = getServletPath(file.substring(_contextPath.length()));
1✔
1545

1546
            if (servletPath.endsWith("j_security_check")) {
1✔
1547
                return new ServletRequestImpl(url, servletPath, SECURITY_CHECK_MAPPING, _filterMapping,
1✔
1548
                        _filterUrlMapping);
1549
            }
1550
            return new ServletRequestImpl(url, servletPath, getMapping(servletPath), _filterMapping, _filterUrlMapping);
1✔
1551
        }
1552

1553
        /**
1554
         * Gets the servlet path.
1555
         *
1556
         * @param urlFile
1557
         *            the url file
1558
         *
1559
         * @return the servlet path
1560
         */
1561
        private String getServletPath(String urlFile) {
1562
            if (urlFile.indexOf('?') < 0) {
1✔
1563
                return urlFile;
1✔
1564
            }
1565
            return urlFile.substring(0, urlFile.indexOf('?'));
1✔
1566
        }
1567

1568
        /**
1569
         * Destroy web resources.
1570
         */
1571
        public void destroyWebResources() {
1572
            if (_defaultMapping != null) {
1!
1573
                _defaultMapping.destroyResource();
×
1574
            }
1575
            destroyWebResources(_exactMatches);
1✔
1576
            destroyWebResources(_extensions);
1✔
1577
            destroyWebResources(_urlTree);
1✔
1578
        }
1✔
1579

1580
        /**
1581
         * Destroy web resources.
1582
         *
1583
         * @param map
1584
         *            the map
1585
         */
1586
        private void destroyWebResources(Map map) {
1587
            for (Object o : map.values()) {
1✔
1588
                if (o instanceof WebResourceMapping) {
1!
1589
                    WebResourceMapping webResourceMapping = (WebResourceMapping) o;
1✔
1590
                    webResourceMapping.destroyResource();
1✔
1591
                } else {
1✔
1592
                    destroyWebResources((Map) o);
×
1593
                }
1594
            }
1✔
1595
        }
1✔
1596

1597
        /**
1598
         * Auto load servlets.
1599
         */
1600
        void autoLoadServlets() {
1601
            ArrayList autoLoadable = new ArrayList<>();
1✔
1602
            if (_defaultMapping != null && _defaultMapping.getConfiguration().isLoadOnStartup()) {
1!
1603
                autoLoadable.add(_defaultMapping.getConfiguration());
×
1604
            }
1605
            collectAutoLoadableServlets(autoLoadable, _exactMatches);
1✔
1606
            collectAutoLoadableServlets(autoLoadable, _extensions);
1✔
1607
            collectAutoLoadableServlets(autoLoadable, _urlTree);
1✔
1608
            if (autoLoadable.isEmpty()) {
1✔
1609
                return;
1✔
1610
            }
1611

1612
            Collections.sort(autoLoadable, (o1, o2) -> {
1✔
1613
                ServletConfiguration sc1 = (ServletConfiguration) o1;
1✔
1614
                ServletConfiguration sc2 = (ServletConfiguration) o2;
1✔
1615
                return sc1.getLoadOrder() <= sc2.getLoadOrder() ? -1 : +1;
1✔
1616
            });
1617
            for (Iterator iterator = autoLoadable.iterator(); iterator.hasNext();) {
1✔
1618
                ServletConfiguration servletConfiguration = (ServletConfiguration) iterator.next();
1✔
1619
                try {
1620
                    servletConfiguration.getServlet();
1✔
1621
                } catch (Exception e) {
×
1622
                    HttpUnitUtils.handleException(e);
×
1623
                    throw new RuntimeException(
×
1624
                            "Unable to autoload servlet: " + servletConfiguration.getClassName() + ": " + e);
×
1625
                }
1✔
1626
            }
1✔
1627
        }
1✔
1628

1629
        /**
1630
         * Collect auto loadable servlets.
1631
         *
1632
         * @param collection
1633
         *            the collection
1634
         * @param map
1635
         *            the map
1636
         */
1637
        private void collectAutoLoadableServlets(Collection collection, Map map) {
1638
            for (Object o : map.values()) {
1✔
1639
                if (o instanceof WebResourceMapping) {
1✔
1640
                    WebResourceMapping servletMapping = (WebResourceMapping) o;
1✔
1641
                    if (servletMapping.getConfiguration().isLoadOnStartup()) {
1✔
1642
                        collection.add(servletMapping.getConfiguration());
1✔
1643
                    }
1644
                } else {
1✔
1645
                    collectAutoLoadableServlets(collection, (Map) o);
1✔
1646
                }
1647
            }
1✔
1648
        }
1✔
1649

1650
        /**
1651
         * Gets the mapping.
1652
         *
1653
         * @param url
1654
         *            the url
1655
         *
1656
         * @return the mapping
1657
         */
1658
        private WebResourceMapping getMapping(String url) {
1659
            if (_exactMatches.containsKey(url)) {
1✔
1660
                return (WebResourceMapping) _exactMatches.get(url);
1✔
1661
            }
1662

1663
            Map context = getContextForLongestPathPrefix(url);
1✔
1664
            if (context.containsKey("*")) {
1✔
1665
                return (WebResourceMapping) context.get("*");
1✔
1666
            }
1667

1668
            if (_extensions.containsKey(getExtension(url))) {
1✔
1669
                return (WebResourceMapping) _extensions.get(getExtension(url));
1✔
1670
            }
1671

1672
            if (_urlTree.containsKey("/")) {
1!
1673
                return (WebResourceMapping) _urlTree.get("/");
×
1674
            }
1675

1676
            if (_defaultMapping != null) {
1✔
1677
                return _defaultMapping;
1✔
1678
            }
1679

1680
            final String prefix = "/servlet/";
1✔
1681
            if (!url.startsWith(prefix)) {
1✔
1682
                return null;
1✔
1683
            }
1684

1685
            String className = url.substring(prefix.length());
1✔
1686
            try {
1687
                Class.forName(className);
1✔
1688
                return new WebResourceMapping(new ServletConfiguration(className));
1✔
1689
            } catch (ClassNotFoundException e) {
×
1690
                return null;
×
1691
            }
1692
        }
1693

1694
        /**
1695
         * Gets the context for longest path prefix.
1696
         *
1697
         * @param url
1698
         *            the url
1699
         *
1700
         * @return the context for longest path prefix
1701
         */
1702
        private Map getContextForLongestPathPrefix(String url) {
1703
            Map context = _urlTree;
1✔
1704

1705
            ParsedPath path = new ParsedPath(url);
1✔
1706
            while (path.hasNext()) {
1✔
1707
                String part = path.next();
1✔
1708
                if (!context.containsKey(part)) {
1✔
1709
                    break;
1✔
1710
                }
1711
                context = (Map) context.get(part);
1✔
1712
            }
1✔
1713
            return context;
1✔
1714
        }
1715

1716
        /**
1717
         * Gets the extension.
1718
         *
1719
         * @param url
1720
         *            the url
1721
         *
1722
         * @return the extension
1723
         */
1724
        private String getExtension(String url) {
1725
            int index = url.lastIndexOf('.');
1✔
1726
            if (index == -1 || index >= url.length() - 1) {
1!
1727
                return "";
1✔
1728
            }
1729
            return url.substring(index + 1);
1✔
1730
        }
1731

1732
    }
1733

1734
    /**
1735
     * return the given ServletConfiguration for the given servlet name.
1736
     *
1737
     * @param servletName
1738
     *            the servlet name
1739
     *
1740
     * @return the corresponding ServletConfiguration
1741
     */
1742
    public ServletConfiguration getServletByName(String servletName) {
1743
        return (ServletConfiguration) _servlets.get(servletName);
1✔
1744
    }
1745

1746
}
1747

1748
/**
1749
 * A utility class for parsing URLs into paths
1750
 */
1751
class ParsedPath {
1752

1753
    private final String path;
1754
    private int position = 0;
1✔
1755
    static final char seperator_char = '/';
1756

1757
    /**
1758
     * Creates a new parsed path for the given path value
1759
     *
1760
     * @param path
1761
     *            the path
1762
     */
1763
    ParsedPath(String path) {
1✔
1764
        if (path.charAt(0) != seperator_char) {
1!
1765
            throw new IllegalArgumentException("Illegal path '" + path + "', does not begin with " + seperator_char);
×
1766
        }
1767
        this.path = path;
1✔
1768
    }
1✔
1769

1770
    /**
1771
     * Returns true if there are more parts left, otherwise false
1772
     */
1773
    public final boolean hasNext() {
1774
        return position < path.length();
1✔
1775
    }
1776

1777
    /**
1778
     * Returns the next part in the path
1779
     */
1780
    public final String next() {
1781
        int offset = position + 1;
1✔
1782
        while (offset < path.length() && path.charAt(offset) != seperator_char) {
1✔
1783
            offset++;
1✔
1784
        }
1785
        String result = path.substring(position + 1, offset);
1✔
1786
        position = offset;
1✔
1787
        return result;
1✔
1788
    }
1789

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