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

hazendaz / httpunit / 646

06 Dec 2025 08:11PM UTC coverage: 80.526% (+0.02%) from 80.509%
646

push

github

hazendaz
Cleanup array usage

3213 of 4105 branches covered (78.27%)

Branch coverage included in aggregate %.

4 of 4 new or added lines in 3 files covered. (100.0%)

532 existing lines in 26 files now uncovered.

8245 of 10124 relevant lines covered (81.44%)

0.81 hits per line

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

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

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

26
import jakarta.servlet.*;
27
import jakarta.servlet.http.*;
28

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

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

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

56
    /** The Constant NULL_SECURITY_CONSTRAINT. */
57
    private static final SecurityConstraint NULL_SECURITY_CONSTRAINT = new NullSecurityConstraint();
1✔
58

59
    /** The security check configuration. */
60
    private final ServletConfiguration SECURITY_CHECK_CONFIGURATION = new ServletConfiguration(
1✔
61
            SecurityCheckServlet.class.getName());
1✔
62

63
    /** The security check mapping. */
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
    /** The security constraints. */
82
    private List<SecurityConstraint> _securityConstraints = new ArrayList<>();
1✔
83

84
    /** The context listeners. */
85
    private List<ServletContextListener> _contextListeners = new ArrayList<>();
1✔
86

87
    /** The context attribute listeners. */
88
    private List<ServletContextAttributeListener> _contextAttributeListeners = new ArrayList<>();
1✔
89

90
    /** The session listeners. */
91
    private List<HttpSessionListener> _sessionListeners = new ArrayList<>();
1✔
92

93
    /** The session attribute listeners. */
94
    private List<HttpSessionAttributeListener> _sessionAttributeListeners = new ArrayList<>();
1✔
95

96
    /** The use basic authentication. */
97
    private boolean _useBasicAuthentication;
98

99
    /** The use form authentication. */
100
    private boolean _useFormAuthentication;
101

102
    /** The authentication realm. */
103
    private String _authenticationRealm = "";
1✔
104

105
    /** The login URL. */
106
    private URL _loginURL;
107

108
    /** The error URL. */
109
    private URL _errorURL;
110

111
    /** The context parameters. */
112
    private Hashtable _contextParameters = new Hashtable<>();
1✔
113

114
    /** The context dir. */
115
    private File _contextDir = null;
1✔
116

117
    /** The context path. */
118
    private String _contextPath = null;
1✔
119

120
    /** The servlet context. */
121
    private ServletUnitServletContext _servletContext;
122

123
    /** The display name. */
124
    private String _displayName;
125

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

133
    /**
134
     * Constructs an application spec from an XML document.
135
     *
136
     * @param document
137
     *            the document
138
     *
139
     * @throws MalformedURLException
140
     *             the malformed URL exception
141
     * @throws SAXException
142
     *             the SAX exception
143
     */
144
    WebApplication(Document document) throws MalformedURLException, SAXException {
145
        this(document, null, "");
1✔
146
    }
1✔
147

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

165
    /**
166
     * Constructs an application spec from an XML document.
167
     *
168
     * @param document
169
     *            the document
170
     * @param file
171
     *            the file
172
     * @param contextPath
173
     *            the context path
174
     *
175
     * @throws MalformedURLException
176
     *             the malformed URL exception
177
     * @throws SAXException
178
     *             the SAX exception
179
     */
180
    WebApplication(Document document, File file, String contextPath) throws MalformedURLException, SAXException {
1✔
181
        if (contextPath != null && !contextPath.isEmpty() && !contextPath.startsWith("/")) {
1!
UNCOV
182
            throw new IllegalArgumentException("Context path " + contextPath + " must start with '/'");
×
183
        }
184
        _contextDir = file;
1✔
185
        _contextPath = contextPath == null ? "" : contextPath;
1✔
186
        NodeList nl = document.getElementsByTagName("display-name");
1✔
187
        if (nl.getLength() > 0) {
1✔
188
            _displayName = XMLUtils.getTextValue(nl.item(0)).trim();
1✔
189
        }
190

191
        registerServlets(document);
1✔
192
        registerFilters(document);
1✔
193
        extractSecurityConstraints(document);
1✔
194
        extractContextParameters(document);
1✔
195
        extractLoginConfiguration(document);
1✔
196
        extractListeners(document);
1✔
197
        notifyContextInitialized();
1✔
198
        _servletMapping.autoLoadServlets();
1✔
199
    }
1✔
200

201
    /**
202
     * Extract listeners.
203
     *
204
     * @param document
205
     *            the document
206
     *
207
     * @throws SAXException
208
     *             the SAX exception
209
     */
210
    private void extractListeners(Document document) throws SAXException {
211
        NodeList nl = document.getElementsByTagName("listener");
1✔
212
        for (int i = 0; i < nl.getLength(); i++) {
1✔
213
            String listenerName = XMLUtils.getChildNodeValue((Element) nl.item(i), "listener-class").trim();
1✔
214
            try {
215
                Object listener = Class.forName(listenerName).getDeclaredConstructor().newInstance();
1✔
216

217
                if (listener instanceof ServletContextListener) {
1✔
218
                    _contextListeners.add((ServletContextListener) listener);
1✔
219
                }
220
                if (listener instanceof ServletContextAttributeListener) {
1✔
221
                    _contextAttributeListeners.add((ServletContextAttributeListener) listener);
1✔
222
                }
223
                if (listener instanceof HttpSessionListener) {
1✔
224
                    _sessionListeners.add((HttpSessionListener) listener);
1✔
225
                }
226
                if (listener instanceof HttpSessionAttributeListener) {
1✔
227
                    _sessionAttributeListeners.add((HttpSessionAttributeListener) listener);
1✔
228
                }
229
            } catch (Throwable e) {
×
UNCOV
230
                throw new RuntimeException("Unable to load context listener " + listenerName + ": " + e.toString());
×
231
            }
1✔
232
        }
233
    }
1✔
234

235
    /**
236
     * Notify context initialized.
237
     */
238
    private void notifyContextInitialized() {
239
        ServletContextEvent event = new ServletContextEvent(getServletContext());
1✔
240

241
        for (Iterator<ServletContextListener> i = _contextListeners.iterator(); i.hasNext();) {
1✔
242
            ServletContextListener listener = i.next();
1✔
243
            listener.contextInitialized(event);
1✔
244
        }
1✔
245
    }
1✔
246

247
    /**
248
     * Shut down.
249
     */
250
    void shutDown() {
251
        destroyServlets();
1✔
252
        notifyContextDestroyed();
1✔
253
    }
1✔
254

255
    /**
256
     * Notify context destroyed.
257
     */
258
    private void notifyContextDestroyed() {
259
        ServletContextEvent event = new ServletContextEvent(getServletContext());
1✔
260

261
        for (ListIterator<ServletContextListener> i = _contextListeners.listIterator(_contextListeners.size()); i
1✔
262
                .hasPrevious();) {
1✔
263
            ServletContextListener listener = i.previous();
1✔
264
            listener.contextDestroyed(event);
1✔
265
        }
1✔
266
    }
1✔
267

268
    /**
269
     * Send attribute added.
270
     *
271
     * @param name
272
     *            the name
273
     * @param value
274
     *            the value
275
     */
276
    void sendAttributeAdded(String name, Object value) {
277
        ServletContextAttributeEvent event = new ServletContextAttributeEvent(getServletContext(), name, value);
1✔
278

279
        for (Iterator<ServletContextAttributeListener> i = _contextAttributeListeners.iterator(); i.hasNext();) {
1✔
280
            ServletContextAttributeListener listener = i.next();
1✔
281
            listener.attributeAdded(event);
1✔
282
        }
1✔
283
    }
1✔
284

285
    /**
286
     * Send attribute replaced.
287
     *
288
     * @param name
289
     *            the name
290
     * @param value
291
     *            the value
292
     */
293
    void sendAttributeReplaced(String name, Object value) {
294
        ServletContextAttributeEvent event = new ServletContextAttributeEvent(getServletContext(), name, value);
1✔
295

296
        for (Iterator<ServletContextAttributeListener> i = _contextAttributeListeners.iterator(); i.hasNext();) {
1✔
297
            ServletContextAttributeListener listener = i.next();
1✔
298
            listener.attributeReplaced(event);
1✔
299
        }
1✔
300
    }
1✔
301

302
    /**
303
     * Send attribute removed.
304
     *
305
     * @param name
306
     *            the name
307
     * @param value
308
     *            the value
309
     */
310
    void sendAttributeRemoved(String name, Object value) {
311
        ServletContextAttributeEvent event = new ServletContextAttributeEvent(getServletContext(), name, value);
1✔
312

313
        for (Iterator<ServletContextAttributeListener> i = _contextAttributeListeners.iterator(); i.hasNext();) {
1✔
314
            ServletContextAttributeListener listener = i.next();
1✔
315
            listener.attributeRemoved(event);
1✔
316
        }
1✔
317
    }
1✔
318

319
    /**
320
     * Extract security constraints.
321
     *
322
     * @param document
323
     *            the document
324
     *
325
     * @throws SAXException
326
     *             the SAX exception
327
     */
328
    private void extractSecurityConstraints(Document document) throws SAXException {
329
        NodeList nl = document.getElementsByTagName("security-constraint");
1✔
330
        for (int i = 0; i < nl.getLength(); i++) {
1✔
331
            _securityConstraints.add(new SecurityConstraintImpl((Element) nl.item(i)));
1✔
332
        }
333
    }
1✔
334

335
    /**
336
     * Gets the context path.
337
     *
338
     * @return the context path
339
     */
340
    String getContextPath() {
341
        return _contextPath;
1✔
342
    }
343

344
    /**
345
     * Gets the servlet context.
346
     *
347
     * @return the servlet context
348
     */
349
    ServletContext getServletContext() {
350
        if (_servletContext == null) {
1✔
351
            _servletContext = new ServletUnitServletContext(this);
1✔
352
        }
353
        return _servletContext;
1✔
354
    }
355

356
    /**
357
     * Registers a servlet class to be run.
358
     *
359
     * @param resourceName
360
     *            the resource name
361
     * @param servletClassName
362
     *            the servlet class name
363
     * @param initParams
364
     *            the init params
365
     */
366
    void registerServlet(String resourceName, String servletClassName, Hashtable initParams) {
367
        registerServlet(resourceName, new ServletConfiguration(servletClassName, initParams));
1✔
368
    }
1✔
369

370
    /**
371
     * Registers a servlet to be run.
372
     *
373
     * @param resourceName
374
     *            the resource name
375
     * @param servletConfiguration
376
     *            the servlet configuration
377
     */
378
    void registerServlet(String resourceName, ServletConfiguration servletConfiguration) {
379
        // FIXME - shouldn't everything start with one or the other?
380
        if (!resourceName.startsWith("/") && !resourceName.startsWith("*")) {
1✔
381
            resourceName = "/" + resourceName;
1✔
382
        }
383
        _servletMapping.put(resourceName, servletConfiguration);
1✔
384
    }
1✔
385

386
    /**
387
     * Calls the destroy method for every active servlet.
388
     */
389
    void destroyServlets() {
390
        _servletMapping.destroyWebResources();
1✔
391
    }
1✔
392

393
    /**
394
     * Gets the servlet request.
395
     *
396
     * @param url
397
     *            the url
398
     *
399
     * @return the servlet request
400
     */
401
    ServletMetaData getServletRequest(URL url) {
402
        return _servletMapping.get(url);
1✔
403
    }
404

405
    /**
406
     * Returns true if this application uses Basic Authentication.
407
     *
408
     * @return true, if successful
409
     */
410
    boolean usesBasicAuthentication() {
411
        return _useBasicAuthentication;
1✔
412
    }
413

414
    /**
415
     * Returns true if this application uses form-based authentication.
416
     *
417
     * @return true, if successful
418
     */
419
    boolean usesFormAuthentication() {
420
        return _useFormAuthentication;
1✔
421
    }
422

423
    /**
424
     * Gets the authentication realm.
425
     *
426
     * @return the authentication realm
427
     */
428
    String getAuthenticationRealm() {
429
        return _authenticationRealm;
1✔
430
    }
431

432
    /**
433
     * Gets the login URL.
434
     *
435
     * @return the login URL
436
     */
437
    URL getLoginURL() {
438
        return _loginURL;
1✔
439
    }
440

441
    /**
442
     * Gets the error URL.
443
     *
444
     * @return the error URL
445
     */
446
    URL getErrorURL() {
447
        return _errorURL;
1✔
448
    }
449

450
    /**
451
     * Returns true if the specified path may only be accesses by an authorized user.
452
     *
453
     * @param url
454
     *            the application-relative path of the URL
455
     *
456
     * @return true, if successful
457
     */
458
    boolean requiresAuthorization(URL url) {
459
        String result;
460
        String file = url.getFile();
1✔
461
        if (_contextPath.equals("")) {
1✔
462
            result = file;
1✔
463
        } else if (file.startsWith(_contextPath)) {
1!
464
            result = file.substring(_contextPath.length());
1✔
465
        } else {
UNCOV
466
            result = null;
×
467
        }
468
        return getControllingConstraint(result) != NULL_SECURITY_CONSTRAINT;
1✔
469
    }
470

471
    /**
472
     * Returns an array containing the roles permitted to access the specified URL.
473
     *
474
     * @param url
475
     *            the url
476
     *
477
     * @return the permitted roles
478
     */
479
    String[] getPermittedRoles(URL url) {
480
        String result;
481
        String file = url.getFile();
1✔
482
        if (_contextPath.equals("")) {
1✔
483
            result = file;
1✔
484
        } else if (file.startsWith(_contextPath)) {
1!
485
            result = file.substring(_contextPath.length());
1✔
486
        } else {
UNCOV
487
            result = null;
×
488
        }
489
        return getControllingConstraint(result).getPermittedRoles();
1✔
490
    }
491

492
    /**
493
     * Gets the controlling constraint.
494
     *
495
     * @param urlPath
496
     *            the url path
497
     *
498
     * @return the controlling constraint
499
     */
500
    private SecurityConstraint getControllingConstraint(String urlPath) {
501
        for (SecurityConstraint sc : _securityConstraints) {
1✔
502
            if (sc.controlsPath(urlPath)) {
1✔
503
                return sc;
1✔
504
            }
505
        }
1✔
506
        return NULL_SECURITY_CONSTRAINT;
1✔
507
    }
508

509
    /**
510
     * Gets the resource file.
511
     *
512
     * @param path
513
     *            the path
514
     *
515
     * @return the resource file
516
     */
517
    File getResourceFile(String path) {
518
        String relativePath = path.startsWith("/") ? path.substring(1) : path;
1✔
519
        if (_contextDir == null) {
1✔
520
            return Path.of(relativePath).toFile();
1✔
521
        }
522
        return _contextDir.toPath().resolve(relativePath).toFile();
1✔
523
    }
524

525
    /**
526
     * Gets the context parameters.
527
     *
528
     * @return the context parameters
529
     */
530
    Hashtable getContextParameters() {
531
        return _contextParameters;
1✔
532
    }
533

534
    // ---------------------------------------- SessionListenerDispatcher methods
535
    // -------------------------------------------
536

537
    @Override
538
    public void sendSessionCreated(HttpSession session) {
539
        HttpSessionEvent event = new HttpSessionEvent(session);
1✔
540

541
        for (HttpSessionListener listener : _sessionListeners) {
1✔
542
            listener.sessionCreated(event);
1✔
543
        }
1✔
544
    }
1✔
545

546
    @Override
547
    public void sendSessionDestroyed(HttpSession session) {
548
        HttpSessionEvent event = new HttpSessionEvent(session);
1✔
549

550
        for (HttpSessionListener listener : _sessionListeners) {
1✔
551
            listener.sessionDestroyed(event);
1✔
552
        }
1✔
553
    }
1✔
554

555
    @Override
556
    public void sendAttributeAdded(HttpSession session, String name, Object value) {
557
        HttpSessionBindingEvent event = new HttpSessionBindingEvent(session, name, value);
1✔
558

559
        for (HttpSessionAttributeListener listener : _sessionAttributeListeners) {
1✔
560
            listener.attributeAdded(event);
1✔
561
        }
1✔
562
    }
1✔
563

564
    @Override
565
    public void sendAttributeReplaced(HttpSession session, String name, Object oldValue) {
566
        HttpSessionBindingEvent event = new HttpSessionBindingEvent(session, name, oldValue);
1✔
567

568
        for (HttpSessionAttributeListener listener : _sessionAttributeListeners) {
1✔
569
            listener.attributeReplaced(event);
1✔
570
        }
1✔
571
    }
1✔
572

573
    @Override
574
    public void sendAttributeRemoved(HttpSession session, String name, Object oldValue) {
575
        HttpSessionBindingEvent event = new HttpSessionBindingEvent(session, name, oldValue);
1✔
576

577
        for (HttpSessionAttributeListener listener : _sessionAttributeListeners) {
1✔
578
            listener.attributeRemoved(event);
1✔
579
        }
1✔
580
    }
1✔
581

582
    // --------------------------------------------------- private members
583
    // --------------------------------------------------
584

585
    /**
586
     * Register filters.
587
     *
588
     * @param document
589
     *            the document
590
     *
591
     * @throws SAXException
592
     *             the SAX exception
593
     */
594
    private void registerFilters(Document document) throws SAXException {
595
        Hashtable nameToClass = new Hashtable<>();
1✔
596
        NodeList nl = document.getElementsByTagName("filter");
1✔
597
        for (int i = 0; i < nl.getLength(); i++) {
1✔
598
            registerFilterClass(nameToClass, (Element) nl.item(i));
1✔
599
        }
600
        nl = document.getElementsByTagName("filter-mapping");
1✔
601
        for (int i = 0; i < nl.getLength(); i++) {
1✔
602
            registerFilter(nameToClass, (Element) nl.item(i));
1✔
603
        }
604
        this._filters = nameToClass;
1✔
605
    }
1✔
606

607
    /**
608
     * Register filter class.
609
     *
610
     * @param mapping
611
     *            the mapping
612
     * @param filterElement
613
     *            the filter element
614
     *
615
     * @throws SAXException
616
     *             the SAX exception
617
     */
618
    private void registerFilterClass(Dictionary mapping, Element filterElement) throws SAXException {
619
        String filterName = XMLUtils.getChildNodeValue(filterElement, "filter-name");
1✔
620
        mapping.put(filterName, new FilterConfiguration(filterName, filterElement));
1✔
621
    }
1✔
622

623
    /**
624
     * Register filter.
625
     *
626
     * @param mapping
627
     *            the mapping
628
     * @param filterElement
629
     *            the filter element
630
     *
631
     * @throws SAXException
632
     *             the SAX exception
633
     */
634
    private void registerFilter(Dictionary mapping, Element filterElement) throws SAXException {
635
        if (XMLUtils.hasChildNode(filterElement, "servlet-name")) {
1✔
636
            registerFilterForServlet(XMLUtils.getChildNodeValue(filterElement, "servlet-name"),
1✔
637
                    (FilterConfiguration) mapping.get(XMLUtils.getChildNodeValue(filterElement, "filter-name")));
1✔
638
        }
639
        if (XMLUtils.hasChildNode(filterElement, "url-pattern")) {
1✔
640
            registerFilterForUrl(XMLUtils.getChildNodeValue(filterElement, "url-pattern"),
1✔
641
                    (FilterConfiguration) mapping.get(XMLUtils.getChildNodeValue(filterElement, "filter-name")));
1✔
642
        }
643
    }
1✔
644

645
    /**
646
     * Register filter for url.
647
     *
648
     * @param resourceName
649
     *            the resource name
650
     * @param filterConfiguration
651
     *            the filter configuration
652
     */
653
    private void registerFilterForUrl(String resourceName, FilterConfiguration filterConfiguration) {
654
        _filterUrlMapping.put(resourceName, filterConfiguration);
1✔
655
    }
1✔
656

657
    /**
658
     * Register filter for servlet.
659
     *
660
     * @param servletName
661
     *            the servlet name
662
     * @param filterConfiguration
663
     *            the filter configuration
664
     */
665
    private void registerFilterForServlet(String servletName, FilterConfiguration filterConfiguration) {
666
        List list = (List) _filterMapping.get(servletName);
1✔
667
        if (list == null) {
1✔
668
            list = new ArrayList<>();
1✔
669
            _filterMapping.put(servletName, list);
1✔
670
        }
671
        list.add(filterConfiguration);
1✔
672
    }
1✔
673

674
    /**
675
     * Extract login configuration.
676
     *
677
     * @param document
678
     *            the document
679
     *
680
     * @throws MalformedURLException
681
     *             the malformed URL exception
682
     * @throws SAXException
683
     *             the SAX exception
684
     */
685
    private void extractLoginConfiguration(Document document) throws MalformedURLException, SAXException {
686
        NodeList nl = document.getElementsByTagName("login-config");
1✔
687
        if (nl.getLength() == 1) {
1✔
688
            final Element loginConfigElement = (Element) nl.item(0);
1✔
689
            String authenticationMethod = XMLUtils.getChildNodeValue(loginConfigElement, "auth-method", "BASIC");
1✔
690
            _authenticationRealm = XMLUtils.getChildNodeValue(loginConfigElement, "realm-name", "");
1✔
691
            if (authenticationMethod.equalsIgnoreCase("BASIC")) {
1✔
692
                _useBasicAuthentication = true;
1✔
693
                if (_authenticationRealm.isEmpty()) {
1!
UNCOV
694
                    throw new SAXException("No realm specified for BASIC Authorization");
×
695
                }
696
            } else if (authenticationMethod.equalsIgnoreCase("FORM")) {
1!
697
                _useFormAuthentication = true;
1✔
698
                if (_authenticationRealm.isEmpty()) {
1!
UNCOV
699
                    throw new SAXException("No realm specified for FORM Authorization");
×
700
                }
701
                _loginURL = new URL("http", "localhost",
1✔
702
                        _contextPath + XMLUtils.getChildNodeValue(loginConfigElement, "form-login-page"));
1✔
703
                _errorURL = new URL("http", "localhost",
1✔
704
                        _contextPath + XMLUtils.getChildNodeValue(loginConfigElement, "form-error-page"));
1✔
705
            }
706
        }
707
    }
1✔
708

709
    /**
710
     * Register servlets.
711
     *
712
     * @param document
713
     *            the document
714
     *
715
     * @throws SAXException
716
     *             the SAX exception
717
     */
718
    private void registerServlets(Document document) throws SAXException {
719
        Hashtable nameToClass = new Hashtable<>();
1✔
720
        NodeList nl = document.getElementsByTagName("servlet");
1✔
721
        for (int i = 0; i < nl.getLength(); i++) {
1✔
722
            registerServletClass(nameToClass, (Element) nl.item(i));
1✔
723
        }
724
        nl = document.getElementsByTagName("servlet-mapping");
1✔
725
        for (int i = 0; i < nl.getLength(); i++) {
1✔
726
            registerServlet(nameToClass, (Element) nl.item(i));
1✔
727
        }
728
        this._servlets = nameToClass;
1✔
729
    }
1✔
730

731
    /**
732
     * Register servlet class.
733
     *
734
     * @param mapping
735
     *            the mapping
736
     * @param servletElement
737
     *            the servlet element
738
     *
739
     * @throws SAXException
740
     *             the SAX exception
741
     */
742
    private void registerServletClass(Dictionary mapping, Element servletElement) throws SAXException {
743
        mapping.put(XMLUtils.getChildNodeValue(servletElement, "servlet-name"),
1✔
744
                new ServletConfiguration(servletElement));
745
    }
1✔
746

747
    /**
748
     * Register servlet.
749
     *
750
     * @param mapping
751
     *            the mapping
752
     * @param servletElement
753
     *            the servlet element
754
     *
755
     * @throws SAXException
756
     *             the SAX exception
757
     */
758
    private void registerServlet(Dictionary mapping, Element servletElement) throws SAXException {
759
        registerServlet(XMLUtils.getChildNodeValue(servletElement, "url-pattern"),
1✔
760
                (ServletConfiguration) mapping.get(XMLUtils.getChildNodeValue(servletElement, "servlet-name")));
1✔
761
    }
1✔
762

763
    /**
764
     * Extract context parameters.
765
     *
766
     * @param document
767
     *            the document
768
     *
769
     * @throws SAXException
770
     *             the SAX exception
771
     */
772
    private void extractContextParameters(Document document) throws SAXException {
773
        NodeList nl = document.getElementsByTagName("context-param");
1✔
774
        for (int i = 0; i < nl.getLength(); i++) {
1✔
775
            Element param = (Element) nl.item(i);
1✔
776
            String name = XMLUtils.getChildNodeValue(param, "param-name");
1✔
777
            String value = XMLUtils.getChildNodeValue(param, "param-value");
1✔
778
            _contextParameters.put(name, value);
1✔
779
        }
780
    }
1✔
781

782
    /**
783
     * Pattern matches.
784
     *
785
     * @param urlPattern
786
     *            the url pattern
787
     * @param urlPath
788
     *            the url path
789
     *
790
     * @return true, if successful
791
     */
792
    private static boolean patternMatches(String urlPattern, String urlPath) {
793
        return urlPattern.equals(urlPath);
1✔
794
    }
795

796
    /**
797
     * Gets the display name.
798
     *
799
     * @return the display name
800
     */
801
    String getDisplayName() {
802
        return _displayName;
1✔
803
    }
804

805
    // ============================================= SecurityCheckServlet class
806
    // =============================================
807

808
    /**
809
     * The Class SecurityCheckServlet.
810
     */
811
    static class SecurityCheckServlet extends HttpServlet {
1✔
812

813
        /** The Constant serialVersionUID. */
814
        private static final long serialVersionUID = 1L;
815

816
        @Override
817
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
818
            handleLogin(req, resp);
×
UNCOV
819
        }
×
820

821
        @Override
822
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
823
            handleLogin(req, resp);
1✔
824
        }
1✔
825

826
        /**
827
         * Handle login.
828
         *
829
         * @param req
830
         *            the req
831
         * @param resp
832
         *            the resp
833
         *
834
         * @throws IOException
835
         *             Signals that an I/O exception has occurred.
836
         */
837
        private void handleLogin(HttpServletRequest req, HttpServletResponse resp) throws IOException {
838
            final String username = req.getParameter("j_username");
1✔
839
            final String roleList = req.getParameter("j_password");
1✔
840
            getServletSession(req).setUserInformation(username, ServletUnitHttpRequest.toArray(roleList));
1✔
841
            resp.sendRedirect(getServletSession(req).getOriginalURL().toExternalForm());
1✔
842
        }
1✔
843

844
        /**
845
         * Gets the servlet session.
846
         *
847
         * @param req
848
         *            the req
849
         *
850
         * @return the servlet session
851
         */
852
        private ServletUnitHttpSession getServletSession(HttpServletRequest req) {
853
            return (ServletUnitHttpSession) req.getSession();
1✔
854
        }
855

856
    }
857

858
    // ============================================= ServletConfiguration class
859
    // =============================================
860

861
    /** The Constant DONT_AUTOLOAD. */
862
    static final int DONT_AUTOLOAD = Integer.MIN_VALUE;
863

864
    /** The Constant ANY_LOAD_ORDER. */
865
    static final int ANY_LOAD_ORDER = Integer.MAX_VALUE;
866

867
    /**
868
     * The Class ServletConfiguration.
869
     */
870
    class ServletConfiguration extends WebResourceConfiguration {
871

872
        /** The servlet. */
873
        private Servlet _servlet;
874

875
        /** The servlet name. */
876
        private String _servletName;
877

878
        /** The jsp file. */
879
        private String _jspFile;
880

881
        /** The load order. */
882
        private int _loadOrder = DONT_AUTOLOAD;
1✔
883

884
        /**
885
         * Instantiates a new servlet configuration.
886
         *
887
         * @param className
888
         *            the class name
889
         */
890
        ServletConfiguration(String className) {
1✔
891
            super(className);
1✔
892
        }
1✔
893

894
        /**
895
         * Instantiates a new servlet configuration.
896
         *
897
         * @param className
898
         *            the class name
899
         * @param initParams
900
         *            the init params
901
         */
902
        ServletConfiguration(String className, Hashtable initParams) {
1✔
903
            super(className, initParams);
1✔
904
        }
1✔
905

906
        /**
907
         * Instantiates a new servlet configuration.
908
         *
909
         * @param servletElement
910
         *            the servlet element
911
         *
912
         * @throws SAXException
913
         *             the SAX exception
914
         */
915
        ServletConfiguration(Element servletElement) throws SAXException {
1✔
916
            super(servletElement, "servlet-class", XMLUtils.getChildNodeValue(servletElement, "servlet-class",
1✔
917
                    "org.apache.jasper.servlet.JspServlet"));
918
            _servletName = XMLUtils.getChildNodeValue(servletElement, "servlet-name");
1✔
919
            _jspFile = XMLUtils.getChildNodeValue(servletElement, "jsp-file", "");
1✔
920
            if ("".equals(_jspFile)) {
1!
921
                _jspFile = null;
1✔
922
            }
923
            final NodeList loadOrder = servletElement.getElementsByTagName("load-on-startup");
1✔
924
            for (int i = 0; i < loadOrder.getLength(); i++) {
1✔
925
                String order = XMLUtils.getTextValue(loadOrder.item(i));
1✔
926
                try {
927
                    _loadOrder = Integer.parseInt(order);
1✔
928
                } catch (NumberFormatException e) {
1✔
929
                    _loadOrder = ANY_LOAD_ORDER;
1✔
930
                }
1✔
931
            }
932
        }
1✔
933

934
        /**
935
         * Gets the servlet.
936
         *
937
         * @return the servlet
938
         *
939
         * @throws ClassNotFoundException
940
         *             the class not found exception
941
         * @throws InstantiationException
942
         *             the instantiation exception
943
         * @throws IllegalAccessException
944
         *             the illegal access exception
945
         * @throws ServletException
946
         *             the servlet exception
947
         * @throws IllegalArgumentException
948
         *             the illegal argument exception
949
         * @throws InvocationTargetException
950
         *             the invocation target exception
951
         * @throws NoSuchMethodException
952
         *             the no such method exception
953
         * @throws SecurityException
954
         *             the security exception
955
         */
956
        synchronized Servlet getServlet()
957
                throws ClassNotFoundException, InstantiationException, IllegalAccessException, ServletException,
958
                IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
959
            if (_servlet == null) {
1✔
960
                Class servletClass = Class.forName(getClassName());
1✔
961
                _servlet = (Servlet) servletClass.getDeclaredConstructor().newInstance();
1✔
962
                String servletName = _servletName != null ? _servletName : _servlet.getClass().getName();
1✔
963
                _servlet.init(new ServletUnitServletConfig(servletName, WebApplication.this, getInitParams()));
1✔
964
            }
965

966
            return _servlet;
1✔
967
        }
968

969
        @Override
970
        synchronized void destroyResource() {
971
            if (_servlet != null) {
1✔
972
                _servlet.destroy();
1✔
973
            }
974
        }
1✔
975

976
        /**
977
         * Gets the servlet name.
978
         *
979
         * @return the servlet name
980
         */
981
        String getServletName() {
982
            return _servletName;
1✔
983
        }
984

985
        @Override
986
        boolean isLoadOnStartup() {
987
            return _loadOrder != DONT_AUTOLOAD;
1✔
988
        }
989

990
        /**
991
         * Gets the load order.
992
         *
993
         * @return the load order
994
         */
995
        public int getLoadOrder() {
996
            return _loadOrder;
1✔
997
        }
998

999
        /**
1000
         * Gets the jsp file.
1001
         *
1002
         * @return the jsp file
1003
         */
1004
        public Object getJspFile() {
UNCOV
1005
            return this._jspFile;
×
1006
        }
1007
    }
1008

1009
    // ============================================= FilterConfiguration class
1010
    // =============================================
1011

1012
    /**
1013
     * The Class FilterConfiguration.
1014
     */
1015
    class FilterConfiguration extends WebResourceConfiguration implements FilterMetaData {
1016

1017
        /** The filter. */
1018
        private Filter _filter;
1019

1020
        /** The name. */
1021
        private String _name;
1022

1023
        /**
1024
         * Instantiates a new filter configuration.
1025
         *
1026
         * @param name
1027
         *            the name
1028
         * @param filterElement
1029
         *            the filter element
1030
         *
1031
         * @throws SAXException
1032
         *             the SAX exception
1033
         */
1034
        FilterConfiguration(String name, Element filterElement) throws SAXException {
1✔
1035
            super(filterElement, "filter-class");
1✔
1036
            _name = name;
1✔
1037
        }
1✔
1038

1039
        @Override
1040
        public synchronized Filter getFilter() throws ServletException {
1041
            try {
1042
                if (_filter == null) {
1✔
1043
                    Class filterClass = Class.forName(getClassName());
1✔
1044
                    _filter = (Filter) filterClass.getDeclaredConstructor().newInstance();
1✔
1045
                    _filter.init(new FilterConfigImpl(_name, getServletContext(), getInitParams()));
1✔
1046
                }
1047

1048
                return _filter;
1✔
1049
            } catch (ClassNotFoundException e) {
×
1050
                throw new ServletException("Did not find filter class: " + getClassName());
×
1051
            } catch (IllegalAccessException e) {
×
1052
                throw new ServletException("Filter class " + getClassName() + " lacks a public no-arg constructor");
×
UNCOV
1053
            } catch (InstantiationException | IllegalArgumentException | InvocationTargetException
×
1054
                    | NoSuchMethodException | SecurityException e) {
1055
                throw new ServletException("Filter class " + getClassName() + " could not be instantiated.");
×
1056
            } catch (ClassCastException e) {
×
1057
                throw new ServletException(
×
UNCOV
1058
                        "Filter class " + getClassName() + " does not implement" + Filter.class.getName());
×
1059
            }
1060
        }
1061

1062
        @Override
1063
        boolean isLoadOnStartup() {
UNCOV
1064
            return false;
×
1065
        }
1066

1067
        @Override
1068
        synchronized void destroyResource() {
1069
            if (_filter != null) {
×
UNCOV
1070
                _filter.destroy();
×
1071
            }
UNCOV
1072
        }
×
1073
    }
1074

1075
    // =================================== SecurityConstract interface and implementations
1076
    // ==================================
1077

1078
    /**
1079
     * The Interface SecurityConstraint.
1080
     */
1081
    interface SecurityConstraint {
1082

1083
        /**
1084
         * Controls path.
1085
         *
1086
         * @param urlPath
1087
         *            the url path
1088
         *
1089
         * @return true, if successful
1090
         */
1091
        boolean controlsPath(String urlPath);
1092

1093
        /**
1094
         * Gets the permitted roles.
1095
         *
1096
         * @return the permitted roles
1097
         */
1098
        String[] getPermittedRoles();
1099
    }
1100

1101
    /**
1102
     * The Class NullSecurityConstraint.
1103
     */
1104
    static class NullSecurityConstraint implements SecurityConstraint {
1✔
1105

1106
        /** The Constant NO_ROLES. */
1107
        private static final String[] NO_ROLES = {};
1✔
1108

1109
        @Override
1110
        public boolean controlsPath(String urlPath) {
UNCOV
1111
            return false;
×
1112
        }
1113

1114
        @Override
1115
        public String[] getPermittedRoles() {
UNCOV
1116
            return NO_ROLES;
×
1117
        }
1118
    }
1119

1120
    /**
1121
     * The Class SecurityConstraintImpl.
1122
     */
1123
    static class SecurityConstraintImpl implements SecurityConstraint {
1124

1125
        /**
1126
         * Instantiates a new security constraint impl.
1127
         *
1128
         * @param root
1129
         *            the root
1130
         *
1131
         * @throws SAXException
1132
         *             the SAX exception
1133
         */
1134
        SecurityConstraintImpl(Element root) throws SAXException {
1✔
1135
            final NodeList roleNames = root.getElementsByTagName("role-name");
1✔
1136
            for (int i = 0; i < roleNames.getLength(); i++) {
1✔
1137
                _roleList.add(XMLUtils.getTextValue(roleNames.item(i)));
1✔
1138
            }
1139

1140
            final NodeList resources = root.getElementsByTagName("web-resource-collection");
1✔
1141
            for (int i = 0; i < resources.getLength(); i++) {
1✔
1142
                _resources.add(new WebResourceCollection((Element) resources.item(i)));
1✔
1143
            }
1144
        }
1✔
1145

1146
        @Override
1147
        public boolean controlsPath(String urlPath) {
1148
            return getMatchingCollection(urlPath) != null;
1✔
1149
        }
1150

1151
        @Override
1152
        public String[] getPermittedRoles() {
1153
            if (_roles == null) {
1✔
1154
                _roles = _roleList.toArray(new String[_roleList.size()]);
1✔
1155
            }
1156
            return _roles;
1✔
1157
        }
1158

1159
        /** The roles. */
1160
        private String[] _roles;
1161

1162
        /** The role list. */
1163
        private List<String> _roleList = new ArrayList<>();
1✔
1164

1165
        /** The resources. */
1166
        private List<WebResourceCollection> _resources = new ArrayList<>();
1✔
1167

1168
        /**
1169
         * Gets the matching collection.
1170
         *
1171
         * @param urlPath
1172
         *            the url path
1173
         *
1174
         * @return the matching collection
1175
         */
1176
        public WebResourceCollection getMatchingCollection(String urlPath) {
1177
            for (WebResourceCollection wrc : _resources) {
1✔
1178
                if (wrc.controlsPath(urlPath)) {
1✔
1179
                    return wrc;
1✔
1180
                }
1181
            }
1✔
1182
            return null;
1✔
1183
        }
1184

1185
        /**
1186
         * The Class WebResourceCollection.
1187
         */
1188
        class WebResourceCollection {
1189

1190
            /**
1191
             * Instantiates a new web resource collection.
1192
             *
1193
             * @param root
1194
             *            the root
1195
             *
1196
             * @throws SAXException
1197
             *             the SAX exception
1198
             */
1199
            WebResourceCollection(Element root) throws SAXException {
1✔
1200
                final NodeList urlPatterns = root.getElementsByTagName("url-pattern");
1✔
1201
                for (int i = 0; i < urlPatterns.getLength(); i++) {
1✔
1202
                    _urlPatterns.add(XMLUtils.getTextValue(urlPatterns.item(i)));
1✔
1203
                }
1204
            }
1✔
1205

1206
            /**
1207
             * Controls path.
1208
             *
1209
             * @param urlPath
1210
             *            the url path
1211
             *
1212
             * @return true, if successful
1213
             */
1214
            boolean controlsPath(String urlPath) {
1215
                for (String pattern : _urlPatterns) {
1✔
1216
                    if (patternMatches(pattern, urlPath)) {
1✔
1217
                        return true;
1✔
1218
                    }
1219
                }
1✔
1220
                return false;
1✔
1221
            }
1222

1223
            /** The url patterns. */
1224
            private List<String> _urlPatterns = new ArrayList<>();
1✔
1225
        }
1226
    }
1227

1228
    /** The Constant NO_FILTERS. */
1229
    static final FilterMetaData[] NO_FILTERS = {};
1✔
1230

1231
    /**
1232
     * The Class ServletRequestImpl.
1233
     */
1234
    static class ServletRequestImpl implements ServletMetaData {
1235

1236
        /** The url. */
1237
        private URL _url;
1238

1239
        /** The full servlet path. */
1240
        private String _fullServletPath;
1241

1242
        /** The mapping. */
1243
        private WebResourceMapping _mapping;
1244

1245
        /** The filters per name. */
1246
        private Hashtable _filtersPerName;
1247

1248
        /** The filters per url. */
1249
        private FilterUrlMap _filtersPerUrl;
1250

1251
        /**
1252
         * Instantiates a new servlet request impl.
1253
         *
1254
         * @param url
1255
         *            the url
1256
         * @param servletPath
1257
         *            the servlet path
1258
         * @param mapping
1259
         *            the mapping
1260
         * @param filtersPerName
1261
         *            the filters per name
1262
         * @param filtersPerUrl
1263
         *            the filters per url
1264
         */
1265
        ServletRequestImpl(URL url, String servletPath, WebResourceMapping mapping, Hashtable filtersPerName,
1266
                FilterUrlMap filtersPerUrl) {
1✔
1267
            _url = url;
1✔
1268
            _fullServletPath = servletPath;
1✔
1269
            _mapping = mapping;
1✔
1270
            _filtersPerName = filtersPerName;
1✔
1271
            _filtersPerUrl = filtersPerUrl;
1✔
1272
        }
1✔
1273

1274
        /**
1275
         * get the Servlet
1276
         *
1277
         * @return the Servlet from the configuration
1278
         *
1279
         * @throws ServletException
1280
         *             - e.g. if no configuration is available
1281
         */
1282
        @Override
1283
        public Servlet getServlet() throws ServletException {
1284
            if (getConfiguration() == null) {
1✔
1285
                throw new HttpNotFoundException("No servlet mapping defined", _url);
1✔
1286
            }
1287

1288
            try {
1289
                return getConfiguration().getServlet();
1✔
1290
            } catch (ClassNotFoundException e) {
×
1291
                throw new HttpNotFoundException(_url, e);
×
UNCOV
1292
            } catch (IllegalAccessException | InstantiationException | IllegalArgumentException
×
1293
                    | InvocationTargetException | NoSuchMethodException | SecurityException e) {
UNCOV
1294
                throw new HttpInternalErrorException(_url, e);
×
1295
            }
1296
        }
1297

1298
        /**
1299
         * get the ServletPath the decoded ServletPath
1300
         */
1301
        @Override
1302
        public String getServletPath() {
1303
            return _mapping == null ? null : HttpUnitUtils.decode(_mapping.getServletPath(_fullServletPath));
1!
1304
        }
1305

1306
        /**
1307
         * get the Path Information
1308
         *
1309
         * @return the decode path
1310
         */
1311
        @Override
1312
        public String getPathInfo() {
1313
            return _mapping == null ? null : HttpUnitUtils.decode(_mapping.getPathInfo(_fullServletPath));
1!
1314
        }
1315

1316
        @Override
1317
        public FilterMetaData[] getFilters() {
1318
            if (getConfiguration() == null) {
1✔
1319
                return NO_FILTERS;
1✔
1320
            }
1321

1322
            List<FilterMetaData> filters = new ArrayList<>();
1✔
1323
            addFiltersForPath(filters, _fullServletPath);
1✔
1324
            addFiltersForServletWithName(filters, getConfiguration().getServletName());
1✔
1325

1326
            return filters.toArray(new FilterMetaData[filters.size()]);
1✔
1327
        }
1328

1329
        /**
1330
         * Adds the filters for path.
1331
         *
1332
         * @param filters
1333
         *            the filters
1334
         * @param fullServletPath
1335
         *            the full servlet path
1336
         */
1337
        private void addFiltersForPath(List<FilterMetaData> filters, String fullServletPath) {
1338
            FilterMetaData[] matches = _filtersPerUrl.getMatchingFilters(fullServletPath);
1✔
1339
            Collections.addAll(filters, matches);
1✔
1340
        }
1✔
1341

1342
        /**
1343
         * Adds the filters for servlet with name.
1344
         *
1345
         * @param filters
1346
         *            the filters
1347
         * @param servletName
1348
         *            the servlet name
1349
         */
1350
        private void addFiltersForServletWithName(List<FilterMetaData> filters, String servletName) {
1351
            if (servletName == null) {
1✔
1352
                return;
1✔
1353
            }
1354
            List<FilterMetaData> matches = (List<FilterMetaData>) _filtersPerName.get(servletName);
1✔
1355
            if (matches != null) {
1✔
1356
                filters.addAll(matches);
1✔
1357
            }
1358
        }
1✔
1359

1360
        /**
1361
         * Gets the configuration.
1362
         *
1363
         * @return the configuration
1364
         */
1365
        private ServletConfiguration getConfiguration() {
1366
            return _mapping == null ? null : (ServletConfiguration) _mapping.getConfiguration();
1✔
1367
        }
1368
    }
1369

1370
    /**
1371
     * mapping for WebResources.
1372
     */
1373
    static class WebResourceMapping {
1374

1375
        /** The configuration. */
1376
        private WebResourceConfiguration _configuration;
1377

1378
        /**
1379
         * Gets the configuration.
1380
         *
1381
         * @return the configuration
1382
         */
1383
        WebResourceConfiguration getConfiguration() {
1384
            return _configuration;
1✔
1385
        }
1386

1387
        /**
1388
         * Instantiates a new web resource mapping.
1389
         *
1390
         * @param configuration
1391
         *            the configuration
1392
         */
1393
        WebResourceMapping(WebResourceConfiguration configuration) {
1✔
1394
            _configuration = configuration;
1✔
1395
        }
1✔
1396

1397
        /**
1398
         * Returns the portion of the request path which was actually used to select the servlet. This default
1399
         * implementation returns the full specified path.
1400
         *
1401
         * @param requestPath
1402
         *            the full path of the request, relative to the application root.
1403
         *
1404
         * @return the servlet path
1405
         */
1406
        String getServletPath(String requestPath) {
1407
            return requestPath;
1✔
1408
        }
1409

1410
        /**
1411
         * Returns the portion of the request path which was not used to select the servlet, and can be used as data by
1412
         * the servlet. This default implementation returns null.
1413
         *
1414
         * @param requestPath
1415
         *            the full path of the request, relative to the application root.
1416
         *
1417
         * @return the path info
1418
         */
1419
        String getPathInfo(String requestPath) {
1420
            return null;
1✔
1421
        }
1422

1423
        /**
1424
         * Destroy resource.
1425
         */
1426
        public void destroyResource() {
1427
            getConfiguration().destroyResource();
1✔
1428
        }
1✔
1429
    }
1430

1431
    /**
1432
     * The Class PartialMatchWebResourceMapping.
1433
     */
1434
    static class PartialMatchWebResourceMapping extends WebResourceMapping {
1435

1436
        /** The prefix. */
1437
        private String _prefix;
1438

1439
        /**
1440
         * Instantiates a new partial match web resource mapping.
1441
         *
1442
         * @param configuration
1443
         *            the configuration
1444
         * @param prefix
1445
         *            the prefix
1446
         */
1447
        public PartialMatchWebResourceMapping(WebResourceConfiguration configuration, String prefix) {
1448
            super(configuration);
1✔
1449
            if (!prefix.endsWith("/*")) {
1!
UNCOV
1450
                throw new IllegalArgumentException(prefix + " does not end with '/*'");
×
1451
            }
1452
            _prefix = prefix.substring(0, prefix.length() - 2);
1✔
1453
        }
1✔
1454

1455
        @Override
1456
        String getServletPath(String requestPath) {
1457
            return _prefix;
1✔
1458
        }
1459

1460
        @Override
1461
        String getPathInfo(String requestPath) {
1462
            return requestPath.length() > _prefix.length() ? requestPath.substring(_prefix.length()) : null;
1✔
1463
        }
1464
    }
1465

1466
    /**
1467
     * A utility class for mapping web resources to url patterns. This implements the matching algorithm documented in
1468
     * section 10 of the JSDK-2.2 reference.
1469
     */
1470
    class WebResourceMap {
1✔
1471

1472
        /** The exact matches. */
1473
        private final Map _exactMatches = new HashMap<>();
1✔
1474

1475
        /** The extensions. */
1476
        private final Map _extensions = new HashMap<>();
1✔
1477

1478
        /** The url tree. */
1479
        private final Map _urlTree = new HashMap<>();
1✔
1480

1481
        /** The default mapping. */
1482
        private WebResourceMapping _defaultMapping;
1483

1484
        /**
1485
         * Put.
1486
         *
1487
         * @param mapping
1488
         *            the mapping
1489
         * @param configuration
1490
         *            the configuration
1491
         */
1492
        void put(String mapping, WebResourceConfiguration configuration) {
1493
            if (mapping.equals("/")) {
1✔
1494
                _defaultMapping = new WebResourceMapping(configuration);
1✔
1495
            } else if (mapping.startsWith("*.")) {
1✔
1496
                _extensions.put(mapping.substring(2), new WebResourceMapping(configuration));
1✔
1497
            } else if (!mapping.startsWith("/") || !mapping.endsWith("/*")) {
1!
1498
                _exactMatches.put(mapping, new WebResourceMapping(configuration));
1✔
1499
            } else {
1500
                ParsedPath path = new ParsedPath(mapping);
1✔
1501
                Map context = _urlTree;
1✔
1502
                while (path.hasNext()) {
1!
1503
                    String part = path.next();
1✔
1504
                    if (part.equals("*")) {
1✔
1505
                        context.put("*", new PartialMatchWebResourceMapping(configuration, mapping));
1✔
1506
                        return;
1✔
1507
                    }
1508
                    if (!context.containsKey(part)) {
1!
1509
                        context.put(part, new HashMap<>());
1✔
1510
                    }
1511
                    context = (Map) context.get(part);
1✔
1512
                }
1✔
1513
            }
1514
        }
1✔
1515

1516
        /**
1517
         * Gets the.
1518
         *
1519
         * @param url
1520
         *            the url
1521
         *
1522
         * @return the servlet meta data
1523
         */
1524
        ServletMetaData get(URL url) {
1525
            String file = url.getFile();
1✔
1526
            if (!file.startsWith(_contextPath)) {
1✔
1527
                throw new HttpNotFoundException("File path does not begin with '" + _contextPath + "'", url);
1✔
1528
            }
1529

1530
            String servletPath = getServletPath(file.substring(_contextPath.length()));
1✔
1531

1532
            if (servletPath.endsWith("j_security_check")) {
1✔
1533
                return new ServletRequestImpl(url, servletPath, SECURITY_CHECK_MAPPING, _filterMapping,
1✔
1534
                        _filterUrlMapping);
1535
            }
1536
            return new ServletRequestImpl(url, servletPath, getMapping(servletPath), _filterMapping, _filterUrlMapping);
1✔
1537
        }
1538

1539
        /**
1540
         * Gets the servlet path.
1541
         *
1542
         * @param urlFile
1543
         *            the url file
1544
         *
1545
         * @return the servlet path
1546
         */
1547
        private String getServletPath(String urlFile) {
1548
            if (urlFile.indexOf('?') < 0) {
1✔
1549
                return urlFile;
1✔
1550
            }
1551
            return urlFile.substring(0, urlFile.indexOf('?'));
1✔
1552
        }
1553

1554
        /**
1555
         * Destroy web resources.
1556
         */
1557
        public void destroyWebResources() {
1558
            if (_defaultMapping != null) {
1!
UNCOV
1559
                _defaultMapping.destroyResource();
×
1560
            }
1561
            destroyWebResources(_exactMatches);
1✔
1562
            destroyWebResources(_extensions);
1✔
1563
            destroyWebResources(_urlTree);
1✔
1564
        }
1✔
1565

1566
        /**
1567
         * Destroy web resources.
1568
         *
1569
         * @param map
1570
         *            the map
1571
         */
1572
        private void destroyWebResources(Map map) {
1573
            for (Object o : map.values()) {
1✔
1574
                if (o instanceof WebResourceMapping) {
1!
1575
                    WebResourceMapping webResourceMapping = (WebResourceMapping) o;
1✔
1576
                    webResourceMapping.destroyResource();
1✔
1577
                } else {
1✔
UNCOV
1578
                    destroyWebResources((Map) o);
×
1579
                }
1580
            }
1✔
1581
        }
1✔
1582

1583
        /**
1584
         * Auto load servlets.
1585
         */
1586
        void autoLoadServlets() {
1587
            ArrayList autoLoadable = new ArrayList<>();
1✔
1588
            if (_defaultMapping != null && _defaultMapping.getConfiguration().isLoadOnStartup()) {
1!
UNCOV
1589
                autoLoadable.add(_defaultMapping.getConfiguration());
×
1590
            }
1591
            collectAutoLoadableServlets(autoLoadable, _exactMatches);
1✔
1592
            collectAutoLoadableServlets(autoLoadable, _extensions);
1✔
1593
            collectAutoLoadableServlets(autoLoadable, _urlTree);
1✔
1594
            if (autoLoadable.isEmpty()) {
1✔
1595
                return;
1✔
1596
            }
1597

1598
            Collections.sort(autoLoadable, (o1, o2) -> {
1✔
1599
                ServletConfiguration sc1 = (ServletConfiguration) o1;
1✔
1600
                ServletConfiguration sc2 = (ServletConfiguration) o2;
1✔
1601
                return sc1.getLoadOrder() <= sc2.getLoadOrder() ? -1 : +1;
1✔
1602
            });
1603
            for (Iterator iterator = autoLoadable.iterator(); iterator.hasNext();) {
1✔
1604
                ServletConfiguration servletConfiguration = (ServletConfiguration) iterator.next();
1✔
1605
                try {
1606
                    servletConfiguration.getServlet();
1✔
1607
                } catch (Exception e) {
×
1608
                    HttpUnitUtils.handleException(e);
×
1609
                    throw new RuntimeException(
×
UNCOV
1610
                            "Unable to autoload servlet: " + servletConfiguration.getClassName() + ": " + e);
×
1611
                }
1✔
1612
            }
1✔
1613
        }
1✔
1614

1615
        /**
1616
         * Collect auto loadable servlets.
1617
         *
1618
         * @param collection
1619
         *            the collection
1620
         * @param map
1621
         *            the map
1622
         */
1623
        private void collectAutoLoadableServlets(Collection collection, Map map) {
1624
            for (Object o : map.values()) {
1✔
1625
                if (o instanceof WebResourceMapping) {
1✔
1626
                    WebResourceMapping servletMapping = (WebResourceMapping) o;
1✔
1627
                    if (servletMapping.getConfiguration().isLoadOnStartup()) {
1✔
1628
                        collection.add(servletMapping.getConfiguration());
1✔
1629
                    }
1630
                } else {
1✔
1631
                    collectAutoLoadableServlets(collection, (Map) o);
1✔
1632
                }
1633
            }
1✔
1634
        }
1✔
1635

1636
        /**
1637
         * Gets the mapping.
1638
         *
1639
         * @param url
1640
         *            the url
1641
         *
1642
         * @return the mapping
1643
         */
1644
        private WebResourceMapping getMapping(String url) {
1645
            if (_exactMatches.containsKey(url)) {
1✔
1646
                return (WebResourceMapping) _exactMatches.get(url);
1✔
1647
            }
1648

1649
            Map context = getContextForLongestPathPrefix(url);
1✔
1650
            if (context.containsKey("*")) {
1✔
1651
                return (WebResourceMapping) context.get("*");
1✔
1652
            }
1653

1654
            if (_extensions.containsKey(getExtension(url))) {
1✔
1655
                return (WebResourceMapping) _extensions.get(getExtension(url));
1✔
1656
            }
1657

1658
            if (_urlTree.containsKey("/")) {
1!
UNCOV
1659
                return (WebResourceMapping) _urlTree.get("/");
×
1660
            }
1661

1662
            if (_defaultMapping != null) {
1✔
1663
                return _defaultMapping;
1✔
1664
            }
1665

1666
            final String prefix = "/servlet/";
1✔
1667
            if (!url.startsWith(prefix)) {
1✔
1668
                return null;
1✔
1669
            }
1670

1671
            String className = url.substring(prefix.length());
1✔
1672
            try {
1673
                Class.forName(className);
1✔
1674
                return new WebResourceMapping(new ServletConfiguration(className));
1✔
1675
            } catch (ClassNotFoundException e) {
×
UNCOV
1676
                return null;
×
1677
            }
1678
        }
1679

1680
        /**
1681
         * Gets the context for longest path prefix.
1682
         *
1683
         * @param url
1684
         *            the url
1685
         *
1686
         * @return the context for longest path prefix
1687
         */
1688
        private Map getContextForLongestPathPrefix(String url) {
1689
            Map context = _urlTree;
1✔
1690

1691
            ParsedPath path = new ParsedPath(url);
1✔
1692
            while (path.hasNext()) {
1✔
1693
                String part = path.next();
1✔
1694
                if (!context.containsKey(part)) {
1✔
1695
                    break;
1✔
1696
                }
1697
                context = (Map) context.get(part);
1✔
1698
            }
1✔
1699
            return context;
1✔
1700
        }
1701

1702
        /**
1703
         * Gets the extension.
1704
         *
1705
         * @param url
1706
         *            the url
1707
         *
1708
         * @return the extension
1709
         */
1710
        private String getExtension(String url) {
1711
            int index = url.lastIndexOf('.');
1✔
1712
            if (index == -1 || index >= url.length() - 1) {
1!
1713
                return "";
1✔
1714
            }
1715
            return url.substring(index + 1);
1✔
1716
        }
1717

1718
    }
1719

1720
    /**
1721
     * return the given ServletConfiguration for the given servlet name.
1722
     *
1723
     * @param servletName
1724
     *            the servlet name
1725
     *
1726
     * @return the corresponding ServletConfiguration
1727
     */
1728
    public ServletConfiguration getServletByName(String servletName) {
1729
        return (ServletConfiguration) _servlets.get(servletName);
1✔
1730
    }
1731

1732
}
1733

1734
/**
1735
 * A utility class for parsing URLs into paths
1736
 */
1737
class ParsedPath {
1738

1739
    private final String path;
1740
    private int position = 0;
1✔
1741
    static final char seperator_char = '/';
1742

1743
    /**
1744
     * Creates a new parsed path for the given path value
1745
     *
1746
     * @param path
1747
     *            the path
1748
     */
1749
    ParsedPath(String path) {
1✔
1750
        if (path.charAt(0) != seperator_char) {
1!
UNCOV
1751
            throw new IllegalArgumentException("Illegal path '" + path + "', does not begin with " + seperator_char);
×
1752
        }
1753
        this.path = path;
1✔
1754
    }
1✔
1755

1756
    /**
1757
     * Returns true if there are more parts left, otherwise false
1758
     */
1759
    public final boolean hasNext() {
1760
        return position < path.length();
1✔
1761
    }
1762

1763
    /**
1764
     * Returns the next part in the path
1765
     */
1766
    public final String next() {
1767
        int offset = position + 1;
1✔
1768
        while (offset < path.length() && path.charAt(offset) != seperator_char) {
1✔
1769
            offset++;
1✔
1770
        }
1771
        String result = path.substring(position + 1, offset);
1✔
1772
        position = offset;
1✔
1773
        return result;
1✔
1774
    }
1775

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

© 2026 Coveralls, Inc