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

hazendaz / httpunit / 755

14 Feb 2026 07:14PM UTC coverage: 80.526%. Remained the same
755

push

github

hazendaz
[ci] Fix badge

3213 of 4105 branches covered (78.27%)

Branch coverage included in aggregate %.

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
 * SPDX-License-Identifier: MIT
3
 * See LICENSE file for details.
4
 *
5
 * Copyright 2000-2026 Russell Gold
6
 * Copyright 2021-2000 hazendaz
7
 */
8
package com.meterware.servletunit;
9

10
import com.meterware.httpunit.HttpInternalErrorException;
11
import com.meterware.httpunit.HttpNotFoundException;
12
import com.meterware.httpunit.HttpUnitUtils;
13

14
import jakarta.servlet.*;
15
import jakarta.servlet.http.*;
16

17
import java.io.File;
18
import java.io.IOException;
19
import java.lang.reflect.InvocationTargetException;
20
import java.net.MalformedURLException;
21
import java.net.URL;
22
import java.nio.file.Path;
23
import java.util.ArrayList;
24
import java.util.Collection;
25
import java.util.Collections;
26
import java.util.Dictionary;
27
import java.util.HashMap;
28
import java.util.Hashtable;
29
import java.util.Iterator;
30
import java.util.List;
31
import java.util.ListIterator;
32
import java.util.Map;
33

34
import org.w3c.dom.Document;
35
import org.w3c.dom.Element;
36
import org.w3c.dom.NodeList;
37
import org.xml.sax.SAXException;
38

39
/**
40
 * This class represents the information recorded about a single web application. It is usually extracted from web.xml.
41
 **/
42
class WebApplication implements SessionListenerDispatcher {
43

44
    /** The Constant NULL_SECURITY_CONSTRAINT. */
45
    private static final SecurityConstraint NULL_SECURITY_CONSTRAINT = new NullSecurityConstraint();
1✔
46

47
    /** The security check configuration. */
48
    private final ServletConfiguration SECURITY_CHECK_CONFIGURATION = new ServletConfiguration(
1✔
49
            SecurityCheckServlet.class.getName());
1✔
50

51
    /** The security check mapping. */
52
    private final WebResourceMapping SECURITY_CHECK_MAPPING = new WebResourceMapping(SECURITY_CHECK_CONFIGURATION);
1✔
53

54
    /** A mapping of resource names to servlet configurations. **/
55
    private WebResourceMap _servletMapping = new WebResourceMap();
1✔
56

57
    /** A mapping of filter names to FilterConfigurations. */
58
    private Hashtable _filters = new Hashtable<>();
1✔
59

60
    /** A mapping of servlet names to ServletConfigurations. */
61
    private Hashtable _servlets = new Hashtable<>();
1✔
62

63
    /** A mapping of resource names to filter configurations. **/
64
    private FilterUrlMap _filterUrlMapping = new FilterUrlMap();
1✔
65

66
    /** A mapping of servlet names to filter configurations. **/
67
    private Hashtable _filterMapping = new Hashtable<>();
1✔
68

69
    /** The security constraints. */
70
    private List<SecurityConstraint> _securityConstraints = new ArrayList<>();
1✔
71

72
    /** The context listeners. */
73
    private List<ServletContextListener> _contextListeners = new ArrayList<>();
1✔
74

75
    /** The context attribute listeners. */
76
    private List<ServletContextAttributeListener> _contextAttributeListeners = new ArrayList<>();
1✔
77

78
    /** The session listeners. */
79
    private List<HttpSessionListener> _sessionListeners = new ArrayList<>();
1✔
80

81
    /** The session attribute listeners. */
82
    private List<HttpSessionAttributeListener> _sessionAttributeListeners = new ArrayList<>();
1✔
83

84
    /** The use basic authentication. */
85
    private boolean _useBasicAuthentication;
86

87
    /** The use form authentication. */
88
    private boolean _useFormAuthentication;
89

90
    /** The authentication realm. */
91
    private String _authenticationRealm = "";
1✔
92

93
    /** The login URL. */
94
    private URL _loginURL;
95

96
    /** The error URL. */
97
    private URL _errorURL;
98

99
    /** The context parameters. */
100
    private Hashtable _contextParameters = new Hashtable<>();
1✔
101

102
    /** The context dir. */
103
    private File _contextDir = null;
1✔
104

105
    /** The context path. */
106
    private String _contextPath = null;
1✔
107

108
    /** The servlet context. */
109
    private ServletUnitServletContext _servletContext;
110

111
    /** The display name. */
112
    private String _displayName;
113

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

121
    /**
122
     * Constructs an application spec from an XML document.
123
     *
124
     * @param document
125
     *            the document
126
     *
127
     * @throws MalformedURLException
128
     *             the malformed URL exception
129
     * @throws SAXException
130
     *             the SAX exception
131
     */
132
    WebApplication(Document document) throws MalformedURLException, SAXException {
133
        this(document, null, "");
1✔
134
    }
1✔
135

136
    /**
137
     * Constructs an application spec from an XML document.
138
     *
139
     * @param document
140
     *            the document
141
     * @param contextPath
142
     *            the context path
143
     *
144
     * @throws MalformedURLException
145
     *             the malformed URL exception
146
     * @throws SAXException
147
     *             the SAX exception
148
     */
149
    WebApplication(Document document, String contextPath) throws MalformedURLException, SAXException {
150
        this(document, null, contextPath);
1✔
151
    }
1✔
152

153
    /**
154
     * Constructs an application spec from an XML document.
155
     *
156
     * @param document
157
     *            the document
158
     * @param file
159
     *            the file
160
     * @param contextPath
161
     *            the context path
162
     *
163
     * @throws MalformedURLException
164
     *             the malformed URL exception
165
     * @throws SAXException
166
     *             the SAX exception
167
     */
168
    WebApplication(Document document, File file, String contextPath) throws MalformedURLException, SAXException {
1✔
169
        if (contextPath != null && !contextPath.isEmpty() && !contextPath.startsWith("/")) {
1!
170
            throw new IllegalArgumentException("Context path " + contextPath + " must start with '/'");
×
171
        }
172
        _contextDir = file;
1✔
173
        _contextPath = contextPath == null ? "" : contextPath;
1✔
174
        NodeList nl = document.getElementsByTagName("display-name");
1✔
175
        if (nl.getLength() > 0) {
1✔
176
            _displayName = XMLUtils.getTextValue(nl.item(0)).trim();
1✔
177
        }
178

179
        registerServlets(document);
1✔
180
        registerFilters(document);
1✔
181
        extractSecurityConstraints(document);
1✔
182
        extractContextParameters(document);
1✔
183
        extractLoginConfiguration(document);
1✔
184
        extractListeners(document);
1✔
185
        notifyContextInitialized();
1✔
186
        _servletMapping.autoLoadServlets();
1✔
187
    }
1✔
188

189
    /**
190
     * Extract listeners.
191
     *
192
     * @param document
193
     *            the document
194
     *
195
     * @throws SAXException
196
     *             the SAX exception
197
     */
198
    private void extractListeners(Document document) throws SAXException {
199
        NodeList nl = document.getElementsByTagName("listener");
1✔
200
        for (int i = 0; i < nl.getLength(); i++) {
1✔
201
            String listenerName = XMLUtils.getChildNodeValue((Element) nl.item(i), "listener-class").trim();
1✔
202
            try {
203
                Object listener = Class.forName(listenerName).getDeclaredConstructor().newInstance();
1✔
204

205
                if (listener instanceof ServletContextListener) {
1✔
206
                    _contextListeners.add((ServletContextListener) listener);
1✔
207
                }
208
                if (listener instanceof ServletContextAttributeListener) {
1✔
209
                    _contextAttributeListeners.add((ServletContextAttributeListener) listener);
1✔
210
                }
211
                if (listener instanceof HttpSessionListener) {
1✔
212
                    _sessionListeners.add((HttpSessionListener) listener);
1✔
213
                }
214
                if (listener instanceof HttpSessionAttributeListener) {
1✔
215
                    _sessionAttributeListeners.add((HttpSessionAttributeListener) listener);
1✔
216
                }
217
            } catch (Throwable e) {
×
218
                throw new RuntimeException("Unable to load context listener " + listenerName + ": " + e.toString());
×
219
            }
1✔
220
        }
221
    }
1✔
222

223
    /**
224
     * Notify context initialized.
225
     */
226
    private void notifyContextInitialized() {
227
        ServletContextEvent event = new ServletContextEvent(getServletContext());
1✔
228

229
        for (Iterator<ServletContextListener> i = _contextListeners.iterator(); i.hasNext();) {
1✔
230
            ServletContextListener listener = i.next();
1✔
231
            listener.contextInitialized(event);
1✔
232
        }
1✔
233
    }
1✔
234

235
    /**
236
     * Shut down.
237
     */
238
    void shutDown() {
239
        destroyServlets();
1✔
240
        notifyContextDestroyed();
1✔
241
    }
1✔
242

243
    /**
244
     * Notify context destroyed.
245
     */
246
    private void notifyContextDestroyed() {
247
        ServletContextEvent event = new ServletContextEvent(getServletContext());
1✔
248

249
        for (ListIterator<ServletContextListener> i = _contextListeners.listIterator(_contextListeners.size()); i
1✔
250
                .hasPrevious();) {
1✔
251
            ServletContextListener listener = i.previous();
1✔
252
            listener.contextDestroyed(event);
1✔
253
        }
1✔
254
    }
1✔
255

256
    /**
257
     * Send attribute added.
258
     *
259
     * @param name
260
     *            the name
261
     * @param value
262
     *            the value
263
     */
264
    void sendAttributeAdded(String name, Object value) {
265
        ServletContextAttributeEvent event = new ServletContextAttributeEvent(getServletContext(), name, value);
1✔
266

267
        for (Iterator<ServletContextAttributeListener> i = _contextAttributeListeners.iterator(); i.hasNext();) {
1✔
268
            ServletContextAttributeListener listener = i.next();
1✔
269
            listener.attributeAdded(event);
1✔
270
        }
1✔
271
    }
1✔
272

273
    /**
274
     * Send attribute replaced.
275
     *
276
     * @param name
277
     *            the name
278
     * @param value
279
     *            the value
280
     */
281
    void sendAttributeReplaced(String name, Object value) {
282
        ServletContextAttributeEvent event = new ServletContextAttributeEvent(getServletContext(), name, value);
1✔
283

284
        for (Iterator<ServletContextAttributeListener> i = _contextAttributeListeners.iterator(); i.hasNext();) {
1✔
285
            ServletContextAttributeListener listener = i.next();
1✔
286
            listener.attributeReplaced(event);
1✔
287
        }
1✔
288
    }
1✔
289

290
    /**
291
     * Send attribute removed.
292
     *
293
     * @param name
294
     *            the name
295
     * @param value
296
     *            the value
297
     */
298
    void sendAttributeRemoved(String name, Object value) {
299
        ServletContextAttributeEvent event = new ServletContextAttributeEvent(getServletContext(), name, value);
1✔
300

301
        for (Iterator<ServletContextAttributeListener> i = _contextAttributeListeners.iterator(); i.hasNext();) {
1✔
302
            ServletContextAttributeListener listener = i.next();
1✔
303
            listener.attributeRemoved(event);
1✔
304
        }
1✔
305
    }
1✔
306

307
    /**
308
     * Extract security constraints.
309
     *
310
     * @param document
311
     *            the document
312
     *
313
     * @throws SAXException
314
     *             the SAX exception
315
     */
316
    private void extractSecurityConstraints(Document document) throws SAXException {
317
        NodeList nl = document.getElementsByTagName("security-constraint");
1✔
318
        for (int i = 0; i < nl.getLength(); i++) {
1✔
319
            _securityConstraints.add(new SecurityConstraintImpl((Element) nl.item(i)));
1✔
320
        }
321
    }
1✔
322

323
    /**
324
     * Gets the context path.
325
     *
326
     * @return the context path
327
     */
328
    String getContextPath() {
329
        return _contextPath;
1✔
330
    }
331

332
    /**
333
     * Gets the servlet context.
334
     *
335
     * @return the servlet context
336
     */
337
    ServletContext getServletContext() {
338
        if (_servletContext == null) {
1✔
339
            _servletContext = new ServletUnitServletContext(this);
1✔
340
        }
341
        return _servletContext;
1✔
342
    }
343

344
    /**
345
     * Registers a servlet class to be run.
346
     *
347
     * @param resourceName
348
     *            the resource name
349
     * @param servletClassName
350
     *            the servlet class name
351
     * @param initParams
352
     *            the init params
353
     */
354
    void registerServlet(String resourceName, String servletClassName, Hashtable initParams) {
355
        registerServlet(resourceName, new ServletConfiguration(servletClassName, initParams));
1✔
356
    }
1✔
357

358
    /**
359
     * Registers a servlet to be run.
360
     *
361
     * @param resourceName
362
     *            the resource name
363
     * @param servletConfiguration
364
     *            the servlet configuration
365
     */
366
    void registerServlet(String resourceName, ServletConfiguration servletConfiguration) {
367
        // FIXME - shouldn't everything start with one or the other?
368
        if (!resourceName.startsWith("/") && !resourceName.startsWith("*")) {
1✔
369
            resourceName = "/" + resourceName;
1✔
370
        }
371
        _servletMapping.put(resourceName, servletConfiguration);
1✔
372
    }
1✔
373

374
    /**
375
     * Calls the destroy method for every active servlet.
376
     */
377
    void destroyServlets() {
378
        _servletMapping.destroyWebResources();
1✔
379
    }
1✔
380

381
    /**
382
     * Gets the servlet request.
383
     *
384
     * @param url
385
     *            the url
386
     *
387
     * @return the servlet request
388
     */
389
    ServletMetaData getServletRequest(URL url) {
390
        return _servletMapping.get(url);
1✔
391
    }
392

393
    /**
394
     * Returns true if this application uses Basic Authentication.
395
     *
396
     * @return true, if successful
397
     */
398
    boolean usesBasicAuthentication() {
399
        return _useBasicAuthentication;
1✔
400
    }
401

402
    /**
403
     * Returns true if this application uses form-based authentication.
404
     *
405
     * @return true, if successful
406
     */
407
    boolean usesFormAuthentication() {
408
        return _useFormAuthentication;
1✔
409
    }
410

411
    /**
412
     * Gets the authentication realm.
413
     *
414
     * @return the authentication realm
415
     */
416
    String getAuthenticationRealm() {
417
        return _authenticationRealm;
1✔
418
    }
419

420
    /**
421
     * Gets the login URL.
422
     *
423
     * @return the login URL
424
     */
425
    URL getLoginURL() {
426
        return _loginURL;
1✔
427
    }
428

429
    /**
430
     * Gets the error URL.
431
     *
432
     * @return the error URL
433
     */
434
    URL getErrorURL() {
435
        return _errorURL;
1✔
436
    }
437

438
    /**
439
     * Returns true if the specified path may only be accesses by an authorized user.
440
     *
441
     * @param url
442
     *            the application-relative path of the URL
443
     *
444
     * @return true, if successful
445
     */
446
    boolean requiresAuthorization(URL url) {
447
        String result;
448
        String file = url.getFile();
1✔
449
        if (_contextPath.equals("")) {
1✔
450
            result = file;
1✔
451
        } else if (file.startsWith(_contextPath)) {
1!
452
            result = file.substring(_contextPath.length());
1✔
453
        } else {
454
            result = null;
×
455
        }
456
        return getControllingConstraint(result) != NULL_SECURITY_CONSTRAINT;
1✔
457
    }
458

459
    /**
460
     * Returns an array containing the roles permitted to access the specified URL.
461
     *
462
     * @param url
463
     *            the url
464
     *
465
     * @return the permitted roles
466
     */
467
    String[] getPermittedRoles(URL url) {
468
        String result;
469
        String file = url.getFile();
1✔
470
        if (_contextPath.equals("")) {
1✔
471
            result = file;
1✔
472
        } else if (file.startsWith(_contextPath)) {
1!
473
            result = file.substring(_contextPath.length());
1✔
474
        } else {
475
            result = null;
×
476
        }
477
        return getControllingConstraint(result).getPermittedRoles();
1✔
478
    }
479

480
    /**
481
     * Gets the controlling constraint.
482
     *
483
     * @param urlPath
484
     *            the url path
485
     *
486
     * @return the controlling constraint
487
     */
488
    private SecurityConstraint getControllingConstraint(String urlPath) {
489
        for (SecurityConstraint sc : _securityConstraints) {
1✔
490
            if (sc.controlsPath(urlPath)) {
1✔
491
                return sc;
1✔
492
            }
493
        }
1✔
494
        return NULL_SECURITY_CONSTRAINT;
1✔
495
    }
496

497
    /**
498
     * Gets the resource file.
499
     *
500
     * @param path
501
     *            the path
502
     *
503
     * @return the resource file
504
     */
505
    File getResourceFile(String path) {
506
        String relativePath = path.startsWith("/") ? path.substring(1) : path;
1✔
507
        if (_contextDir == null) {
1✔
508
            return Path.of(relativePath).toFile();
1✔
509
        }
510
        return _contextDir.toPath().resolve(relativePath).toFile();
1✔
511
    }
512

513
    /**
514
     * Gets the context parameters.
515
     *
516
     * @return the context parameters
517
     */
518
    Hashtable getContextParameters() {
519
        return _contextParameters;
1✔
520
    }
521

522
    // ---------------------------------------- SessionListenerDispatcher methods
523
    // -------------------------------------------
524

525
    @Override
526
    public void sendSessionCreated(HttpSession session) {
527
        HttpSessionEvent event = new HttpSessionEvent(session);
1✔
528

529
        for (HttpSessionListener listener : _sessionListeners) {
1✔
530
            listener.sessionCreated(event);
1✔
531
        }
1✔
532
    }
1✔
533

534
    @Override
535
    public void sendSessionDestroyed(HttpSession session) {
536
        HttpSessionEvent event = new HttpSessionEvent(session);
1✔
537

538
        for (HttpSessionListener listener : _sessionListeners) {
1✔
539
            listener.sessionDestroyed(event);
1✔
540
        }
1✔
541
    }
1✔
542

543
    @Override
544
    public void sendAttributeAdded(HttpSession session, String name, Object value) {
545
        HttpSessionBindingEvent event = new HttpSessionBindingEvent(session, name, value);
1✔
546

547
        for (HttpSessionAttributeListener listener : _sessionAttributeListeners) {
1✔
548
            listener.attributeAdded(event);
1✔
549
        }
1✔
550
    }
1✔
551

552
    @Override
553
    public void sendAttributeReplaced(HttpSession session, String name, Object oldValue) {
554
        HttpSessionBindingEvent event = new HttpSessionBindingEvent(session, name, oldValue);
1✔
555

556
        for (HttpSessionAttributeListener listener : _sessionAttributeListeners) {
1✔
557
            listener.attributeReplaced(event);
1✔
558
        }
1✔
559
    }
1✔
560

561
    @Override
562
    public void sendAttributeRemoved(HttpSession session, String name, Object oldValue) {
563
        HttpSessionBindingEvent event = new HttpSessionBindingEvent(session, name, oldValue);
1✔
564

565
        for (HttpSessionAttributeListener listener : _sessionAttributeListeners) {
1✔
566
            listener.attributeRemoved(event);
1✔
567
        }
1✔
568
    }
1✔
569

570
    // --------------------------------------------------- private members
571
    // --------------------------------------------------
572

573
    /**
574
     * Register filters.
575
     *
576
     * @param document
577
     *            the document
578
     *
579
     * @throws SAXException
580
     *             the SAX exception
581
     */
582
    private void registerFilters(Document document) throws SAXException {
583
        Hashtable nameToClass = new Hashtable<>();
1✔
584
        NodeList nl = document.getElementsByTagName("filter");
1✔
585
        for (int i = 0; i < nl.getLength(); i++) {
1✔
586
            registerFilterClass(nameToClass, (Element) nl.item(i));
1✔
587
        }
588
        nl = document.getElementsByTagName("filter-mapping");
1✔
589
        for (int i = 0; i < nl.getLength(); i++) {
1✔
590
            registerFilter(nameToClass, (Element) nl.item(i));
1✔
591
        }
592
        this._filters = nameToClass;
1✔
593
    }
1✔
594

595
    /**
596
     * Register filter class.
597
     *
598
     * @param mapping
599
     *            the mapping
600
     * @param filterElement
601
     *            the filter element
602
     *
603
     * @throws SAXException
604
     *             the SAX exception
605
     */
606
    private void registerFilterClass(Dictionary mapping, Element filterElement) throws SAXException {
607
        String filterName = XMLUtils.getChildNodeValue(filterElement, "filter-name");
1✔
608
        mapping.put(filterName, new FilterConfiguration(filterName, filterElement));
1✔
609
    }
1✔
610

611
    /**
612
     * Register filter.
613
     *
614
     * @param mapping
615
     *            the mapping
616
     * @param filterElement
617
     *            the filter element
618
     *
619
     * @throws SAXException
620
     *             the SAX exception
621
     */
622
    private void registerFilter(Dictionary mapping, Element filterElement) throws SAXException {
623
        if (XMLUtils.hasChildNode(filterElement, "servlet-name")) {
1✔
624
            registerFilterForServlet(XMLUtils.getChildNodeValue(filterElement, "servlet-name"),
1✔
625
                    (FilterConfiguration) mapping.get(XMLUtils.getChildNodeValue(filterElement, "filter-name")));
1✔
626
        }
627
        if (XMLUtils.hasChildNode(filterElement, "url-pattern")) {
1✔
628
            registerFilterForUrl(XMLUtils.getChildNodeValue(filterElement, "url-pattern"),
1✔
629
                    (FilterConfiguration) mapping.get(XMLUtils.getChildNodeValue(filterElement, "filter-name")));
1✔
630
        }
631
    }
1✔
632

633
    /**
634
     * Register filter for url.
635
     *
636
     * @param resourceName
637
     *            the resource name
638
     * @param filterConfiguration
639
     *            the filter configuration
640
     */
641
    private void registerFilterForUrl(String resourceName, FilterConfiguration filterConfiguration) {
642
        _filterUrlMapping.put(resourceName, filterConfiguration);
1✔
643
    }
1✔
644

645
    /**
646
     * Register filter for servlet.
647
     *
648
     * @param servletName
649
     *            the servlet name
650
     * @param filterConfiguration
651
     *            the filter configuration
652
     */
653
    private void registerFilterForServlet(String servletName, FilterConfiguration filterConfiguration) {
654
        List list = (List) _filterMapping.get(servletName);
1✔
655
        if (list == null) {
1✔
656
            list = new ArrayList<>();
1✔
657
            _filterMapping.put(servletName, list);
1✔
658
        }
659
        list.add(filterConfiguration);
1✔
660
    }
1✔
661

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

697
    /**
698
     * Register servlets.
699
     *
700
     * @param document
701
     *            the document
702
     *
703
     * @throws SAXException
704
     *             the SAX exception
705
     */
706
    private void registerServlets(Document document) throws SAXException {
707
        Hashtable nameToClass = new Hashtable<>();
1✔
708
        NodeList nl = document.getElementsByTagName("servlet");
1✔
709
        for (int i = 0; i < nl.getLength(); i++) {
1✔
710
            registerServletClass(nameToClass, (Element) nl.item(i));
1✔
711
        }
712
        nl = document.getElementsByTagName("servlet-mapping");
1✔
713
        for (int i = 0; i < nl.getLength(); i++) {
1✔
714
            registerServlet(nameToClass, (Element) nl.item(i));
1✔
715
        }
716
        this._servlets = nameToClass;
1✔
717
    }
1✔
718

719
    /**
720
     * Register servlet class.
721
     *
722
     * @param mapping
723
     *            the mapping
724
     * @param servletElement
725
     *            the servlet element
726
     *
727
     * @throws SAXException
728
     *             the SAX exception
729
     */
730
    private void registerServletClass(Dictionary mapping, Element servletElement) throws SAXException {
731
        mapping.put(XMLUtils.getChildNodeValue(servletElement, "servlet-name"),
1✔
732
                new ServletConfiguration(servletElement));
733
    }
1✔
734

735
    /**
736
     * Register servlet.
737
     *
738
     * @param mapping
739
     *            the mapping
740
     * @param servletElement
741
     *            the servlet element
742
     *
743
     * @throws SAXException
744
     *             the SAX exception
745
     */
746
    private void registerServlet(Dictionary mapping, Element servletElement) throws SAXException {
747
        registerServlet(XMLUtils.getChildNodeValue(servletElement, "url-pattern"),
1✔
748
                (ServletConfiguration) mapping.get(XMLUtils.getChildNodeValue(servletElement, "servlet-name")));
1✔
749
    }
1✔
750

751
    /**
752
     * Extract context parameters.
753
     *
754
     * @param document
755
     *            the document
756
     *
757
     * @throws SAXException
758
     *             the SAX exception
759
     */
760
    private void extractContextParameters(Document document) throws SAXException {
761
        NodeList nl = document.getElementsByTagName("context-param");
1✔
762
        for (int i = 0; i < nl.getLength(); i++) {
1✔
763
            Element param = (Element) nl.item(i);
1✔
764
            String name = XMLUtils.getChildNodeValue(param, "param-name");
1✔
765
            String value = XMLUtils.getChildNodeValue(param, "param-value");
1✔
766
            _contextParameters.put(name, value);
1✔
767
        }
768
    }
1✔
769

770
    /**
771
     * Pattern matches.
772
     *
773
     * @param urlPattern
774
     *            the url pattern
775
     * @param urlPath
776
     *            the url path
777
     *
778
     * @return true, if successful
779
     */
780
    private static boolean patternMatches(String urlPattern, String urlPath) {
781
        return urlPattern.equals(urlPath);
1✔
782
    }
783

784
    /**
785
     * Gets the display name.
786
     *
787
     * @return the display name
788
     */
789
    String getDisplayName() {
790
        return _displayName;
1✔
791
    }
792

793
    // ============================================= SecurityCheckServlet class
794
    // =============================================
795

796
    /**
797
     * The Class SecurityCheckServlet.
798
     */
799
    static class SecurityCheckServlet extends HttpServlet {
1✔
800

801
        /** The Constant serialVersionUID. */
802
        private static final long serialVersionUID = 1L;
803

804
        @Override
805
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
806
            handleLogin(req, resp);
×
807
        }
×
808

809
        @Override
810
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
811
            handleLogin(req, resp);
1✔
812
        }
1✔
813

814
        /**
815
         * Handle login.
816
         *
817
         * @param req
818
         *            the req
819
         * @param resp
820
         *            the resp
821
         *
822
         * @throws IOException
823
         *             Signals that an I/O exception has occurred.
824
         */
825
        private void handleLogin(HttpServletRequest req, HttpServletResponse resp) throws IOException {
826
            final String username = req.getParameter("j_username");
1✔
827
            final String roleList = req.getParameter("j_password");
1✔
828
            getServletSession(req).setUserInformation(username, ServletUnitHttpRequest.toArray(roleList));
1✔
829
            resp.sendRedirect(getServletSession(req).getOriginalURL().toExternalForm());
1✔
830
        }
1✔
831

832
        /**
833
         * Gets the servlet session.
834
         *
835
         * @param req
836
         *            the req
837
         *
838
         * @return the servlet session
839
         */
840
        private ServletUnitHttpSession getServletSession(HttpServletRequest req) {
841
            return (ServletUnitHttpSession) req.getSession();
1✔
842
        }
843

844
    }
845

846
    // ============================================= ServletConfiguration class
847
    // =============================================
848

849
    /** The Constant DONT_AUTOLOAD. */
850
    static final int DONT_AUTOLOAD = Integer.MIN_VALUE;
851

852
    /** The Constant ANY_LOAD_ORDER. */
853
    static final int ANY_LOAD_ORDER = Integer.MAX_VALUE;
854

855
    /**
856
     * The Class ServletConfiguration.
857
     */
858
    class ServletConfiguration extends WebResourceConfiguration {
859

860
        /** The servlet. */
861
        private Servlet _servlet;
862

863
        /** The servlet name. */
864
        private String _servletName;
865

866
        /** The jsp file. */
867
        private String _jspFile;
868

869
        /** The load order. */
870
        private int _loadOrder = DONT_AUTOLOAD;
1✔
871

872
        /**
873
         * Instantiates a new servlet configuration.
874
         *
875
         * @param className
876
         *            the class name
877
         */
878
        ServletConfiguration(String className) {
1✔
879
            super(className);
1✔
880
        }
1✔
881

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

894
        /**
895
         * Instantiates a new servlet configuration.
896
         *
897
         * @param servletElement
898
         *            the servlet element
899
         *
900
         * @throws SAXException
901
         *             the SAX exception
902
         */
903
        ServletConfiguration(Element servletElement) throws SAXException {
1✔
904
            super(servletElement, "servlet-class", XMLUtils.getChildNodeValue(servletElement, "servlet-class",
1✔
905
                    "org.apache.jasper.servlet.JspServlet"));
906
            _servletName = XMLUtils.getChildNodeValue(servletElement, "servlet-name");
1✔
907
            _jspFile = XMLUtils.getChildNodeValue(servletElement, "jsp-file", "");
1✔
908
            if ("".equals(_jspFile)) {
1!
909
                _jspFile = null;
1✔
910
            }
911
            final NodeList loadOrder = servletElement.getElementsByTagName("load-on-startup");
1✔
912
            for (int i = 0; i < loadOrder.getLength(); i++) {
1✔
913
                String order = XMLUtils.getTextValue(loadOrder.item(i));
1✔
914
                try {
915
                    _loadOrder = Integer.parseInt(order);
1✔
916
                } catch (NumberFormatException e) {
1✔
917
                    _loadOrder = ANY_LOAD_ORDER;
1✔
918
                }
1✔
919
            }
920
        }
1✔
921

922
        /**
923
         * Gets the servlet.
924
         *
925
         * @return the servlet
926
         *
927
         * @throws ClassNotFoundException
928
         *             the class not found exception
929
         * @throws InstantiationException
930
         *             the instantiation exception
931
         * @throws IllegalAccessException
932
         *             the illegal access exception
933
         * @throws ServletException
934
         *             the servlet exception
935
         * @throws IllegalArgumentException
936
         *             the illegal argument exception
937
         * @throws InvocationTargetException
938
         *             the invocation target exception
939
         * @throws NoSuchMethodException
940
         *             the no such method exception
941
         * @throws SecurityException
942
         *             the security exception
943
         */
944
        synchronized Servlet getServlet()
945
                throws ClassNotFoundException, InstantiationException, IllegalAccessException, ServletException,
946
                IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
947
            if (_servlet == null) {
1✔
948
                Class servletClass = Class.forName(getClassName());
1✔
949
                _servlet = (Servlet) servletClass.getDeclaredConstructor().newInstance();
1✔
950
                String servletName = _servletName != null ? _servletName : _servlet.getClass().getName();
1✔
951
                _servlet.init(new ServletUnitServletConfig(servletName, WebApplication.this, getInitParams()));
1✔
952
            }
953

954
            return _servlet;
1✔
955
        }
956

957
        @Override
958
        synchronized void destroyResource() {
959
            if (_servlet != null) {
1✔
960
                _servlet.destroy();
1✔
961
            }
962
        }
1✔
963

964
        /**
965
         * Gets the servlet name.
966
         *
967
         * @return the servlet name
968
         */
969
        String getServletName() {
970
            return _servletName;
1✔
971
        }
972

973
        @Override
974
        boolean isLoadOnStartup() {
975
            return _loadOrder != DONT_AUTOLOAD;
1✔
976
        }
977

978
        /**
979
         * Gets the load order.
980
         *
981
         * @return the load order
982
         */
983
        public int getLoadOrder() {
984
            return _loadOrder;
1✔
985
        }
986

987
        /**
988
         * Gets the jsp file.
989
         *
990
         * @return the jsp file
991
         */
992
        public Object getJspFile() {
993
            return this._jspFile;
×
994
        }
995
    }
996

997
    // ============================================= FilterConfiguration class
998
    // =============================================
999

1000
    /**
1001
     * The Class FilterConfiguration.
1002
     */
1003
    class FilterConfiguration extends WebResourceConfiguration implements FilterMetaData {
1004

1005
        /** The filter. */
1006
        private Filter _filter;
1007

1008
        /** The name. */
1009
        private String _name;
1010

1011
        /**
1012
         * Instantiates a new filter configuration.
1013
         *
1014
         * @param name
1015
         *            the name
1016
         * @param filterElement
1017
         *            the filter element
1018
         *
1019
         * @throws SAXException
1020
         *             the SAX exception
1021
         */
1022
        FilterConfiguration(String name, Element filterElement) throws SAXException {
1✔
1023
            super(filterElement, "filter-class");
1✔
1024
            _name = name;
1✔
1025
        }
1✔
1026

1027
        @Override
1028
        public synchronized Filter getFilter() throws ServletException {
1029
            try {
1030
                if (_filter == null) {
1✔
1031
                    Class filterClass = Class.forName(getClassName());
1✔
1032
                    _filter = (Filter) filterClass.getDeclaredConstructor().newInstance();
1✔
1033
                    _filter.init(new FilterConfigImpl(_name, getServletContext(), getInitParams()));
1✔
1034
                }
1035

1036
                return _filter;
1✔
1037
            } catch (ClassNotFoundException e) {
×
1038
                throw new ServletException("Did not find filter class: " + getClassName());
×
1039
            } catch (IllegalAccessException e) {
×
1040
                throw new ServletException("Filter class " + getClassName() + " lacks a public no-arg constructor");
×
1041
            } catch (InstantiationException | IllegalArgumentException | InvocationTargetException
×
1042
                    | NoSuchMethodException | SecurityException e) {
1043
                throw new ServletException("Filter class " + getClassName() + " could not be instantiated.");
×
1044
            } catch (ClassCastException e) {
×
1045
                throw new ServletException(
×
1046
                        "Filter class " + getClassName() + " does not implement" + Filter.class.getName());
×
1047
            }
1048
        }
1049

1050
        @Override
1051
        boolean isLoadOnStartup() {
1052
            return false;
×
1053
        }
1054

1055
        @Override
1056
        synchronized void destroyResource() {
1057
            if (_filter != null) {
×
1058
                _filter.destroy();
×
1059
            }
1060
        }
×
1061
    }
1062

1063
    // =================================== SecurityConstract interface and implementations
1064
    // ==================================
1065

1066
    /**
1067
     * The Interface SecurityConstraint.
1068
     */
1069
    interface SecurityConstraint {
1070

1071
        /**
1072
         * Controls path.
1073
         *
1074
         * @param urlPath
1075
         *            the url path
1076
         *
1077
         * @return true, if successful
1078
         */
1079
        boolean controlsPath(String urlPath);
1080

1081
        /**
1082
         * Gets the permitted roles.
1083
         *
1084
         * @return the permitted roles
1085
         */
1086
        String[] getPermittedRoles();
1087
    }
1088

1089
    /**
1090
     * The Class NullSecurityConstraint.
1091
     */
1092
    static class NullSecurityConstraint implements SecurityConstraint {
1✔
1093

1094
        /** The Constant NO_ROLES. */
1095
        private static final String[] NO_ROLES = {};
1✔
1096

1097
        @Override
1098
        public boolean controlsPath(String urlPath) {
1099
            return false;
×
1100
        }
1101

1102
        @Override
1103
        public String[] getPermittedRoles() {
1104
            return NO_ROLES;
×
1105
        }
1106
    }
1107

1108
    /**
1109
     * The Class SecurityConstraintImpl.
1110
     */
1111
    static class SecurityConstraintImpl implements SecurityConstraint {
1112

1113
        /**
1114
         * Instantiates a new security constraint impl.
1115
         *
1116
         * @param root
1117
         *            the root
1118
         *
1119
         * @throws SAXException
1120
         *             the SAX exception
1121
         */
1122
        SecurityConstraintImpl(Element root) throws SAXException {
1✔
1123
            final NodeList roleNames = root.getElementsByTagName("role-name");
1✔
1124
            for (int i = 0; i < roleNames.getLength(); i++) {
1✔
1125
                _roleList.add(XMLUtils.getTextValue(roleNames.item(i)));
1✔
1126
            }
1127

1128
            final NodeList resources = root.getElementsByTagName("web-resource-collection");
1✔
1129
            for (int i = 0; i < resources.getLength(); i++) {
1✔
1130
                _resources.add(new WebResourceCollection((Element) resources.item(i)));
1✔
1131
            }
1132
        }
1✔
1133

1134
        @Override
1135
        public boolean controlsPath(String urlPath) {
1136
            return getMatchingCollection(urlPath) != null;
1✔
1137
        }
1138

1139
        @Override
1140
        public String[] getPermittedRoles() {
1141
            if (_roles == null) {
1✔
1142
                _roles = _roleList.toArray(new String[_roleList.size()]);
1✔
1143
            }
1144
            return _roles;
1✔
1145
        }
1146

1147
        /** The roles. */
1148
        private String[] _roles;
1149

1150
        /** The role list. */
1151
        private List<String> _roleList = new ArrayList<>();
1✔
1152

1153
        /** The resources. */
1154
        private List<WebResourceCollection> _resources = new ArrayList<>();
1✔
1155

1156
        /**
1157
         * Gets the matching collection.
1158
         *
1159
         * @param urlPath
1160
         *            the url path
1161
         *
1162
         * @return the matching collection
1163
         */
1164
        public WebResourceCollection getMatchingCollection(String urlPath) {
1165
            for (WebResourceCollection wrc : _resources) {
1✔
1166
                if (wrc.controlsPath(urlPath)) {
1✔
1167
                    return wrc;
1✔
1168
                }
1169
            }
1✔
1170
            return null;
1✔
1171
        }
1172

1173
        /**
1174
         * The Class WebResourceCollection.
1175
         */
1176
        class WebResourceCollection {
1177

1178
            /**
1179
             * Instantiates a new web resource collection.
1180
             *
1181
             * @param root
1182
             *            the root
1183
             *
1184
             * @throws SAXException
1185
             *             the SAX exception
1186
             */
1187
            WebResourceCollection(Element root) throws SAXException {
1✔
1188
                final NodeList urlPatterns = root.getElementsByTagName("url-pattern");
1✔
1189
                for (int i = 0; i < urlPatterns.getLength(); i++) {
1✔
1190
                    _urlPatterns.add(XMLUtils.getTextValue(urlPatterns.item(i)));
1✔
1191
                }
1192
            }
1✔
1193

1194
            /**
1195
             * Controls path.
1196
             *
1197
             * @param urlPath
1198
             *            the url path
1199
             *
1200
             * @return true, if successful
1201
             */
1202
            boolean controlsPath(String urlPath) {
1203
                for (String pattern : _urlPatterns) {
1✔
1204
                    if (patternMatches(pattern, urlPath)) {
1✔
1205
                        return true;
1✔
1206
                    }
1207
                }
1✔
1208
                return false;
1✔
1209
            }
1210

1211
            /** The url patterns. */
1212
            private List<String> _urlPatterns = new ArrayList<>();
1✔
1213
        }
1214
    }
1215

1216
    /** The Constant NO_FILTERS. */
1217
    static final FilterMetaData[] NO_FILTERS = {};
1✔
1218

1219
    /**
1220
     * The Class ServletRequestImpl.
1221
     */
1222
    static class ServletRequestImpl implements ServletMetaData {
1223

1224
        /** The url. */
1225
        private URL _url;
1226

1227
        /** The full servlet path. */
1228
        private String _fullServletPath;
1229

1230
        /** The mapping. */
1231
        private WebResourceMapping _mapping;
1232

1233
        /** The filters per name. */
1234
        private Hashtable _filtersPerName;
1235

1236
        /** The filters per url. */
1237
        private FilterUrlMap _filtersPerUrl;
1238

1239
        /**
1240
         * Instantiates a new servlet request impl.
1241
         *
1242
         * @param url
1243
         *            the url
1244
         * @param servletPath
1245
         *            the servlet path
1246
         * @param mapping
1247
         *            the mapping
1248
         * @param filtersPerName
1249
         *            the filters per name
1250
         * @param filtersPerUrl
1251
         *            the filters per url
1252
         */
1253
        ServletRequestImpl(URL url, String servletPath, WebResourceMapping mapping, Hashtable filtersPerName,
1254
                FilterUrlMap filtersPerUrl) {
1✔
1255
            _url = url;
1✔
1256
            _fullServletPath = servletPath;
1✔
1257
            _mapping = mapping;
1✔
1258
            _filtersPerName = filtersPerName;
1✔
1259
            _filtersPerUrl = filtersPerUrl;
1✔
1260
        }
1✔
1261

1262
        /**
1263
         * get the Servlet
1264
         *
1265
         * @return the Servlet from the configuration
1266
         *
1267
         * @throws ServletException
1268
         *             - e.g. if no configuration is available
1269
         */
1270
        @Override
1271
        public Servlet getServlet() throws ServletException {
1272
            if (getConfiguration() == null) {
1✔
1273
                throw new HttpNotFoundException("No servlet mapping defined", _url);
1✔
1274
            }
1275

1276
            try {
1277
                return getConfiguration().getServlet();
1✔
1278
            } catch (ClassNotFoundException e) {
×
1279
                throw new HttpNotFoundException(_url, e);
×
1280
            } catch (IllegalAccessException | InstantiationException | IllegalArgumentException
×
1281
                    | InvocationTargetException | NoSuchMethodException | SecurityException e) {
1282
                throw new HttpInternalErrorException(_url, e);
×
1283
            }
1284
        }
1285

1286
        /**
1287
         * get the ServletPath the decoded ServletPath
1288
         */
1289
        @Override
1290
        public String getServletPath() {
1291
            return _mapping == null ? null : HttpUnitUtils.decode(_mapping.getServletPath(_fullServletPath));
1!
1292
        }
1293

1294
        /**
1295
         * get the Path Information
1296
         *
1297
         * @return the decode path
1298
         */
1299
        @Override
1300
        public String getPathInfo() {
1301
            return _mapping == null ? null : HttpUnitUtils.decode(_mapping.getPathInfo(_fullServletPath));
1!
1302
        }
1303

1304
        @Override
1305
        public FilterMetaData[] getFilters() {
1306
            if (getConfiguration() == null) {
1✔
1307
                return NO_FILTERS;
1✔
1308
            }
1309

1310
            List<FilterMetaData> filters = new ArrayList<>();
1✔
1311
            addFiltersForPath(filters, _fullServletPath);
1✔
1312
            addFiltersForServletWithName(filters, getConfiguration().getServletName());
1✔
1313

1314
            return filters.toArray(new FilterMetaData[filters.size()]);
1✔
1315
        }
1316

1317
        /**
1318
         * Adds the filters for path.
1319
         *
1320
         * @param filters
1321
         *            the filters
1322
         * @param fullServletPath
1323
         *            the full servlet path
1324
         */
1325
        private void addFiltersForPath(List<FilterMetaData> filters, String fullServletPath) {
1326
            FilterMetaData[] matches = _filtersPerUrl.getMatchingFilters(fullServletPath);
1✔
1327
            Collections.addAll(filters, matches);
1✔
1328
        }
1✔
1329

1330
        /**
1331
         * Adds the filters for servlet with name.
1332
         *
1333
         * @param filters
1334
         *            the filters
1335
         * @param servletName
1336
         *            the servlet name
1337
         */
1338
        private void addFiltersForServletWithName(List<FilterMetaData> filters, String servletName) {
1339
            if (servletName == null) {
1✔
1340
                return;
1✔
1341
            }
1342
            List<FilterMetaData> matches = (List<FilterMetaData>) _filtersPerName.get(servletName);
1✔
1343
            if (matches != null) {
1✔
1344
                filters.addAll(matches);
1✔
1345
            }
1346
        }
1✔
1347

1348
        /**
1349
         * Gets the configuration.
1350
         *
1351
         * @return the configuration
1352
         */
1353
        private ServletConfiguration getConfiguration() {
1354
            return _mapping == null ? null : (ServletConfiguration) _mapping.getConfiguration();
1✔
1355
        }
1356
    }
1357

1358
    /**
1359
     * mapping for WebResources.
1360
     */
1361
    static class WebResourceMapping {
1362

1363
        /** The configuration. */
1364
        private WebResourceConfiguration _configuration;
1365

1366
        /**
1367
         * Gets the configuration.
1368
         *
1369
         * @return the configuration
1370
         */
1371
        WebResourceConfiguration getConfiguration() {
1372
            return _configuration;
1✔
1373
        }
1374

1375
        /**
1376
         * Instantiates a new web resource mapping.
1377
         *
1378
         * @param configuration
1379
         *            the configuration
1380
         */
1381
        WebResourceMapping(WebResourceConfiguration configuration) {
1✔
1382
            _configuration = configuration;
1✔
1383
        }
1✔
1384

1385
        /**
1386
         * Returns the portion of the request path which was actually used to select the servlet. This default
1387
         * implementation returns the full specified path.
1388
         *
1389
         * @param requestPath
1390
         *            the full path of the request, relative to the application root.
1391
         *
1392
         * @return the servlet path
1393
         */
1394
        String getServletPath(String requestPath) {
1395
            return requestPath;
1✔
1396
        }
1397

1398
        /**
1399
         * Returns the portion of the request path which was not used to select the servlet, and can be used as data by
1400
         * the servlet. This default implementation returns null.
1401
         *
1402
         * @param requestPath
1403
         *            the full path of the request, relative to the application root.
1404
         *
1405
         * @return the path info
1406
         */
1407
        String getPathInfo(String requestPath) {
1408
            return null;
1✔
1409
        }
1410

1411
        /**
1412
         * Destroy resource.
1413
         */
1414
        public void destroyResource() {
1415
            getConfiguration().destroyResource();
1✔
1416
        }
1✔
1417
    }
1418

1419
    /**
1420
     * The Class PartialMatchWebResourceMapping.
1421
     */
1422
    static class PartialMatchWebResourceMapping extends WebResourceMapping {
1423

1424
        /** The prefix. */
1425
        private String _prefix;
1426

1427
        /**
1428
         * Instantiates a new partial match web resource mapping.
1429
         *
1430
         * @param configuration
1431
         *            the configuration
1432
         * @param prefix
1433
         *            the prefix
1434
         */
1435
        public PartialMatchWebResourceMapping(WebResourceConfiguration configuration, String prefix) {
1436
            super(configuration);
1✔
1437
            if (!prefix.endsWith("/*")) {
1!
1438
                throw new IllegalArgumentException(prefix + " does not end with '/*'");
×
1439
            }
1440
            _prefix = prefix.substring(0, prefix.length() - 2);
1✔
1441
        }
1✔
1442

1443
        @Override
1444
        String getServletPath(String requestPath) {
1445
            return _prefix;
1✔
1446
        }
1447

1448
        @Override
1449
        String getPathInfo(String requestPath) {
1450
            return requestPath.length() > _prefix.length() ? requestPath.substring(_prefix.length()) : null;
1✔
1451
        }
1452
    }
1453

1454
    /**
1455
     * A utility class for mapping web resources to url patterns. This implements the matching algorithm documented in
1456
     * section 10 of the JSDK-2.2 reference.
1457
     */
1458
    class WebResourceMap {
1✔
1459

1460
        /** The exact matches. */
1461
        private final Map _exactMatches = new HashMap<>();
1✔
1462

1463
        /** The extensions. */
1464
        private final Map _extensions = new HashMap<>();
1✔
1465

1466
        /** The url tree. */
1467
        private final Map _urlTree = new HashMap<>();
1✔
1468

1469
        /** The default mapping. */
1470
        private WebResourceMapping _defaultMapping;
1471

1472
        /**
1473
         * Put.
1474
         *
1475
         * @param mapping
1476
         *            the mapping
1477
         * @param configuration
1478
         *            the configuration
1479
         */
1480
        void put(String mapping, WebResourceConfiguration configuration) {
1481
            if (mapping.equals("/")) {
1✔
1482
                _defaultMapping = new WebResourceMapping(configuration);
1✔
1483
            } else if (mapping.startsWith("*.")) {
1✔
1484
                _extensions.put(mapping.substring(2), new WebResourceMapping(configuration));
1✔
1485
            } else if (!mapping.startsWith("/") || !mapping.endsWith("/*")) {
1!
1486
                _exactMatches.put(mapping, new WebResourceMapping(configuration));
1✔
1487
            } else {
1488
                ParsedPath path = new ParsedPath(mapping);
1✔
1489
                Map context = _urlTree;
1✔
1490
                while (path.hasNext()) {
1!
1491
                    String part = path.next();
1✔
1492
                    if (part.equals("*")) {
1✔
1493
                        context.put("*", new PartialMatchWebResourceMapping(configuration, mapping));
1✔
1494
                        return;
1✔
1495
                    }
1496
                    if (!context.containsKey(part)) {
1!
1497
                        context.put(part, new HashMap<>());
1✔
1498
                    }
1499
                    context = (Map) context.get(part);
1✔
1500
                }
1✔
1501
            }
1502
        }
1✔
1503

1504
        /**
1505
         * Gets the.
1506
         *
1507
         * @param url
1508
         *            the url
1509
         *
1510
         * @return the servlet meta data
1511
         */
1512
        ServletMetaData get(URL url) {
1513
            String file = url.getFile();
1✔
1514
            if (!file.startsWith(_contextPath)) {
1✔
1515
                throw new HttpNotFoundException("File path does not begin with '" + _contextPath + "'", url);
1✔
1516
            }
1517

1518
            String servletPath = getServletPath(file.substring(_contextPath.length()));
1✔
1519

1520
            if (servletPath.endsWith("j_security_check")) {
1✔
1521
                return new ServletRequestImpl(url, servletPath, SECURITY_CHECK_MAPPING, _filterMapping,
1✔
1522
                        _filterUrlMapping);
1523
            }
1524
            return new ServletRequestImpl(url, servletPath, getMapping(servletPath), _filterMapping, _filterUrlMapping);
1✔
1525
        }
1526

1527
        /**
1528
         * Gets the servlet path.
1529
         *
1530
         * @param urlFile
1531
         *            the url file
1532
         *
1533
         * @return the servlet path
1534
         */
1535
        private String getServletPath(String urlFile) {
1536
            if (urlFile.indexOf('?') < 0) {
1✔
1537
                return urlFile;
1✔
1538
            }
1539
            return urlFile.substring(0, urlFile.indexOf('?'));
1✔
1540
        }
1541

1542
        /**
1543
         * Destroy web resources.
1544
         */
1545
        public void destroyWebResources() {
1546
            if (_defaultMapping != null) {
1!
1547
                _defaultMapping.destroyResource();
×
1548
            }
1549
            destroyWebResources(_exactMatches);
1✔
1550
            destroyWebResources(_extensions);
1✔
1551
            destroyWebResources(_urlTree);
1✔
1552
        }
1✔
1553

1554
        /**
1555
         * Destroy web resources.
1556
         *
1557
         * @param map
1558
         *            the map
1559
         */
1560
        private void destroyWebResources(Map map) {
1561
            for (Object o : map.values()) {
1✔
1562
                if (o instanceof WebResourceMapping) {
1!
1563
                    WebResourceMapping webResourceMapping = (WebResourceMapping) o;
1✔
1564
                    webResourceMapping.destroyResource();
1✔
1565
                } else {
1✔
1566
                    destroyWebResources((Map) o);
×
1567
                }
1568
            }
1✔
1569
        }
1✔
1570

1571
        /**
1572
         * Auto load servlets.
1573
         */
1574
        void autoLoadServlets() {
1575
            ArrayList autoLoadable = new ArrayList<>();
1✔
1576
            if (_defaultMapping != null && _defaultMapping.getConfiguration().isLoadOnStartup()) {
1!
1577
                autoLoadable.add(_defaultMapping.getConfiguration());
×
1578
            }
1579
            collectAutoLoadableServlets(autoLoadable, _exactMatches);
1✔
1580
            collectAutoLoadableServlets(autoLoadable, _extensions);
1✔
1581
            collectAutoLoadableServlets(autoLoadable, _urlTree);
1✔
1582
            if (autoLoadable.isEmpty()) {
1✔
1583
                return;
1✔
1584
            }
1585

1586
            Collections.sort(autoLoadable, (o1, o2) -> {
1✔
1587
                ServletConfiguration sc1 = (ServletConfiguration) o1;
1✔
1588
                ServletConfiguration sc2 = (ServletConfiguration) o2;
1✔
1589
                return sc1.getLoadOrder() <= sc2.getLoadOrder() ? -1 : +1;
1✔
1590
            });
1591
            for (Iterator iterator = autoLoadable.iterator(); iterator.hasNext();) {
1✔
1592
                ServletConfiguration servletConfiguration = (ServletConfiguration) iterator.next();
1✔
1593
                try {
1594
                    servletConfiguration.getServlet();
1✔
1595
                } catch (Exception e) {
×
1596
                    HttpUnitUtils.handleException(e);
×
1597
                    throw new RuntimeException(
×
1598
                            "Unable to autoload servlet: " + servletConfiguration.getClassName() + ": " + e);
×
1599
                }
1✔
1600
            }
1✔
1601
        }
1✔
1602

1603
        /**
1604
         * Collect auto loadable servlets.
1605
         *
1606
         * @param collection
1607
         *            the collection
1608
         * @param map
1609
         *            the map
1610
         */
1611
        private void collectAutoLoadableServlets(Collection collection, Map map) {
1612
            for (Object o : map.values()) {
1✔
1613
                if (o instanceof WebResourceMapping) {
1✔
1614
                    WebResourceMapping servletMapping = (WebResourceMapping) o;
1✔
1615
                    if (servletMapping.getConfiguration().isLoadOnStartup()) {
1✔
1616
                        collection.add(servletMapping.getConfiguration());
1✔
1617
                    }
1618
                } else {
1✔
1619
                    collectAutoLoadableServlets(collection, (Map) o);
1✔
1620
                }
1621
            }
1✔
1622
        }
1✔
1623

1624
        /**
1625
         * Gets the mapping.
1626
         *
1627
         * @param url
1628
         *            the url
1629
         *
1630
         * @return the mapping
1631
         */
1632
        private WebResourceMapping getMapping(String url) {
1633
            if (_exactMatches.containsKey(url)) {
1✔
1634
                return (WebResourceMapping) _exactMatches.get(url);
1✔
1635
            }
1636

1637
            Map context = getContextForLongestPathPrefix(url);
1✔
1638
            if (context.containsKey("*")) {
1✔
1639
                return (WebResourceMapping) context.get("*");
1✔
1640
            }
1641

1642
            if (_extensions.containsKey(getExtension(url))) {
1✔
1643
                return (WebResourceMapping) _extensions.get(getExtension(url));
1✔
1644
            }
1645

1646
            if (_urlTree.containsKey("/")) {
1!
1647
                return (WebResourceMapping) _urlTree.get("/");
×
1648
            }
1649

1650
            if (_defaultMapping != null) {
1✔
1651
                return _defaultMapping;
1✔
1652
            }
1653

1654
            final String prefix = "/servlet/";
1✔
1655
            if (!url.startsWith(prefix)) {
1✔
1656
                return null;
1✔
1657
            }
1658

1659
            String className = url.substring(prefix.length());
1✔
1660
            try {
1661
                Class.forName(className);
1✔
1662
                return new WebResourceMapping(new ServletConfiguration(className));
1✔
1663
            } catch (ClassNotFoundException e) {
×
1664
                return null;
×
1665
            }
1666
        }
1667

1668
        /**
1669
         * Gets the context for longest path prefix.
1670
         *
1671
         * @param url
1672
         *            the url
1673
         *
1674
         * @return the context for longest path prefix
1675
         */
1676
        private Map getContextForLongestPathPrefix(String url) {
1677
            Map context = _urlTree;
1✔
1678

1679
            ParsedPath path = new ParsedPath(url);
1✔
1680
            while (path.hasNext()) {
1✔
1681
                String part = path.next();
1✔
1682
                if (!context.containsKey(part)) {
1✔
1683
                    break;
1✔
1684
                }
1685
                context = (Map) context.get(part);
1✔
1686
            }
1✔
1687
            return context;
1✔
1688
        }
1689

1690
        /**
1691
         * Gets the extension.
1692
         *
1693
         * @param url
1694
         *            the url
1695
         *
1696
         * @return the extension
1697
         */
1698
        private String getExtension(String url) {
1699
            int index = url.lastIndexOf('.');
1✔
1700
            if (index == -1 || index >= url.length() - 1) {
1!
1701
                return "";
1✔
1702
            }
1703
            return url.substring(index + 1);
1✔
1704
        }
1705

1706
    }
1707

1708
    /**
1709
     * return the given ServletConfiguration for the given servlet name.
1710
     *
1711
     * @param servletName
1712
     *            the servlet name
1713
     *
1714
     * @return the corresponding ServletConfiguration
1715
     */
1716
    public ServletConfiguration getServletByName(String servletName) {
1717
        return (ServletConfiguration) _servlets.get(servletName);
1✔
1718
    }
1719

1720
}
1721

1722
/**
1723
 * A utility class for parsing URLs into paths
1724
 */
1725
class ParsedPath {
1726

1727
    private final String path;
1728
    private int position = 0;
1✔
1729
    static final char seperator_char = '/';
1730

1731
    /**
1732
     * Creates a new parsed path for the given path value
1733
     *
1734
     * @param path
1735
     *            the path
1736
     */
1737
    ParsedPath(String path) {
1✔
1738
        if (path.charAt(0) != seperator_char) {
1!
1739
            throw new IllegalArgumentException("Illegal path '" + path + "', does not begin with " + seperator_char);
×
1740
        }
1741
        this.path = path;
1✔
1742
    }
1✔
1743

1744
    /**
1745
     * Returns true if there are more parts left, otherwise false
1746
     */
1747
    public final boolean hasNext() {
1748
        return position < path.length();
1✔
1749
    }
1750

1751
    /**
1752
     * Returns the next part in the path
1753
     */
1754
    public final String next() {
1755
        int offset = position + 1;
1✔
1756
        while (offset < path.length() && path.charAt(offset) != seperator_char) {
1✔
1757
            offset++;
1✔
1758
        }
1759
        String result = path.substring(position + 1, offset);
1✔
1760
        position = offset;
1✔
1761
        return result;
1✔
1762
    }
1763

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