1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17 package org.apache.catalina.startup;
18
19 import java.io.File;
20 import java.io.FileInputStream;
21 import java.io.FileNotFoundException;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.net.MalformedURLException;
25 import java.net.URISyntaxException;
26 import java.net.URL;
27 import java.net.URLConnection;
28 import java.util.ArrayList;
29 import java.util.Collections;
30 import java.util.HashMap;
31 import java.util.HashSet;
32 import java.util.LinkedHashMap;
33 import java.util.LinkedHashSet;
34 import java.util.List;
35 import java.util.Locale;
36 import java.util.Map;
37 import java.util.Map.Entry;
38 import java.util.Properties;
39 import java.util.Set;
40 import java.util.concurrent.ConcurrentHashMap;
41
42 import javax.servlet.MultipartConfigElement;
43 import javax.servlet.ServletContainerInitializer;
44 import javax.servlet.ServletContext;
45 import javax.servlet.SessionCookieConfig;
46 import javax.servlet.annotation.HandlesTypes;
47
48 import org.apache.catalina.Authenticator;
49 import org.apache.catalina.Container;
50 import org.apache.catalina.Context;
51 import org.apache.catalina.Engine;
52 import org.apache.catalina.Globals;
53 import org.apache.catalina.Host;
54 import org.apache.catalina.Lifecycle;
55 import org.apache.catalina.LifecycleEvent;
56 import org.apache.catalina.LifecycleListener;
57 import org.apache.catalina.Pipeline;
58 import org.apache.catalina.Server;
59 import org.apache.catalina.Service;
60 import org.apache.catalina.Valve;
61 import org.apache.catalina.WebResource;
62 import org.apache.catalina.WebResourceRoot;
63 import org.apache.catalina.Wrapper;
64 import org.apache.catalina.core.StandardContext;
65 import org.apache.catalina.core.StandardHost;
66 import org.apache.catalina.util.ContextName;
67 import org.apache.catalina.util.Introspection;
68 import org.apache.juli.logging.Log;
69 import org.apache.juli.logging.LogFactory;
70 import org.apache.tomcat.Jar;
71 import org.apache.tomcat.JarScanType;
72 import org.apache.tomcat.JarScanner;
73 import org.apache.tomcat.util.ExceptionUtils;
74 import org.apache.tomcat.util.bcel.classfile.AnnotationElementValue;
75 import org.apache.tomcat.util.bcel.classfile.AnnotationEntry;
76 import org.apache.tomcat.util.bcel.classfile.ArrayElementValue;
77 import org.apache.tomcat.util.bcel.classfile.ClassFormatException;
78 import org.apache.tomcat.util.bcel.classfile.ClassParser;
79 import org.apache.tomcat.util.bcel.classfile.ElementValue;
80 import org.apache.tomcat.util.bcel.classfile.ElementValuePair;
81 import org.apache.tomcat.util.bcel.classfile.JavaClass;
82 import org.apache.tomcat.util.buf.UriUtil;
83 import org.apache.tomcat.util.descriptor.InputSourceUtil;
84 import org.apache.tomcat.util.descriptor.XmlErrorHandler;
85 import org.apache.tomcat.util.descriptor.web.ContextEjb;
86 import org.apache.tomcat.util.descriptor.web.ContextEnvironment;
87 import org.apache.tomcat.util.descriptor.web.ContextLocalEjb;
88 import org.apache.tomcat.util.descriptor.web.ContextResource;
89 import org.apache.tomcat.util.descriptor.web.ContextResourceEnvRef;
90 import org.apache.tomcat.util.descriptor.web.ContextService;
91 import org.apache.tomcat.util.descriptor.web.ErrorPage;
92 import org.apache.tomcat.util.descriptor.web.FilterDef;
93 import org.apache.tomcat.util.descriptor.web.FilterMap;
94 import org.apache.tomcat.util.descriptor.web.FragmentJarScannerCallback;
95 import org.apache.tomcat.util.descriptor.web.JspPropertyGroup;
96 import org.apache.tomcat.util.descriptor.web.LoginConfig;
97 import org.apache.tomcat.util.descriptor.web.MessageDestinationRef;
98 import org.apache.tomcat.util.descriptor.web.MultipartDef;
99 import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
100 import org.apache.tomcat.util.descriptor.web.SecurityRoleRef;
101 import org.apache.tomcat.util.descriptor.web.ServletDef;
102 import org.apache.tomcat.util.descriptor.web.SessionConfig;
103 import org.apache.tomcat.util.descriptor.web.WebXml;
104 import org.apache.tomcat.util.descriptor.web.WebXmlParser;
105 import org.apache.tomcat.util.digester.Digester;
106 import org.apache.tomcat.util.digester.RuleSet;
107 import org.apache.tomcat.util.file.ConfigFileLoader;
108 import org.apache.tomcat.util.file.ConfigurationSource;
109 import org.apache.tomcat.util.res.StringManager;
110 import org.apache.tomcat.util.scan.JarFactory;
111 import org.xml.sax.InputSource;
112 import org.xml.sax.SAXParseException;
113
114 /**
115  * Startup event listener for a <b>Context</b> that configures the properties
116  * of that Context, and the associated defined servlets.
117  *
118  * @author Craig R. McClanahan
119  */

120 public class ContextConfig implements LifecycleListener {
121
122     private static final Log log = LogFactory.getLog(ContextConfig.class);
123
124
125     /**
126      * The string resources for this package.
127      */

128     protected static final StringManager sm =
129         StringManager.getManager(Constants.Package);
130
131
132     protected static final LoginConfig DUMMY_LOGIN_CONFIG =
133         new LoginConfig("NONE"nullnullnull);
134
135
136     /**
137      * The set of Authenticators that we know how to configure.  The key is
138      * the name of the implemented authentication method, and the value is
139      * the fully qualified Java class name of the corresponding Valve.
140      */

141     protected static final Properties authenticators;
142
143     static {
144         // Load our mapping properties for the standard authenticators
145         Properties props = new Properties();
146         try (InputStream is = ContextConfig.class.getClassLoader().getResourceAsStream(
147                 "org/apache/catalina/startup/Authenticators.properties")) {
148             if (is != null) {
149                 props.load(is);
150             }
151         } catch (IOException ioe) {
152             props = null;
153         }
154         authenticators = props;
155     }
156
157     /**
158      * Deployment count.
159      */

160     protected static long deploymentCount = 0L;
161
162
163     /**
164      * Cache of default web.xml fragments per Host
165      */

166     protected static final Map<Host,DefaultWebXmlCacheEntry> hostWebXmlCache =
167             new ConcurrentHashMap<>();
168
169
170     /**
171      * Set used as the value for {@code JavaClassCacheEntry.sciSet} when there
172      * are no SCIs associated with a class.
173      */

174     private static final Set<ServletContainerInitializer> EMPTY_SCI_SET = Collections.emptySet();
175
176
177     // ----------------------------------------------------- Instance Variables
178     /**
179      * Custom mappings of login methods to authenticators
180      */

181     protected Map<String,Authenticator> customAuthenticators;
182
183
184     /**
185      * The Context we are associated with.
186      */

187     protected volatile Context context = null;
188
189
190     /**
191      * The default web application's deployment descriptor location.
192      */

193     protected String defaultWebXml = null;
194
195
196     /**
197      * Track any fatal errors during startup configuration processing.
198      */

199     protected boolean ok = false;
200
201
202     /**
203      * Original docBase.
204      */

205     protected String originalDocBase = null;
206
207
208     /**
209      * Anti-locking docBase. It is a path to a copy of the web application
210      * in the java.io.tmpdir directory. This path is always an absolute one.
211      */

212     private File antiLockingDocBase = null;
213
214
215     /**
216      * Map of ServletContainerInitializer to classes they expressed interest in.
217      */

218     protected final Map<ServletContainerInitializer, Set<Class<?>>> initializerClassMap =
219             new LinkedHashMap<>();
220
221     /**
222      * Map of Types to ServletContainerInitializer that are interested in those
223      * types.
224      */

225     protected final Map<Class<?>, Set<ServletContainerInitializer>> typeInitializerMap =
226             new HashMap<>();
227
228     /**
229      * Flag that indicates if at least one {@link HandlesTypes} entry is present
230      * that represents an annotation.
231      */

232     protected boolean handlesTypesAnnotations = false;
233
234     /**
235      * Flag that indicates if at least one {@link HandlesTypes} entry is present
236      * that represents a non-annotation.
237      */

238     protected boolean handlesTypesNonAnnotations = false;
239
240
241     // ------------------------------------------------------------- Properties
242
243     /**
244      * Obtain the location of the default deployment descriptor.
245      *
246      * @return The path to the default web.xml. If not absolute, it is relative
247      *         to CATALINA_BASE.
248      */

249     public String getDefaultWebXml() {
250         if (defaultWebXml == null) {
251             defaultWebXml = Constants.DefaultWebXml;
252         }
253         return defaultWebXml;
254     }
255
256
257     /**
258      * Set the location of the default deployment descriptor.
259      *
260      * @param path The path to the default web.xml. If not absolute, it is
261      *             relative to CATALINA_BASE.
262      */

263     public void setDefaultWebXml(String path) {
264         this.defaultWebXml = path;
265     }
266
267
268     /**
269      * Sets custom mappings of login methods to authenticators.
270      *
271      * @param customAuthenticators Custom mappings of login methods to
272      * authenticators
273      */

274     public void setCustomAuthenticators(
275             Map<String,Authenticator> customAuthenticators) {
276         this.customAuthenticators = customAuthenticators;
277     }
278
279
280     // --------------------------------------------------------- Public Methods
281
282
283     /**
284      * Process events for an associated Context.
285      *
286      * @param event The lifecycle event that has occurred
287      */

288     @Override
289     public void lifecycleEvent(LifecycleEvent event) {
290
291         // Identify the context we are associated with
292         try {
293             context = (Context) event.getLifecycle();
294         } catch (ClassCastException e) {
295             log.error(sm.getString("contextConfig.cce", event.getLifecycle()), e);
296             return;
297         }
298
299         // Process the event that has occurred
300         if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
301             configureStart();
302         } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
303             beforeStart();
304         } else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {
305             // Restore docBase for management tools
306             if (originalDocBase != null) {
307                 context.setDocBase(originalDocBase);
308             }
309         } else if (event.getType().equals(Lifecycle.CONFIGURE_STOP_EVENT)) {
310             configureStop();
311         } else if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) {
312             init();
313         } else if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) {
314             destroy();
315         }
316
317     }
318
319
320     // -------------------------------------------------------- protected Methods
321
322
323     /**
324      * Process the application classes annotations, if it exists.
325      */

326     protected void applicationAnnotationsConfig() {
327
328         long t1=System.currentTimeMillis();
329
330         WebAnnotationSet.loadApplicationAnnotations(context);
331
332         long t2=System.currentTimeMillis();
333         if (context instanceof StandardContext) {
334             ((StandardContext) context).setStartupTime(t2-t1+
335                     ((StandardContext) context).getStartupTime());
336         }
337     }
338
339
340     /**
341      * Set up an Authenticator automatically if required, and one has not
342      * already been configured.
343      */

344     protected void authenticatorConfig() {
345
346         LoginConfig loginConfig = context.getLoginConfig();
347         if (loginConfig == null) {
348             // Need an authenticator to support HttpServletRequest.login()
349             loginConfig = DUMMY_LOGIN_CONFIG;
350             context.setLoginConfig(loginConfig);
351         }
352
353         // Has an authenticator been configured already?
354         if (context.getAuthenticator() != null) {
355             return;
356         }
357
358         // Has a Realm been configured for us to authenticate against?
359         if (context.getRealm() == null) {
360             log.error(sm.getString("contextConfig.missingRealm"));
361             ok = false;
362             return;
363         }
364
365         /*
366          * First check to see if there is a custom mapping for the login
367          * method. If so, use it. Otherwise, check if there is a mapping in
368          * org/apache/catalina/startup/Authenticators.properties.
369          */

370         Valve authenticator = null;
371         if (customAuthenticators != null) {
372             authenticator = (Valve) customAuthenticators.get(loginConfig.getAuthMethod());
373         }
374
375         if (authenticator == null) {
376             if (authenticators == null) {
377                 log.error(sm.getString("contextConfig.authenticatorResources"));
378                 ok = false;
379                 return;
380             }
381
382             // Identify the class name of the Valve we should configure
383             String authenticatorName = authenticators.getProperty(loginConfig.getAuthMethod());
384             if (authenticatorName == null) {
385                 log.error(sm.getString("contextConfig.authenticatorMissing",
386                                  loginConfig.getAuthMethod()));
387                 ok = false;
388                 return;
389             }
390
391             // Instantiate and install an Authenticator of the requested class
392             try {
393                 Class<?> authenticatorClass = Class.forName(authenticatorName);
394                 authenticator = (Valve) authenticatorClass.getConstructor().newInstance();
395             } catch (Throwable t) {
396                 ExceptionUtils.handleThrowable(t);
397                 log.error(sm.getString(
398                                     "contextConfig.authenticatorInstantiate",
399                                     authenticatorName),
400                           t);
401                 ok = false;
402             }
403         }
404
405         if (authenticator != null) {
406             Pipeline pipeline = context.getPipeline();
407             if (pipeline != null) {
408                 pipeline.addValve(authenticator);
409                 if (log.isDebugEnabled()) {
410                     log.debug(sm.getString(
411                                     "contextConfig.authenticatorConfigured",
412                                     loginConfig.getAuthMethod()));
413                 }
414             }
415         }
416     }
417
418
419     /**
420      * Create (if necessary) and return a Digester configured to process the
421      * context configuration descriptor for an application.
422      * @return the digester for context.xml files
423      */

424     protected Digester createContextDigester() {
425         Digester digester = new Digester();
426         digester.setValidating(false);
427         digester.setRulesValidation(true);
428         Map<Class<?>, List<String>> fakeAttributes = new HashMap<>();
429         List<String> objectAttrs = new ArrayList<>();
430         objectAttrs.add("className");
431         fakeAttributes.put(Object.class, objectAttrs);
432         // Ignore attribute added by Eclipse for its internal tracking
433         List<String> contextAttrs = new ArrayList<>();
434         contextAttrs.add("source");
435         fakeAttributes.put(StandardContext.class, contextAttrs);
436         digester.setFakeAttributes(fakeAttributes);
437         RuleSet contextRuleSet = new ContextRuleSet(""false);
438         digester.addRuleSet(contextRuleSet);
439         RuleSet namingRuleSet = new NamingRuleSet("Context/");
440         digester.addRuleSet(namingRuleSet);
441         return digester;
442     }
443
444
445     /**
446      * Process the default configuration file, if it exists.
447      * @param digester The digester that will be used for XML parsing
448      */

449     protected void contextConfig(Digester digester) {
450
451         String defaultContextXml = null;
452
453         // Open the default context.xml file, if it exists
454         if (context instanceof StandardContext) {
455             defaultContextXml = ((StandardContext)context).getDefaultContextXml();
456         }
457         // set the default if we don't have any overrides
458         if (defaultContextXml == null) {
459             defaultContextXml = Constants.DefaultContextXml;
460         }
461
462         if (!context.getOverride()) {
463             try (ConfigurationSource.Resource contextXmlResource =
464                     ConfigFileLoader.getSource().getResource(defaultContextXml)) {
465                 URL defaultContextUrl = contextXmlResource.getURI().toURL();
466                 processContextConfig(digester, defaultContextUrl, contextXmlResource.getInputStream());
467             } catch (MalformedURLException e) {
468                 log.error(sm.getString("contextConfig.badUrl", defaultContextXml), e);
469             } catch (IOException e) {
470                 // Not found
471             }
472
473             String hostContextFile = Container.getConfigPath(context, Constants.HostContextXml);
474             try (ConfigurationSource.Resource contextXmlResource =
475                     ConfigFileLoader.getSource().getResource(hostContextFile)) {
476                 URL defaultContextUrl = contextXmlResource.getURI().toURL();
477                 processContextConfig(digester, defaultContextUrl, contextXmlResource.getInputStream());
478             } catch (MalformedURLException e) {
479                 log.error(sm.getString("contextConfig.badUrl", hostContextFile), e);
480             } catch (IOException e) {
481                 // Not found
482             }
483         }
484         if (context.getConfigFile() != null) {
485             processContextConfig(digester, context.getConfigFile(), null);
486         }
487
488     }
489
490
491     /**
492      * Process a context.xml.
493      * @param digester The digester that will be used for XML parsing
494      * @param contextXml The URL to the context.xml configuration
495      * @param stream The XML resource stream
496      */

497     protected void processContextConfig(Digester digester, URL contextXml, InputStream stream) {
498
499         if (log.isDebugEnabled()) {
500             log.debug("Processing context [" + context.getName()
501                     + "] configuration file [" + contextXml + "]");
502         }
503
504         InputSource source = null;
505
506         try {
507             source = new InputSource(contextXml.toString());
508             if (stream == null) {
509                 URLConnection xmlConn = contextXml.openConnection();
510                 xmlConn.setUseCaches(false);
511                 stream = xmlConn.getInputStream();
512             }
513         } catch (Exception e) {
514             log.error(sm.getString("contextConfig.contextMissing",
515                       contextXml) , e);
516         }
517
518         if (source == null) {
519             return;
520         }
521
522         try {
523             source.setByteStream(stream);
524             digester.setClassLoader(this.getClass().getClassLoader());
525             digester.setUseContextClassLoader(false);
526             digester.push(context.getParent());
527             digester.push(context);
528             XmlErrorHandler errorHandler = new XmlErrorHandler();
529             digester.setErrorHandler(errorHandler);
530             digester.parse(source);
531             if (errorHandler.getWarnings().size() > 0 ||
532                     errorHandler.getErrors().size() > 0) {
533                 errorHandler.logFindings(log, contextXml.toString());
534                 ok = false;
535             }
536             if (log.isDebugEnabled()) {
537                 log.debug("Successfully processed context [" + context.getName()
538                         + "] configuration file [" + contextXml + "]");
539             }
540         } catch (SAXParseException e) {
541             log.error(sm.getString("contextConfig.contextParse",
542                     context.getName()), e);
543             log.error(sm.getString("contextConfig.defaultPosition",
544                              "" + e.getLineNumber(),
545                              "" + e.getColumnNumber()));
546             ok = false;
547         } catch (Exception e) {
548             log.error(sm.getString("contextConfig.contextParse",
549                     context.getName()), e);
550             ok = false;
551         } finally {
552             try {
553                 if (stream != null) {
554                     stream.close();
555                 }
556             } catch (IOException e) {
557                 log.error(sm.getString("contextConfig.contextClose"), e);
558             }
559         }
560     }
561
562
563     /**
564      * Adjust docBase.
565      * @throws IOException cannot access the context base path
566      */

567     protected void fixDocBase() throws IOException {
568
569         Host host = (Host) context.getParent();
570         File appBase = host.getAppBaseFile();
571
572         // This could be blank, relative, absolute or canonical
573         String docBaseConfigured = context.getDocBase();
574         // If there is no explicit docBase, derive it from the path and version
575         if (docBaseConfigured == null) {
576             // Trying to guess the docBase according to the path
577             String path = context.getPath();
578             if (path == null) {
579                 return;
580             }
581             ContextName cn = new ContextName(path, context.getWebappVersion());
582             docBaseConfigured = cn.getBaseName();
583         }
584
585         // Obtain the absolute docBase in String and File form
586         String docBaseAbsolute;
587         File docBaseConfiguredFile = new File(docBaseConfigured);
588         if (!docBaseConfiguredFile.isAbsolute()) {
589             docBaseAbsolute = (new File(appBase, docBaseConfigured)).getAbsolutePath();
590         } else {
591             docBaseAbsolute = docBaseConfiguredFile.getAbsolutePath();
592         }
593         File docBaseAbsoluteFile = new File(docBaseAbsolute);
594         String originalDocBase = docBaseAbsolute;
595
596         ContextName cn = new ContextName(context.getPath(), context.getWebappVersion());
597         String pathName = cn.getBaseName();
598
599         boolean unpackWARs = true;
600         if (host instanceof StandardHost) {
601             unpackWARs = ((StandardHost) host).isUnpackWARs();
602             if (unpackWARs && context instanceof StandardContext) {
603                 unpackWARs =  ((StandardContext) context).getUnpackWAR();
604             }
605         }
606
607         // At this point we need to determine if we have a WAR file in the
608         // appBase that needs to be expanded. Therefore we consider the absolute
609         // docBase NOT the canonical docBase. This is because some users symlink
610         // WAR files into the appBase and we want this to work correctly.
611         boolean docBaseAbsoluteInAppBase = docBaseAbsolute.startsWith(appBase.getPath() + File.separatorChar);
612         if (docBaseAbsolute.toLowerCase(Locale.ENGLISH).endsWith(".war") && !docBaseAbsoluteFile.isDirectory()) {
613             URL war = UriUtil.buildJarUrl(docBaseAbsoluteFile);
614             if (unpackWARs) {
615                 docBaseAbsolute = ExpandWar.expand(host, war, pathName);
616                 docBaseAbsoluteFile = new File(docBaseAbsolute);
617                 if (context instanceof StandardContext) {
618                     ((StandardContext) context).setOriginalDocBase(originalDocBase);
619                 }
620             } else {
621                 ExpandWar.validate(host, war, pathName);
622             }
623         } else {
624             File docBaseAbsoluteFileWar = new File(docBaseAbsolute + ".war");
625             URL war = null;
626             if (docBaseAbsoluteFileWar.exists() && docBaseAbsoluteInAppBase) {
627                 war = UriUtil.buildJarUrl(docBaseAbsoluteFileWar);
628             }
629             if (docBaseAbsoluteFile.exists()) {
630                 if (war != null && unpackWARs) {
631                     // Check if WAR needs to be re-expanded (e.g. if it has
632                     // changed). Note: HostConfig.deployWar() takes care of
633                     // ensuring that the correct XML file is used.
634                     // This will be a NO-OP if the WAR is unchanged.
635                     ExpandWar.expand(host, war, pathName);
636                 }
637             } else {
638                 if (war != null) {
639                     if (unpackWARs) {
640                         docBaseAbsolute = ExpandWar.expand(host, war, pathName);
641                         docBaseAbsoluteFile = new File(docBaseAbsolute);
642                     } else {
643                         docBaseAbsoluteFile = docBaseAbsoluteFileWar;
644                         ExpandWar.validate(host, war, pathName);
645                     }
646                 }
647                 if (context instanceof StandardContext) {
648                     ((StandardContext) context).setOriginalDocBase(originalDocBase);
649                 }
650             }
651         }
652
653         String docBaseCanonical = docBaseAbsoluteFile.getCanonicalPath();
654
655         // Re-calculate now docBase is a canonical path
656         boolean docBaseCanonicalInAppBase = docBaseCanonical.startsWith(appBase.getPath() + File.separatorChar);
657         String docBase;
658         if (docBaseCanonicalInAppBase) {
659             docBase = docBaseCanonical.substring(appBase.getPath().length());
660             docBase = docBase.replace(File.separatorChar, '/');
661             if (docBase.startsWith("/")) {
662                 docBase = docBase.substring(1);
663             }
664         } else {
665             docBase = docBaseCanonical.replace(File.separatorChar, '/');
666         }
667
668         context.setDocBase(docBase);
669     }
670
671
672     protected void antiLocking() {
673
674         if ((context instanceof StandardContext)
675             && ((StandardContext) context).getAntiResourceLocking()) {
676
677             Host host = (Host) context.getParent();
678             String docBase = context.getDocBase();
679             if (docBase == null) {
680                 return;
681             }
682             originalDocBase = docBase;
683
684             File docBaseFile = new File(docBase);
685             if (!docBaseFile.isAbsolute()) {
686                 docBaseFile = new File(host.getAppBaseFile(), docBase);
687             }
688
689             String path = context.getPath();
690             if (path == null) {
691                 return;
692             }
693             ContextName cn = new ContextName(path, context.getWebappVersion());
694             docBase = cn.getBaseName();
695
696             if (originalDocBase.toLowerCase(Locale.ENGLISH).endsWith(".war")) {
697                 antiLockingDocBase = new File(
698                         System.getProperty("java.io.tmpdir"),
699                         deploymentCount++ + "-" + docBase + ".war");
700             } else {
701                 antiLockingDocBase = new File(
702                         System.getProperty("java.io.tmpdir"),
703                         deploymentCount++ + "-" + docBase);
704             }
705             antiLockingDocBase = antiLockingDocBase.getAbsoluteFile();
706
707             if (log.isDebugEnabled()) {
708                 log.debug("Anti locking context[" + context.getName()
709                         + "] setting docBase to " +
710                         antiLockingDocBase.getPath());
711             }
712
713             // Cleanup just in case an old deployment is lying around
714             ExpandWar.delete(antiLockingDocBase);
715             if (ExpandWar.copy(docBaseFile, antiLockingDocBase)) {
716                 context.setDocBase(antiLockingDocBase.getPath());
717             }
718         }
719     }
720
721
722     /**
723      * Process a "init" event for this Context.
724      */

725     protected synchronized void init() {
726         // Called from StandardContext.init()
727
728         Digester contextDigester = createContextDigester();
729         contextDigester.getParser();
730
731         if (log.isDebugEnabled()) {
732             log.debug(sm.getString("contextConfig.init"));
733         }
734         context.setConfigured(false);
735         ok = true;
736
737         contextConfig(contextDigester);
738     }
739
740
741     /**
742      * Process a "before start" event for this Context.
743      */

744     protected synchronized void beforeStart() {
745
746         try {
747             fixDocBase();
748         } catch (IOException e) {
749             log.error(sm.getString(
750                     "contextConfig.fixDocBase", context.getName()), e);
751         }
752
753         antiLocking();
754     }
755
756
757     /**
758      * Process a "contextConfig" event for this Context.
759      */

760     protected synchronized void configureStart() {
761         // Called from StandardContext.start()
762
763         if (log.isDebugEnabled()) {
764             log.debug(sm.getString("contextConfig.start"));
765         }
766
767         if (log.isDebugEnabled()) {
768             log.debug(sm.getString("contextConfig.xmlSettings",
769                     context.getName(),
770                     Boolean.valueOf(context.getXmlValidation()),
771                     Boolean.valueOf(context.getXmlNamespaceAware())));
772         }
773
774         webConfig();
775
776         if (!context.getIgnoreAnnotations()) {
777             applicationAnnotationsConfig();
778         }
779         if (ok) {
780             validateSecurityRoles();
781         }
782
783         // Configure an authenticator if we need one
784         if (ok) {
785             authenticatorConfig();
786         }
787
788         // Dump the contents of this pipeline if requested
789         if (log.isDebugEnabled()) {
790             log.debug("Pipeline Configuration:");
791             Pipeline pipeline = context.getPipeline();
792             Valve valves[] = null;
793             if (pipeline != null) {
794                 valves = pipeline.getValves();
795             }
796             if (valves != null) {
797                 for (int i = 0; i < valves.length; i++) {
798                     log.debug("  " + valves[i].getClass().getName());
799                 }
800             }
801             log.debug("======================");
802         }
803
804         // Make our application available if no problems were encountered
805         if (ok) {
806             context.setConfigured(true);
807         } else {
808             log.error(sm.getString("contextConfig.unavailable"));
809             context.setConfigured(false);
810         }
811
812     }
813
814
815     /**
816      * Process a "stop" event for this Context.
817      */

818     protected synchronized void configureStop() {
819
820         if (log.isDebugEnabled()) {
821             log.debug(sm.getString("contextConfig.stop"));
822         }
823
824         int i;
825
826         // Removing children
827         Container[] children = context.findChildren();
828         for (i = 0; i < children.length; i++) {
829             context.removeChild(children[i]);
830         }
831
832         // Removing application parameters
833         /*
834         ApplicationParameter[] applicationParameters =
835             context.findApplicationParameters();
836         for (i = 0; i < applicationParameters.length; i++) {
837             context.removeApplicationParameter
838                 (applicationParameters[i].getName());
839         }
840         */

841
842         // Removing security constraints
843         SecurityConstraint[] securityConstraints = context.findConstraints();
844         for (i = 0; i < securityConstraints.length; i++) {
845             context.removeConstraint(securityConstraints[i]);
846         }
847
848         // Removing Ejbs
849         /*
850         ContextEjb[] contextEjbs = context.findEjbs();
851         for (i = 0; i < contextEjbs.length; i++) {
852             context.removeEjb(contextEjbs[i].getName());
853         }
854         */

855
856         // Removing environments
857         /*
858         ContextEnvironment[] contextEnvironments = context.findEnvironments();
859         for (i = 0; i < contextEnvironments.length; i++) {
860             context.removeEnvironment(contextEnvironments[i].getName());
861         }
862         */

863
864         // Removing errors pages
865         ErrorPage[] errorPages = context.findErrorPages();
866         for (i = 0; i < errorPages.length; i++) {
867             context.removeErrorPage(errorPages[i]);
868         }
869
870         // Removing filter defs
871         FilterDef[] filterDefs = context.findFilterDefs();
872         for (i = 0; i < filterDefs.length; i++) {
873             context.removeFilterDef(filterDefs[i]);
874         }
875
876         // Removing filter maps
877         FilterMap[] filterMaps = context.findFilterMaps();
878         for (i = 0; i < filterMaps.length; i++) {
879             context.removeFilterMap(filterMaps[i]);
880         }
881
882         // Removing local ejbs
883         /*
884         ContextLocalEjb[] contextLocalEjbs = context.findLocalEjbs();
885         for (i = 0; i < contextLocalEjbs.length; i++) {
886             context.removeLocalEjb(contextLocalEjbs[i].getName());
887         }
888         */

889
890         // Removing Mime mappings
891         String[] mimeMappings = context.findMimeMappings();
892         for (i = 0; i < mimeMappings.length; i++) {
893             context.removeMimeMapping(mimeMappings[i]);
894         }
895
896         // Removing parameters
897         String[] parameters = context.findParameters();
898         for (i = 0; i < parameters.length; i++) {
899             context.removeParameter(parameters[i]);
900         }
901
902         // Removing resource env refs
903         /*
904         String[] resourceEnvRefs = context.findResourceEnvRefs();
905         for (i = 0; i < resourceEnvRefs.length; i++) {
906             context.removeResourceEnvRef(resourceEnvRefs[i]);
907         }
908         */

909
910         // Removing resource links
911         /*
912         ContextResourceLink[] contextResourceLinks =
913             context.findResourceLinks();
914         for (i = 0; i < contextResourceLinks.length; i++) {
915             context.removeResourceLink(contextResourceLinks[i].getName());
916         }
917         */

918
919         // Removing resources
920         /*
921         ContextResource[] contextResources = context.findResources();
922         for (i = 0; i < contextResources.length; i++) {
923             context.removeResource(contextResources[i].getName());
924         }
925         */

926
927         // Removing security role
928         String[] securityRoles = context.findSecurityRoles();
929         for (i = 0; i < securityRoles.length; i++) {
930             context.removeSecurityRole(securityRoles[i]);
931         }
932
933         // Removing servlet mappings
934         String[] servletMappings = context.findServletMappings();
935         for (i = 0; i < servletMappings.length; i++) {
936             context.removeServletMapping(servletMappings[i]);
937         }
938
939         // FIXME : Removing status pages
940
941         // Removing welcome files
942         String[] welcomeFiles = context.findWelcomeFiles();
943         for (i = 0; i < welcomeFiles.length; i++) {
944             context.removeWelcomeFile(welcomeFiles[i]);
945         }
946
947         // Removing wrapper lifecycles
948         String[] wrapperLifecycles = context.findWrapperLifecycles();
949         for (i = 0; i < wrapperLifecycles.length; i++) {
950             context.removeWrapperLifecycle(wrapperLifecycles[i]);
951         }
952
953         // Removing wrapper listeners
954         String[] wrapperListeners = context.findWrapperListeners();
955         for (i = 0; i < wrapperListeners.length; i++) {
956             context.removeWrapperListener(wrapperListeners[i]);
957         }
958
959         // Remove (partially) folders and files created by antiLocking
960         if (antiLockingDocBase != null) {
961             // No need to log failure - it is expected in this case
962             ExpandWar.delete(antiLockingDocBase, false);
963         }
964
965         // Reset ServletContextInitializer scanning
966         initializerClassMap.clear();
967         typeInitializerMap.clear();
968
969         ok = true;
970
971     }
972
973
974     /**
975      * Process a "destroy" event for this Context.
976      */

977     protected synchronized void destroy() {
978         // Called from StandardContext.destroy()
979         if (log.isDebugEnabled()) {
980             log.debug(sm.getString("contextConfig.destroy"));
981         }
982
983         // Skip clearing the work directory if Tomcat is being shutdown
984         Server s = getServer();
985         if (s != null && !s.getState().isAvailable()) {
986             return;
987         }
988
989         // Changed to getWorkPath per Bugzilla 35819.
990         if (context instanceof StandardContext) {
991             String workDir = ((StandardContext) context).getWorkPath();
992             if (workDir != null) {
993                 ExpandWar.delete(new File(workDir));
994             }
995         }
996     }
997
998
999     private Server getServer() {
1000         Container c = context;
1001         while (c != null && !(c instanceof Engine)) {
1002             c = c.getParent();
1003         }
1004
1005         if (c == null) {
1006             return null;
1007         }
1008
1009         Service s = ((Engine)c).getService();
1010
1011         if (s == null) {
1012             return null;
1013         }
1014
1015         return s.getServer();
1016     }
1017
1018     /**
1019      * Validate the usage of security role names in the web application
1020      * deployment descriptor.  If any problems are found, issue warning
1021      * messages (for backwards compatibility) and add the missing roles.
1022      * (To make these problems fatal instead, simply set the <code>ok</code>
1023      * instance variable to <code>false</code> as well).
1024      */

1025     protected void validateSecurityRoles() {
1026
1027         // Check role names used in <security-constraint> elements
1028         SecurityConstraint constraints[] = context.findConstraints();
1029         for (int i = 0; i < constraints.length; i++) {
1030             String roles[] = constraints[i].findAuthRoles();
1031             for (int j = 0; j < roles.length; j++) {
1032                 if (!"*".equals(roles[j]) &&
1033                     !context.findSecurityRole(roles[j])) {
1034                     log.warn(sm.getString("contextConfig.role.auth", roles[j]));
1035                     context.addSecurityRole(roles[j]);
1036                 }
1037             }
1038         }
1039
1040         // Check role names used in <servlet> elements
1041         Container wrappers[] = context.findChildren();
1042         for (int i = 0; i < wrappers.length; i++) {
1043             Wrapper wrapper = (Wrapper) wrappers[i];
1044             String runAs = wrapper.getRunAs();
1045             if ((runAs != null) && !context.findSecurityRole(runAs)) {
1046                 log.warn(sm.getString("contextConfig.role.runas", runAs));
1047                 context.addSecurityRole(runAs);
1048             }
1049             String names[] = wrapper.findSecurityReferences();
1050             for (int j = 0; j < names.length; j++) {
1051                 String link = wrapper.findSecurityReference(names[j]);
1052                 if ((link != null) && !context.findSecurityRole(link)) {
1053                     log.warn(sm.getString("contextConfig.role.link", link));
1054                     context.addSecurityRole(link);
1055                 }
1056             }
1057         }
1058
1059     }
1060
1061
1062     protected File getHostConfigBase() {
1063         File file = null;
1064         if (context.getParent() instanceof Host) {
1065             file = ((Host)context.getParent()).getConfigBaseFile();
1066         }
1067         return file;
1068     }
1069
1070     /**
1071      * Scan the web.xml files that apply to the web application and merge them
1072      * using the rules defined in the spec. For the global web.xml files,
1073      * where there is duplicate configuration, the most specific level wins. ie
1074      * an application's web.xml takes precedence over the host level or global
1075      * web.xml file.
1076      */

1077     protected void webConfig() {
1078         /*
1079          * Anything and everything can override the global and host defaults.
1080          * This is implemented in two parts
1081          * - Handle as a web fragment that gets added after everything else so
1082          *   everything else takes priority
1083          * - Mark Servlets as overridable so SCI configuration can replace
1084          *   configuration from the defaults
1085          */

1086
1087         /*
1088          * The rules for annotation scanning are not as clear-cut as one might
1089          * think. Tomcat implements the following process:
1090          * - As per SRV.1.6.2, Tomcat will scan for annotations regardless of
1091          *   which Servlet spec version is declared in web.xml. The EG has
1092          *   confirmed this is the expected behaviour.
1093          * - As per http://java.net/jira/browse/SERVLET_SPEC-36, if the main
1094          *   web.xml is marked as metadata-complete, JARs are still processed
1095          *   for SCIs.
1096          * - If metadata-complete=true and an absolute ordering is specified,
1097          *   JARs excluded from the ordering are also excluded from the SCI
1098          *   processing.
1099          * - If an SCI has a @HandlesType annotation then all classes (except
1100          *   those in JARs excluded from an absolute ordering) need to be
1101          *   scanned to check if they match.
1102          */

1103         WebXmlParser webXmlParser = new WebXmlParser(context.getXmlNamespaceAware(),
1104                 context.getXmlValidation(), context.getXmlBlockExternal());
1105
1106         Set<WebXml> defaults = new HashSet<>();
1107         defaults.add(getDefaultWebXmlFragment(webXmlParser));
1108
1109         Set<WebXml> tomcatWebXml = new HashSet<>();
1110         tomcatWebXml.add(getTomcatWebXmlFragment(webXmlParser));
1111
1112         WebXml webXml = createWebXml();
1113
1114         // Parse context level web.xml
1115         InputSource contextWebXml = getContextWebXmlSource();
1116         if (!webXmlParser.parseWebXml(contextWebXml, webXml, false)) {
1117             ok = false;
1118         }
1119
1120         ServletContext sContext = context.getServletContext();
1121
1122         // Ordering is important here
1123
1124         // Step 1. Identify all the JARs packaged with the application and those
1125         // provided by the container. If any of the application JARs have a
1126         // web-fragment.xml it will be parsed at this point. web-fragment.xml
1127         // files are ignored for container provided JARs.
1128         Map<String,WebXml> fragments = processJarsForWebFragments(webXml, webXmlParser);
1129
1130         // Step 2. Order the fragments.
1131         Set<WebXml> orderedFragments = null;
1132         orderedFragments =
1133                 WebXml.orderWebFragments(webXml, fragments, sContext);
1134
1135         // Step 3. Look for ServletContainerInitializer implementations
1136         if (ok) {
1137             processServletContainerInitializers();
1138         }
1139
1140         if  (!webXml.isMetadataComplete() || typeInitializerMap.size() > 0) {
1141             // Steps 4 & 5.
1142             processClasses(webXml, orderedFragments);
1143         }
1144
1145         if (!webXml.isMetadataComplete()) {
1146             // Step 6. Merge web-fragment.xml files into the main web.xml
1147             // file.
1148             if (ok) {
1149                 ok = webXml.merge(orderedFragments);
1150             }
1151
1152             // Step 7a
1153             // merge tomcat-web.xml
1154             webXml.merge(tomcatWebXml);
1155
1156             // Step 7b. Apply global defaults
1157             // Have to merge defaults before JSP conversion since defaults
1158             // provide JSP servlet definition.
1159             webXml.merge(defaults);
1160
1161             // Step 8. Convert explicitly mentioned jsps to servlets
1162             if (ok) {
1163                 convertJsps(webXml);
1164             }
1165
1166             // Step 9. Apply merged web.xml to Context
1167             if (ok) {
1168                 configureContext(webXml);
1169             }
1170         } else {
1171             webXml.merge(tomcatWebXml);
1172             webXml.merge(defaults);
1173             convertJsps(webXml);
1174             configureContext(webXml);
1175         }
1176
1177         if (context.getLogEffectiveWebXml()) {
1178             log.info(sm.getString("contextConfig.effectiveWebXml", webXml.toXml()));
1179         }
1180
1181         // Always need to look for static resources
1182         // Step 10. Look for static resources packaged in JARs
1183         if (ok) {
1184             // Spec does not define an order.
1185             // Use ordered JARs followed by remaining JARs
1186             Set<WebXml> resourceJars = new LinkedHashSet<>();
1187             for (WebXml fragment : orderedFragments) {
1188                 resourceJars.add(fragment);
1189             }
1190             for (WebXml fragment : fragments.values()) {
1191                 if (!resourceJars.contains(fragment)) {
1192                     resourceJars.add(fragment);
1193                 }
1194             }
1195             processResourceJARs(resourceJars);
1196             // See also StandardContext.resourcesStart() for
1197             // WEB-INF/classes/META-INF/resources configuration
1198         }
1199
1200         // Step 11. Apply the ServletContainerInitializer config to the
1201         // context
1202         if (ok) {
1203             for (Map.Entry<ServletContainerInitializer,
1204                     Set<Class<?>>> entry :
1205                         initializerClassMap.entrySet()) {
1206                 if (entry.getValue().isEmpty()) {
1207                     context.addServletContainerInitializer(
1208                             entry.getKey(), null);
1209                 } else {
1210                     context.addServletContainerInitializer(
1211                             entry.getKey(), entry.getValue());
1212                 }
1213             }
1214         }
1215     }
1216
1217
1218     protected void processClasses(WebXml webXml, Set<WebXml> orderedFragments) {
1219         // Step 4. Process /WEB-INF/classes for annotations and
1220         // @HandlesTypes matches
1221         Map<String, JavaClassCacheEntry> javaClassCache = new HashMap<>();
1222
1223         if (ok) {
1224             WebResource[] webResources =
1225                     context.getResources().listResources("/WEB-INF/classes");
1226
1227             for (WebResource webResource : webResources) {
1228                 // Skip the META-INF directory from any JARs that have been
1229                 // expanded in to WEB-INF/classes (sometimes IDEs do this).
1230                 if ("META-INF".equals(webResource.getName())) {
1231                     continue;
1232                 }
1233                 processAnnotationsWebResource(webResource, webXml,
1234                         webXml.isMetadataComplete(), javaClassCache);
1235             }
1236         }
1237
1238         // Step 5. Process JARs for annotations and
1239         // @HandlesTypes matches - only need to process those fragments we
1240         // are going to use (remember orderedFragments includes any
1241         // container fragments)
1242         if (ok) {
1243             processAnnotations(
1244                     orderedFragments, webXml.isMetadataComplete(), javaClassCache);
1245         }
1246
1247         // Cache, if used, is no longer required so clear it
1248         javaClassCache.clear();
1249     }
1250
1251
1252     private void configureContext(WebXml webxml) {
1253         // As far as possible, process in alphabetical order so it is easy to
1254         // check everything is present
1255         // Some validation depends on correct public ID
1256         context.setPublicId(webxml.getPublicId());
1257
1258         // Everything else in order
1259         context.setEffectiveMajorVersion(webxml.getMajorVersion());
1260         context.setEffectiveMinorVersion(webxml.getMinorVersion());
1261
1262         for (Entry<String, String> entry : webxml.getContextParams().entrySet()) {
1263             context.addParameter(entry.getKey(), entry.getValue());
1264         }
1265         context.setDenyUncoveredHttpMethods(
1266                 webxml.getDenyUncoveredHttpMethods());
1267         context.setDisplayName(webxml.getDisplayName());
1268         context.setDistributable(webxml.isDistributable());
1269         for (ContextLocalEjb ejbLocalRef : webxml.getEjbLocalRefs().values()) {
1270             context.getNamingResources().addLocalEjb(ejbLocalRef);
1271         }
1272         for (ContextEjb ejbRef : webxml.getEjbRefs().values()) {
1273             context.getNamingResources().addEjb(ejbRef);
1274         }
1275         for (ContextEnvironment environment : webxml.getEnvEntries().values()) {
1276             context.getNamingResources().addEnvironment(environment);
1277         }
1278         for (ErrorPage errorPage : webxml.getErrorPages().values()) {
1279             context.addErrorPage(errorPage);
1280         }
1281         for (FilterDef filter : webxml.getFilters().values()) {
1282             if (filter.getAsyncSupported() == null) {
1283                 filter.setAsyncSupported("false");
1284             }
1285             context.addFilterDef(filter);
1286         }
1287         for (FilterMap filterMap : webxml.getFilterMappings()) {
1288             context.addFilterMap(filterMap);
1289         }
1290         context.setJspConfigDescriptor(webxml.getJspConfigDescriptor());
1291         for (String listener : webxml.getListeners()) {
1292             context.addApplicationListener(listener);
1293         }
1294         for (Entry<String, String> entry :
1295                 webxml.getLocaleEncodingMappings().entrySet()) {
1296             context.addLocaleEncodingMappingParameter(entry.getKey(),
1297                     entry.getValue());
1298         }
1299         // Prevents IAE
1300         if (webxml.getLoginConfig() != null) {
1301             context.setLoginConfig(webxml.getLoginConfig());
1302         }
1303         for (MessageDestinationRef mdr :
1304                 webxml.getMessageDestinationRefs().values()) {
1305             context.getNamingResources().addMessageDestinationRef(mdr);
1306         }
1307
1308         // messageDestinations were ignored in Tomcat 6, so ignore here
1309
1310         context.setIgnoreAnnotations(webxml.isMetadataComplete());
1311         for (Entry<String, String> entry :
1312                 webxml.getMimeMappings().entrySet()) {
1313             context.addMimeMapping(entry.getKey(), entry.getValue());
1314         }
1315         context.setRequestCharacterEncoding(webxml.getRequestCharacterEncoding());
1316         // Name is just used for ordering
1317         for (ContextResourceEnvRef resource :
1318                 webxml.getResourceEnvRefs().values()) {
1319             context.getNamingResources().addResourceEnvRef(resource);
1320         }
1321         for (ContextResource resource : webxml.getResourceRefs().values()) {
1322             context.getNamingResources().addResource(resource);
1323         }
1324         context.setResponseCharacterEncoding(webxml.getResponseCharacterEncoding());
1325         boolean allAuthenticatedUsersIsAppRole =
1326                 webxml.getSecurityRoles().contains(
1327                         SecurityConstraint.ROLE_ALL_AUTHENTICATED_USERS);
1328         for (SecurityConstraint constraint : webxml.getSecurityConstraints()) {
1329             if (allAuthenticatedUsersIsAppRole) {
1330                 constraint.treatAllAuthenticatedUsersAsApplicationRole();
1331             }
1332             context.addConstraint(constraint);
1333         }
1334         for (String role : webxml.getSecurityRoles()) {
1335             context.addSecurityRole(role);
1336         }
1337         for (ContextService service : webxml.getServiceRefs().values()) {
1338             context.getNamingResources().addService(service);
1339         }
1340         for (ServletDef servlet : webxml.getServlets().values()) {
1341             Wrapper wrapper = context.createWrapper();
1342             // Description is ignored
1343             // Display name is ignored
1344             // Icons are ignored
1345
1346             // jsp-file gets passed to the JSP Servlet as an init-param
1347
1348             if (servlet.getLoadOnStartup() != null) {
1349                 wrapper.setLoadOnStartup(servlet.getLoadOnStartup().intValue());
1350             }
1351             if (servlet.getEnabled() != null) {
1352                 wrapper.setEnabled(servlet.getEnabled().booleanValue());
1353             }
1354             wrapper.setName(servlet.getServletName());
1355             Map<String,String> params = servlet.getParameterMap();
1356             for (Entry<String, String> entry : params.entrySet()) {
1357                 wrapper.addInitParameter(entry.getKey(), entry.getValue());
1358             }
1359             wrapper.setRunAs(servlet.getRunAs());
1360             Set<SecurityRoleRef> roleRefs = servlet.getSecurityRoleRefs();
1361             for (SecurityRoleRef roleRef : roleRefs) {
1362                 wrapper.addSecurityReference(
1363                         roleRef.getName(), roleRef.getLink());
1364             }
1365             wrapper.setServletClass(servlet.getServletClass());
1366             MultipartDef multipartdef = servlet.getMultipartDef();
1367             if (multipartdef != null) {
1368                 if (multipartdef.getMaxFileSize() != null &&
1369                         multipartdef.getMaxRequestSize()!= null &&
1370                         multipartdef.getFileSizeThreshold() != null) {
1371                     wrapper.setMultipartConfigElement(new MultipartConfigElement(
1372                             multipartdef.getLocation(),
1373                             Long.parseLong(multipartdef.getMaxFileSize()),
1374                             Long.parseLong(multipartdef.getMaxRequestSize()),
1375                             Integer.parseInt(
1376                                     multipartdef.getFileSizeThreshold())));
1377                 } else {
1378                     wrapper.setMultipartConfigElement(new MultipartConfigElement(
1379                             multipartdef.getLocation()));
1380                 }
1381             }
1382             if (servlet.getAsyncSupported() != null) {
1383                 wrapper.setAsyncSupported(
1384                         servlet.getAsyncSupported().booleanValue());
1385             }
1386             wrapper.setOverridable(servlet.isOverridable());
1387             context.addChild(wrapper);
1388         }
1389         for (Entry<String, String> entry :
1390                 webxml.getServletMappings().entrySet()) {
1391             context.addServletMappingDecoded(entry.getKey(), entry.getValue());
1392         }
1393         SessionConfig sessionConfig = webxml.getSessionConfig();
1394         if (sessionConfig != null) {
1395             if (sessionConfig.getSessionTimeout() != null) {
1396                 context.setSessionTimeout(
1397                         sessionConfig.getSessionTimeout().intValue());
1398             }
1399             SessionCookieConfig scc =
1400                 context.getServletContext().getSessionCookieConfig();
1401             scc.setName(sessionConfig.getCookieName());
1402             scc.setDomain(sessionConfig.getCookieDomain());
1403             scc.setPath(sessionConfig.getCookiePath());
1404             scc.setComment(sessionConfig.getCookieComment());
1405             if (sessionConfig.getCookieHttpOnly() != null) {
1406                 scc.setHttpOnly(sessionConfig.getCookieHttpOnly().booleanValue());
1407             }
1408             if (sessionConfig.getCookieSecure() != null) {
1409                 scc.setSecure(sessionConfig.getCookieSecure().booleanValue());
1410             }
1411             if (sessionConfig.getCookieMaxAge() != null) {
1412                 scc.setMaxAge(sessionConfig.getCookieMaxAge().intValue());
1413             }
1414             if (sessionConfig.getSessionTrackingModes().size() > 0) {
1415                 context.getServletContext().setSessionTrackingModes(
1416                         sessionConfig.getSessionTrackingModes());
1417             }
1418         }
1419
1420         // Context doesn't use version directly
1421
1422         for (String welcomeFile : webxml.getWelcomeFiles()) {
1423             /*
1424              * The following will result in a welcome file of "" so don't add
1425              * that to the context
1426              * <welcome-file-list>
1427              *   <welcome-file/>
1428              * </welcome-file-list>
1429              */

1430             if (welcomeFile != null && welcomeFile.length() > 0) {
1431                 context.addWelcomeFile(welcomeFile);
1432             }
1433         }
1434
1435         // Do this last as it depends on servlets
1436         for (JspPropertyGroup jspPropertyGroup :
1437                 webxml.getJspPropertyGroups()) {
1438             String jspServletName = context.findServletMapping("*.jsp");
1439             if (jspServletName == null) {
1440                 jspServletName = "jsp";
1441             }
1442             if (context.findChild(jspServletName) != null) {
1443                 for (String urlPattern : jspPropertyGroup.getUrlPatterns()) {
1444                     context.addServletMappingDecoded(urlPattern, jspServletName, true);
1445                 }
1446             } else {
1447                 if(log.isDebugEnabled()) {
1448                     for (String urlPattern : jspPropertyGroup.getUrlPatterns()) {
1449                         log.debug("Skipping " + urlPattern + " , no servlet " +
1450                                 jspServletName);
1451                     }
1452                 }
1453             }
1454         }
1455
1456         for (Entry<String, String> entry :
1457                 webxml.getPostConstructMethods().entrySet()) {
1458             context.addPostConstructMethod(entry.getKey(), entry.getValue());
1459         }
1460
1461         for (Entry<String, String> entry :
1462             webxml.getPreDestroyMethods().entrySet()) {
1463             context.addPreDestroyMethod(entry.getKey(), entry.getValue());
1464         }
1465     }
1466
1467
1468     private WebXml getTomcatWebXmlFragment(WebXmlParser webXmlParser) {
1469
1470         WebXml webXmlTomcatFragment = createWebXml();
1471         webXmlTomcatFragment.setOverridable(true);
1472
1473         // Set to distributable else every app will be prevented from being
1474         // distributable when the Tomcat fragment is merged with the main
1475         // web.xml
1476         webXmlTomcatFragment.setDistributable(true);
1477         // When merging, the default welcome files are only used if the app has
1478         // not defined any welcomes files.
1479         webXmlTomcatFragment.setAlwaysAddWelcomeFiles(false);
1480
1481         WebResource resource = context.getResources().getResource(Constants.TomcatWebXml);
1482         if (resource.isFile()) {
1483             try {
1484                 InputSource source = new InputSource(resource.getURL().toURI().toString());
1485                 source.setByteStream(resource.getInputStream());
1486                 if (!webXmlParser.parseWebXml(source, webXmlTomcatFragment, false)) {
1487                     ok = false;
1488                 }
1489             } catch (URISyntaxException e) {
1490                 log.error(sm.getString("contextConfig.tomcatWebXmlError"), e);
1491             }
1492         }
1493         return webXmlTomcatFragment;
1494     }
1495
1496
1497     private WebXml getDefaultWebXmlFragment(WebXmlParser webXmlParser) {
1498
1499         // Host should never be null
1500         Host host = (Host) context.getParent();
1501
1502         DefaultWebXmlCacheEntry entry = hostWebXmlCache.get(host);
1503
1504         InputSource globalWebXml = getGlobalWebXmlSource();
1505         InputSource hostWebXml = getHostWebXmlSource();
1506
1507         long globalTimeStamp = 0;
1508         long hostTimeStamp = 0;
1509
1510         if (globalWebXml != null) {
1511             URLConnection uc = null;
1512             try {
1513                 URL url = new URL(globalWebXml.getSystemId());
1514                 uc = url.openConnection();
1515                 globalTimeStamp = uc.getLastModified();
1516             } catch (IOException e) {
1517                 globalTimeStamp = -1;
1518             } finally {
1519                 if (uc != null) {
1520                     try {
1521                         uc.getInputStream().close();
1522                     } catch (IOException e) {
1523                         ExceptionUtils.handleThrowable(e);
1524                         globalTimeStamp = -1;
1525                     }
1526                 }
1527             }
1528         }
1529
1530         if (hostWebXml != null) {
1531             URLConnection uc = null;
1532             try {
1533                 URL url = new URL(hostWebXml.getSystemId());
1534                 uc = url.openConnection();
1535                 hostTimeStamp = uc.getLastModified();
1536             } catch (IOException e) {
1537                 hostTimeStamp = -1;
1538             } finally {
1539                 if (uc != null) {
1540                     try {
1541                         uc.getInputStream().close();
1542                     } catch (IOException e) {
1543                         ExceptionUtils.handleThrowable(e);
1544                         hostTimeStamp = -1;
1545                     }
1546                 }
1547             }
1548         }
1549
1550         if (entry != null && entry.getGlobalTimeStamp() == globalTimeStamp &&
1551                 entry.getHostTimeStamp() == hostTimeStamp) {
1552             InputSourceUtil.close(globalWebXml);
1553             InputSourceUtil.close(hostWebXml);
1554             return entry.getWebXml();
1555         }
1556
1557         // Parsing global web.xml is relatively expensive. Use a sync block to
1558         // make sure it only happens once. Use the pipeline since a lock will
1559         // already be held on the host by another thread
1560         synchronized (host.getPipeline()) {
1561             entry = hostWebXmlCache.get(host);
1562             if (entry != null && entry.getGlobalTimeStamp() == globalTimeStamp &&
1563                     entry.getHostTimeStamp() == hostTimeStamp) {
1564                 return entry.getWebXml();
1565             }
1566
1567             WebXml webXmlDefaultFragment = createWebXml();
1568             webXmlDefaultFragment.setOverridable(true);
1569             // Set to distributable else every app will be prevented from being
1570             // distributable when the default fragment is merged with the main
1571             // web.xml
1572             webXmlDefaultFragment.setDistributable(true);
1573             // When merging, the default welcome files are only used if the app has
1574             // not defined any welcomes files.
1575             webXmlDefaultFragment.setAlwaysAddWelcomeFiles(false);
1576
1577             // Parse global web.xml if present
1578             if (globalWebXml == null) {
1579                 // This is unusual enough to log
1580                 log.info(sm.getString("contextConfig.defaultMissing"));
1581             } else {
1582                 if (!webXmlParser.parseWebXml(
1583                         globalWebXml, webXmlDefaultFragment, false)) {
1584                     ok = false;
1585                 }
1586             }
1587
1588             // Parse host level web.xml if present
1589             // Additive apart from welcome pages
1590             webXmlDefaultFragment.setReplaceWelcomeFiles(true);
1591
1592             if (!webXmlParser.parseWebXml(
1593                     hostWebXml, webXmlDefaultFragment, false)) {
1594                 ok = false;
1595             }
1596
1597             // Don't update the cache if an error occurs
1598             if (globalTimeStamp != -1 && hostTimeStamp != -1) {
1599                 entry = new DefaultWebXmlCacheEntry(webXmlDefaultFragment,
1600                         globalTimeStamp, hostTimeStamp);
1601                 hostWebXmlCache.put(host, entry);
1602                 // Add a Lifecycle listener to the Host that will remove it from
1603                 // the hostWebXmlCache once the Host is destroyed
1604                 host.addLifecycleListener(new HostWebXmlCacheCleaner());
1605             }
1606
1607             return webXmlDefaultFragment;
1608         }
1609     }
1610
1611
1612     private void convertJsps(WebXml webXml) {
1613         Map<String,String> jspInitParams;
1614         ServletDef jspServlet = webXml.getServlets().get("jsp");
1615         if (jspServlet == null) {
1616             jspInitParams = new HashMap<>();
1617             Wrapper w = (Wrapper) context.findChild("jsp");
1618             if (w != null) {
1619                 String[] params = w.findInitParameters();
1620                 for (String param : params) {
1621                     jspInitParams.put(param, w.findInitParameter(param));
1622                 }
1623             }
1624         } else {
1625             jspInitParams = jspServlet.getParameterMap();
1626         }
1627         for (ServletDef servletDef: webXml.getServlets().values()) {
1628             if (servletDef.getJspFile() != null) {
1629                 convertJsp(servletDef, jspInitParams);
1630             }
1631         }
1632     }
1633
1634     private void convertJsp(ServletDef servletDef,
1635             Map<String,String> jspInitParams) {
1636         servletDef.setServletClass(org.apache.catalina.core.Constants.JSP_SERVLET_CLASS);
1637         String jspFile = servletDef.getJspFile();
1638         if ((jspFile != null) && !jspFile.startsWith("/")) {
1639             if (context.isServlet22()) {
1640                 if(log.isDebugEnabled()) {
1641                     log.debug(sm.getString("contextConfig.jspFile.warning",
1642                                        jspFile));
1643                 }
1644                 jspFile = "/" + jspFile;
1645             } else {
1646                 throw new IllegalArgumentException
1647                     (sm.getString("contextConfig.jspFile.error", jspFile));
1648             }
1649         }
1650         servletDef.getParameterMap().put("jspFile", jspFile);
1651         servletDef.setJspFile(null);
1652         for (Map.Entry<String, String> initParam: jspInitParams.entrySet()) {
1653             servletDef.addInitParameter(initParam.getKey(), initParam.getValue());
1654         }
1655     }
1656
1657     protected WebXml createWebXml() {
1658         return new WebXml();
1659     }
1660
1661     /**
1662      * Scan JARs for ServletContainerInitializer implementations.
1663      */

1664     protected void processServletContainerInitializers() {
1665
1666         List<ServletContainerInitializer> detectedScis;
1667         try {
1668             WebappServiceLoader<ServletContainerInitializer> loader = new WebappServiceLoader<>(context);
1669             detectedScis = loader.load(ServletContainerInitializer.class);
1670         } catch (IOException e) {
1671             log.error(sm.getString(
1672                     "contextConfig.servletContainerInitializerFail",
1673                     context.getName()),
1674                 e);
1675             ok = false;
1676             return;
1677         }
1678
1679         for (ServletContainerInitializer sci : detectedScis) {
1680             initializerClassMap.put(sci, new HashSet<Class<?>>());
1681
1682             HandlesTypes ht;
1683             try {
1684                 ht = sci.getClass().getAnnotation(HandlesTypes.class);
1685             } catch (Exception e) {
1686                 if (log.isDebugEnabled()) {
1687                     log.info(sm.getString("contextConfig.sci.debug",
1688                             sci.getClass().getName()),
1689                             e);
1690                 } else {
1691                     log.info(sm.getString("contextConfig.sci.info",
1692                             sci.getClass().getName()));
1693                 }
1694                 continue;
1695             }
1696             if (ht == null) {
1697                 continue;
1698             }
1699             Class<?>[] types = ht.value();
1700             if (types == null) {
1701                 continue;
1702             }
1703
1704             for (Class<?> type : types) {
1705                 if (type.isAnnotation()) {
1706                     handlesTypesAnnotations = true;
1707                 } else {
1708                     handlesTypesNonAnnotations = true;
1709                 }
1710                 Set<ServletContainerInitializer> scis =
1711                         typeInitializerMap.get(type);
1712                 if (scis == null) {
1713                     scis = new HashSet<>();
1714                     typeInitializerMap.put(type, scis);
1715                 }
1716                 scis.add(sci);
1717             }
1718         }
1719     }
1720
1721
1722     /**
1723      * Scan JARs that contain web-fragment.xml files that will be used to
1724      * configure this application to see if they also contain static resources.
1725      * If static resources are found, add them to the context. Resources are
1726      * added in web-fragment.xml priority order.
1727      * @param fragments The set of fragments that will be scanned for
1728      *  static resources
1729      */

1730     protected void processResourceJARs(Set<WebXml> fragments) {
1731         for (WebXml fragment : fragments) {
1732             URL url = fragment.getURL();
1733             try {
1734                 if ("jar".equals(url.getProtocol()) || url.toString().endsWith(".jar")) {
1735                     try (Jar jar = JarFactory.newInstance(url)) {
1736                         jar.nextEntry();
1737                         String entryName = jar.getEntryName();
1738                         while (entryName != null) {
1739                             if (entryName.startsWith("META-INF/resources/")) {
1740                                 context.getResources().createWebResourceSet(
1741                                         WebResourceRoot.ResourceSetType.RESOURCE_JAR,
1742                                         "/", url, "/META-INF/resources");
1743                                 break;
1744                             }
1745                             jar.nextEntry();
1746                             entryName = jar.getEntryName();
1747                         }
1748                     }
1749                 } else if ("file".equals(url.getProtocol())) {
1750                     File file = new File(url.toURI());
1751                     File resources = new File(file, "META-INF/resources/");
1752                     if (resources.isDirectory()) {
1753                         context.getResources().createWebResourceSet(
1754                                 WebResourceRoot.ResourceSetType.RESOURCE_JAR,
1755                                 "/", resources.getAbsolutePath(), null"/");
1756                     }
1757                 }
1758             } catch (IOException ioe) {
1759                 log.error(sm.getString("contextConfig.resourceJarFail", url,
1760                         context.getName()));
1761             } catch (URISyntaxException e) {
1762                 log.error(sm.getString("contextConfig.resourceJarFail", url,
1763                     context.getName()));
1764             }
1765         }
1766     }
1767
1768
1769     /**
1770      * Identify the default web.xml to be used and obtain an input source for
1771      * it.
1772      * @return an input source to the default web.xml
1773      */

1774     protected InputSource getGlobalWebXmlSource() {
1775         // Is a default web.xml specified for the Context?
1776         if (defaultWebXml == null && context instanceof StandardContext) {
1777             defaultWebXml = ((StandardContext) context).getDefaultWebXml();
1778         }
1779         // Set the default if we don't have any overrides
1780         if (defaultWebXml == null) {
1781             getDefaultWebXml();
1782         }
1783
1784         // Is it explicitly suppressed, e.g. in embedded environment?
1785         if (Constants.NoDefaultWebXml.equals(defaultWebXml)) {
1786             return null;
1787         }
1788         return getWebXmlSource(defaultWebXml, true);
1789     }
1790
1791
1792     /**
1793      * Identify the host web.xml to be used and obtain an input source for
1794      * it.
1795      * @return an input source to the default per host web.xml
1796      */

1797     protected InputSource getHostWebXmlSource() {
1798         File hostConfigBase = getHostConfigBase();
1799         if (hostConfigBase == null)
1800             return null;
1801
1802         return getWebXmlSource(hostConfigBase.getPath(), false);
1803     }
1804
1805     /**
1806      * Identify the application web.xml to be used and obtain an input source
1807      * for it.
1808      * @return an input source to the context web.xml
1809      */

1810     protected InputSource getContextWebXmlSource() {
1811         InputStream stream = null;
1812         InputSource source = null;
1813         URL url = null;
1814
1815         String altDDName = null;
1816
1817         // Open the application web.xml file, if it exists
1818         ServletContext servletContext = context.getServletContext();
1819         try {
1820             if (servletContext != null) {
1821                 altDDName = (String)servletContext.getAttribute(Globals.ALT_DD_ATTR);
1822                 if (altDDName != null) {
1823                     try {
1824                         stream = new FileInputStream(altDDName);
1825                         url = new File(altDDName).toURI().toURL();
1826                     } catch (FileNotFoundException e) {
1827                         log.error(sm.getString("contextConfig.altDDNotFound",
1828                                                altDDName));
1829                     } catch (MalformedURLException e) {
1830                         log.error(sm.getString("contextConfig.applicationUrl"));
1831                     }
1832                 }
1833                 else {
1834                     stream = servletContext.getResourceAsStream
1835                         (Constants.ApplicationWebXml);
1836                     try {
1837                         url = servletContext.getResource(
1838                                 Constants.ApplicationWebXml);
1839                     } catch (MalformedURLException e) {
1840                         log.error(sm.getString("contextConfig.applicationUrl"));
1841                     }
1842                 }
1843             }
1844             if (stream == null || url == null) {
1845                 if (log.isDebugEnabled()) {
1846                     log.debug(sm.getString("contextConfig.applicationMissing") + " " + context);
1847                 }
1848             } else {
1849                 source = new InputSource(url.toExternalForm());
1850                 source.setByteStream(stream);
1851             }
1852         } finally {
1853             if (source == null && stream != null) {
1854                 try {
1855                     stream.close();
1856                 } catch (IOException e) {
1857                     // Ignore
1858                 }
1859             }
1860         }
1861
1862         return source;
1863     }
1864
1865     public String getConfigBasePath() {
1866         String path = null;
1867         if (context.getParent() instanceof Host) {
1868             Host host = (Host) context.getParent();
1869             if (host.getXmlBase() != null) {
1870                 path = host.getXmlBase();
1871             } else {
1872                 StringBuilder xmlDir = new StringBuilder("conf");
1873                 Container parent = host.getParent();
1874                 if (parent instanceof Engine) {
1875                     xmlDir.append('/');
1876                     xmlDir.append(parent.getName());
1877                 }
1878                 xmlDir.append('/');
1879                 xmlDir.append(host.getName());
1880                 path = xmlDir.toString();
1881             }
1882         }
1883         return path;
1884     }
1885
1886     /**
1887      * Utility method to create an input source from the specified XML file.
1888      * @param filename  Name of the file (possibly with one or more leading path
1889      *                  segments) to read
1890      * @param global true if processing a shared resource, false if processing
1891      *        a host based resource
1892      * @return the input source
1893      */

1894     protected InputSource getWebXmlSource(String filename, boolean global) {
1895         ConfigurationSource.Resource webXmlResource = null;
1896         try {
1897             if (global) {
1898                 if (Constants.DefaultWebXml.equals(filename)) {
1899                     webXmlResource = ConfigFileLoader.getSource().getSharedWebXml();
1900                 } else {
1901                     webXmlResource = ConfigFileLoader.getSource().getResource(filename);
1902                 }
1903             } else {
1904                 String hostWebXml = Container.getConfigPath(context, Constants.HostWebXml);
1905                 webXmlResource = ConfigFileLoader.getSource().getResource(hostWebXml);
1906             }
1907         } catch (IOException e) {
1908             // Ignore if not found
1909             return null;
1910         }
1911
1912         InputStream stream = null;
1913         InputSource source = null;
1914
1915         try {
1916             stream = webXmlResource.getInputStream();
1917             source = new InputSource(webXmlResource.getURI().toString());
1918             if (stream != null) {
1919                 source.setByteStream(stream);
1920             }
1921         } catch (Exception e) {
1922             log.error(sm.getString("contextConfig.defaultError", filename, webXmlResource.getURI()), e);
1923         } finally {
1924             if (source == null && stream != null) {
1925                 try {
1926                     stream.close();
1927                 } catch (IOException e) {
1928                     // Ignore
1929                 }
1930             }
1931         }
1932
1933         return source;
1934     }
1935
1936
1937     /**
1938      * Scan /WEB-INF/lib for JARs and for each one found add it and any
1939      * /META-INF/web-fragment.xml to the resulting Map. web-fragment.xml files
1940      * will be parsed before being added to the map. Every JAR will be added and
1941      * <code>null</code> will be used if no web-fragment.xml was found. Any JARs
1942      * known not contain fragments will be skipped.
1943      *
1944      * @param application The main web.xml metadata
1945      * @param webXmlParser The parser to use to process the web.xml file
1946      * @return A map of JAR name to processed web fragment (if any)
1947      */

1948     protected Map<String,WebXml> processJarsForWebFragments(WebXml application,
1949             WebXmlParser webXmlParser) {
1950
1951         JarScanner jarScanner = context.getJarScanner();
1952         boolean delegate = false;
1953         if (context instanceof StandardContext) {
1954             delegate = ((StandardContext) context).getDelegate();
1955         }
1956         boolean parseRequired = true;
1957         Set<String> absoluteOrder = application.getAbsoluteOrdering();
1958         if (absoluteOrder != null && absoluteOrder.isEmpty() &&
1959                 !context.getXmlValidation()) {
1960             // Skip parsing when there is an empty absolute ordering and
1961             // validation is not enabled
1962             parseRequired = false;
1963         }
1964
1965         FragmentJarScannerCallback callback =
1966                 new FragmentJarScannerCallback(webXmlParser, delegate, parseRequired);
1967
1968         jarScanner.scan(JarScanType.PLUGGABILITY,
1969                 context.getServletContext(), callback);
1970
1971         if (!callback.isOk()) {
1972             ok = false;
1973         }
1974         return callback.getFragments();
1975     }
1976
1977     protected void processAnnotations(Set<WebXml> fragments,
1978             boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) {
1979         for(WebXml fragment : fragments) {
1980             // Only need to scan for @HandlesTypes matches if any of the
1981             // following are true:
1982             // - it has already been determined only @HandlesTypes is required
1983             //   (e.g. main web.xml has metadata-complete="true"
1984             // - this fragment is for a container JAR (Servlet 3.1 section 8.1)
1985             // - this fragment has metadata-complete="true"
1986             boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() ||
1987                     fragment.isMetadataComplete();
1988
1989             WebXml annotations = new WebXml();
1990             // no impact on distributable
1991             annotations.setDistributable(true);
1992             URL url = fragment.getURL();
1993             processAnnotationsUrl(url, annotations, htOnly, javaClassCache);
1994             Set<WebXml> set = new HashSet<>();
1995             set.add(annotations);
1996             // Merge annotations into fragment - fragment takes priority
1997             fragment.merge(set);
1998         }
1999     }
2000
2001     protected void processAnnotationsWebResource(WebResource webResource,
2002             WebXml fragment, boolean handlesTypesOnly,
2003             Map<String,JavaClassCacheEntry> javaClassCache) {
2004
2005         if (webResource.isDirectory()) {
2006             WebResource[] webResources =
2007                     webResource.getWebResourceRoot().listResources(
2008                             webResource.getWebappPath());
2009             if (webResources.length > 0) {
2010                 if (log.isDebugEnabled()) {
2011                     log.debug(sm.getString(
2012                             "contextConfig.processAnnotationsWebDir.debug",
2013                             webResource.getURL()));
2014                 }
2015                 for (WebResource r : webResources) {
2016                     processAnnotationsWebResource(r, fragment, handlesTypesOnly, javaClassCache);
2017                 }
2018             }
2019         } else if (webResource.isFile() &&
2020                 webResource.getName().endsWith(".class")) {
2021             try (InputStream is = webResource.getInputStream()) {
2022                 processAnnotationsStream(is, fragment, handlesTypesOnly, javaClassCache);
2023             } catch (IOException e) {
2024                 log.error(sm.getString("contextConfig.inputStreamWebResource",
2025                         webResource.getWebappPath()),e);
2026             } catch (ClassFormatException e) {
2027                 log.error(sm.getString("contextConfig.inputStreamWebResource",
2028                         webResource.getWebappPath()),e);
2029             }
2030         }
2031     }
2032
2033
2034     protected void processAnnotationsUrl(URL url, WebXml fragment,
2035             boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) {
2036         if (url == null) {
2037             // Nothing to do.
2038             return;
2039         } else if ("jar".equals(url.getProtocol()) || url.toString().endsWith(".jar")) {
2040             processAnnotationsJar(url, fragment, handlesTypesOnly, javaClassCache);
2041         } else if ("file".equals(url.getProtocol())) {
2042             try {
2043                 processAnnotationsFile(
2044                         new File(url.toURI()), fragment, handlesTypesOnly, javaClassCache);
2045             } catch (URISyntaxException e) {
2046                 log.error(sm.getString("contextConfig.fileUrl", url), e);
2047             }
2048         } else {
2049             log.error(sm.getString("contextConfig.unknownUrlProtocol",
2050                     url.getProtocol(), url));
2051         }
2052
2053     }
2054
2055
2056     protected void processAnnotationsJar(URL url, WebXml fragment,
2057             boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) {
2058
2059         try (Jar jar = JarFactory.newInstance(url)) {
2060             if (log.isDebugEnabled()) {
2061                 log.debug(sm.getString(
2062                         "contextConfig.processAnnotationsJar.debug", url));
2063             }
2064
2065             jar.nextEntry();
2066             String entryName = jar.getEntryName();
2067             while (entryName != null) {
2068                 if (entryName.endsWith(".class")) {
2069                     try (InputStream is = jar.getEntryInputStream()) {
2070                         processAnnotationsStream(is, fragment, handlesTypesOnly, javaClassCache);
2071                     } catch (IOException e) {
2072                         log.error(sm.getString("contextConfig.inputStreamJar",
2073                                 entryName, url),e);
2074                     } catch (ClassFormatException e) {
2075                         log.error(sm.getString("contextConfig.inputStreamJar",
2076                                 entryName, url),e);
2077                     }
2078                 }
2079                 jar.nextEntry();
2080                 entryName = jar.getEntryName();
2081             }
2082         } catch (IOException e) {
2083             log.error(sm.getString("contextConfig.jarFile", url), e);
2084         }
2085     }
2086
2087
2088     protected void processAnnotationsFile(File file, WebXml fragment,
2089             boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) {
2090
2091         if (file.isDirectory()) {
2092             // Returns null if directory is not readable
2093             String[] dirs = file.list();
2094             if (dirs != null) {
2095                 if (log.isDebugEnabled()) {
2096                     log.debug(sm.getString(
2097                             "contextConfig.processAnnotationsDir.debug", file));
2098                 }
2099                 for (String dir : dirs) {
2100                     processAnnotationsFile(
2101                             new File(file,dir), fragment, handlesTypesOnly, javaClassCache);
2102                 }
2103             }
2104         } else if (file.getName().endsWith(".class") && file.canRead()) {
2105             try (FileInputStream fis = new FileInputStream(file)) {
2106                 processAnnotationsStream(fis, fragment, handlesTypesOnly, javaClassCache);
2107             } catch (IOException e) {
2108                 log.error(sm.getString("contextConfig.inputStreamFile",
2109                         file.getAbsolutePath()),e);
2110             } catch (ClassFormatException e) {
2111                 log.error(sm.getString("contextConfig.inputStreamFile",
2112                         file.getAbsolutePath()),e);
2113             }
2114         }
2115     }
2116
2117
2118     protected void processAnnotationsStream(InputStream is, WebXml fragment,
2119             boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache)
2120             throws ClassFormatException, IOException {
2121
2122         ClassParser parser = new ClassParser(is);
2123         JavaClass clazz = parser.parse();
2124         checkHandlesTypes(clazz, javaClassCache);
2125
2126         if (handlesTypesOnly) {
2127             return;
2128         }
2129
2130         processClass(fragment, clazz);
2131     }
2132
2133
2134     protected void processClass(WebXml fragment, JavaClass clazz) {
2135         AnnotationEntry[] annotationsEntries = clazz.getAnnotationEntries();
2136         if (annotationsEntries != null) {
2137             String className = clazz.getClassName();
2138             for (AnnotationEntry ae : annotationsEntries) {
2139                 String type = ae.getAnnotationType();
2140                 if ("Ljavax/servlet/annotation/WebServlet;".equals(type)) {
2141                     processAnnotationWebServlet(className, ae, fragment);
2142                 }else if ("Ljavax/servlet/annotation/WebFilter;".equals(type)) {
2143                     processAnnotationWebFilter(className, ae, fragment);
2144                 }else if ("Ljavax/servlet/annotation/WebListener;".equals(type)) {
2145                     fragment.addListener(className);
2146                 } else {
2147                     // Unknown annotation - ignore
2148                 }
2149             }
2150         }
2151     }
2152
2153
2154     /**
2155      * For classes packaged with the web application, the class and each
2156      * super class needs to be checked for a match with {@link HandlesTypes} or
2157      * for an annotation that matches {@link HandlesTypes}.
2158      * @param javaClass the class to check
2159      * @param javaClassCache a class cache
2160      */

2161     protected void checkHandlesTypes(JavaClass javaClass,
2162             Map<String,JavaClassCacheEntry> javaClassCache) {
2163
2164         // Skip this if we can
2165         if (typeInitializerMap.size() == 0) {
2166             return;
2167         }
2168
2169         if ((javaClass.getAccessFlags() &
2170                 org.apache.tomcat.util.bcel.Const.ACC_ANNOTATION) != 0) {
2171             // Skip annotations.
2172             return;
2173         }
2174
2175         String className = javaClass.getClassName();
2176
2177         Class<?> clazz = null;
2178         if (handlesTypesNonAnnotations) {
2179             // This *might* be match for a HandlesType.
2180             populateJavaClassCache(className, javaClass, javaClassCache);
2181             JavaClassCacheEntry entry = javaClassCache.get(className);
2182             if (entry.getSciSet() == null) {
2183                 try {
2184                     populateSCIsForCacheEntry(entry, javaClassCache);
2185                 } catch (StackOverflowError soe) {
2186                     throw new IllegalStateException(sm.getString(
2187                             "contextConfig.annotationsStackOverflow",
2188                             context.getName(),
2189                             classHierarchyToString(className, entry, javaClassCache)));
2190                 }
2191             }
2192             if (!entry.getSciSet().isEmpty()) {
2193                 // Need to try and load the class
2194                 clazz = Introspection.loadClass(context, className);
2195                 if (clazz == null) {
2196                     // Can't load the class so no point continuing
2197                     return;
2198                 }
2199
2200                 for (ServletContainerInitializer sci : entry.getSciSet()) {
2201                     Set<Class<?>> classes = initializerClassMap.get(sci);
2202                     if (classes == null) {
2203                         classes = new HashSet<>();
2204                         initializerClassMap.put(sci, classes);
2205                     }
2206                     classes.add(clazz);
2207                 }
2208             }
2209         }
2210
2211         if (handlesTypesAnnotations) {
2212             AnnotationEntry[] annotationEntries = javaClass.getAnnotationEntries();
2213             if (annotationEntries != null) {
2214                 for (Map.Entry<Class<?>, Set<ServletContainerInitializer>> entry :
2215                         typeInitializerMap.entrySet()) {
2216                     if (entry.getKey().isAnnotation()) {
2217                         String entryClassName = entry.getKey().getName();
2218                         for (AnnotationEntry annotationEntry : annotationEntries) {
2219                             if (entryClassName.equals(
2220                                     getClassName(annotationEntry.getAnnotationType()))) {
2221                                 if (clazz == null) {
2222                                     clazz = Introspection.loadClass(
2223                                             context, className);
2224                                     if (clazz == null) {
2225                                         // Can't load the class so no point
2226                                         // continuing
2227                                         return;
2228                                     }
2229                                 }
2230                                 for (ServletContainerInitializer sci : entry.getValue()) {
2231                                     initializerClassMap.get(sci).add(clazz);
2232                                 }
2233                                 break;
2234                             }
2235                         }
2236                     }
2237                 }
2238             }
2239         }
2240     }
2241
2242
2243     private String classHierarchyToString(String className,
2244             JavaClassCacheEntry entry, Map<String,JavaClassCacheEntry> javaClassCache) {
2245         JavaClassCacheEntry start = entry;
2246         StringBuilder msg = new StringBuilder(className);
2247         msg.append("->");
2248
2249         String parentName = entry.getSuperclassName();
2250         JavaClassCacheEntry parent = javaClassCache.get(parentName);
2251         int count = 0;
2252
2253         while (count < 100 && parent != null && parent != start) {
2254             msg.append(parentName);
2255             msg.append("->");
2256
2257             count ++;
2258             parentName = parent.getSuperclassName();
2259             parent = javaClassCache.get(parentName);
2260         }
2261
2262         msg.append(parentName);
2263
2264         return msg.toString();
2265     }
2266
2267     private void populateJavaClassCache(String className, JavaClass javaClass,
2268             Map<String,JavaClassCacheEntry> javaClassCache) {
2269         if (javaClassCache.containsKey(className)) {
2270             return;
2271         }
2272
2273         // Add this class to the cache
2274         javaClassCache.put(className, new JavaClassCacheEntry(javaClass));
2275
2276         populateJavaClassCache(javaClass.getSuperclassName(), javaClassCache);
2277
2278         for (String interfaceName : javaClass.getInterfaceNames()) {
2279             populateJavaClassCache(interfaceName, javaClassCache);
2280         }
2281     }
2282
2283     private void populateJavaClassCache(String className,
2284             Map<String,JavaClassCacheEntry> javaClassCache) {
2285         if (!javaClassCache.containsKey(className)) {
2286             String name = className.replace('.', '/') + ".class";
2287             try (InputStream is = context.getLoader().getClassLoader().getResourceAsStream(name)) {
2288                 if (is == null) {
2289                     return;
2290                 }
2291                 ClassParser parser = new ClassParser(is);
2292                 JavaClass clazz = parser.parse();
2293                 populateJavaClassCache(clazz.getClassName(), clazz, javaClassCache);
2294             } catch (ClassFormatException e) {
2295                 log.debug(sm.getString("contextConfig.invalidSciHandlesTypes",
2296                         className), e);
2297             } catch (IOException e) {
2298                 log.debug(sm.getString("contextConfig.invalidSciHandlesTypes",
2299                         className), e);
2300             }
2301         }
2302     }
2303
2304     private void populateSCIsForCacheEntry(JavaClassCacheEntry cacheEntry,
2305             Map<String,JavaClassCacheEntry> javaClassCache) {
2306         Set<ServletContainerInitializer> result = new HashSet<>();
2307
2308         // Super class
2309         String superClassName = cacheEntry.getSuperclassName();
2310         JavaClassCacheEntry superClassCacheEntry =
2311                 javaClassCache.get(superClassName);
2312
2313         // Avoid an infinite loop with java.lang.Object
2314         if (cacheEntry.equals(superClassCacheEntry)) {
2315             cacheEntry.setSciSet(EMPTY_SCI_SET);
2316             return;
2317         }
2318
2319         // May be null of the class is not present or could not be loaded.
2320         if (superClassCacheEntry != null) {
2321             if (superClassCacheEntry.getSciSet() == null) {
2322                 populateSCIsForCacheEntry(superClassCacheEntry, javaClassCache);
2323             }
2324             result.addAll(superClassCacheEntry.getSciSet());
2325         }
2326         result.addAll(getSCIsForClass(superClassName));
2327
2328         // Interfaces
2329         for (String interfaceName : cacheEntry.getInterfaceNames()) {
2330             JavaClassCacheEntry interfaceEntry =
2331                     javaClassCache.get(interfaceName);
2332             // A null could mean that the class not present in application or
2333             // that there is nothing of interest. Either way, nothing to do here
2334             // so move along
2335             if (interfaceEntry != null) {
2336                 if (interfaceEntry.getSciSet() == null) {
2337                     populateSCIsForCacheEntry(interfaceEntry, javaClassCache);
2338                 }
2339                 result.addAll(interfaceEntry.getSciSet());
2340             }
2341             result.addAll(getSCIsForClass(interfaceName));
2342         }
2343
2344         cacheEntry.setSciSet(result.isEmpty() ? EMPTY_SCI_SET : result);
2345     }
2346
2347     private Set<ServletContainerInitializer> getSCIsForClass(String className) {
2348         for (Map.Entry<Class<?>, Set<ServletContainerInitializer>> entry :
2349                 typeInitializerMap.entrySet()) {
2350             Class<?> clazz = entry.getKey();
2351             if (!clazz.isAnnotation()) {
2352                 if (clazz.getName().equals(className)) {
2353                     return entry.getValue();
2354                 }
2355             }
2356         }
2357         return EMPTY_SCI_SET;
2358     }
2359
2360     private static final String getClassName(String internalForm) {
2361         if (!internalForm.startsWith("L")) {
2362             return internalForm;
2363         }
2364
2365         // Assume starts with L, ends with ; and uses / rather than .
2366         return internalForm.substring(1,
2367                 internalForm.length() - 1).replace('/', '.');
2368     }
2369
2370     protected void processAnnotationWebServlet(String className,
2371             AnnotationEntry ae, WebXml fragment) {
2372         String servletName = null;
2373         // must search for name s. Spec Servlet API 3.0 - 8.2.3.3.n.ii page 81
2374         List<ElementValuePair> evps = ae.getElementValuePairs();
2375         for (ElementValuePair evp : evps) {
2376             String name = evp.getNameString();
2377             if ("name".equals(name)) {
2378                 servletName = evp.getValue().stringifyValue();
2379                 break;
2380             }
2381         }
2382         if (servletName == null) {
2383             // classname is default servletName as annotation has no name!
2384             servletName = className;
2385         }
2386         ServletDef servletDef = fragment.getServlets().get(servletName);
2387
2388         boolean isWebXMLservletDef;
2389         if (servletDef == null) {
2390             servletDef = new ServletDef();
2391             servletDef.setServletName(servletName);
2392             servletDef.setServletClass(className);
2393             isWebXMLservletDef = false;
2394         } else {
2395             isWebXMLservletDef = true;
2396         }
2397
2398         boolean urlPatternsSet = false;
2399         String[] urlPatterns = null;
2400
2401         // List<ElementValuePair> evps = ae.getElementValuePairs();
2402         for (ElementValuePair evp : evps) {
2403             String name = evp.getNameString();
2404             if ("value".equals(name) || "urlPatterns".equals(name)) {
2405                 if (urlPatternsSet) {
2406                     throw new IllegalArgumentException(sm.getString(
2407                             "contextConfig.urlPatternValue""WebServlet", className));
2408                 }
2409                 urlPatternsSet = true;
2410                 urlPatterns = processAnnotationsStringArray(evp.getValue());
2411             } else if ("description".equals(name)) {
2412                 if (servletDef.getDescription() == null) {
2413                     servletDef.setDescription(evp.getValue().stringifyValue());
2414                 }
2415             } else if ("displayName".equals(name)) {
2416                 if (servletDef.getDisplayName() == null) {
2417                     servletDef.setDisplayName(evp.getValue().stringifyValue());
2418                 }
2419             } else if ("largeIcon".equals(name)) {
2420                 if (servletDef.getLargeIcon() == null) {
2421                     servletDef.setLargeIcon(evp.getValue().stringifyValue());
2422                 }
2423             } else if ("smallIcon".equals(name)) {
2424                 if (servletDef.getSmallIcon() == null) {
2425                     servletDef.setSmallIcon(evp.getValue().stringifyValue());
2426                 }
2427             } else if ("asyncSupported".equals(name)) {
2428                 if (servletDef.getAsyncSupported() == null) {
2429                     servletDef.setAsyncSupported(evp.getValue()
2430                             .stringifyValue());
2431                 }
2432             } else if ("loadOnStartup".equals(name)) {
2433                 if (servletDef.getLoadOnStartup() == null) {
2434                     servletDef
2435                             .setLoadOnStartup(evp.getValue().stringifyValue());
2436                 }
2437             } else if ("initParams".equals(name)) {
2438                 Map<String, String> initParams = processAnnotationWebInitParams(evp
2439                         .getValue());
2440                 if (isWebXMLservletDef) {
2441                     Map<String, String> webXMLInitParams = servletDef
2442                             .getParameterMap();
2443                     for (Map.Entry<String, String> entry : initParams
2444                             .entrySet()) {
2445                         if (webXMLInitParams.get(entry.getKey()) == null) {
2446                             servletDef.addInitParameter(entry.getKey(), entry
2447                                     .getValue());
2448                         }
2449                     }
2450                 } else {
2451                     for (Map.Entry<String, String> entry : initParams
2452                             .entrySet()) {
2453                         servletDef.addInitParameter(entry.getKey(), entry
2454                                 .getValue());
2455                     }
2456                 }
2457             }
2458         }
2459         if (!isWebXMLservletDef && urlPatterns != null) {
2460             fragment.addServlet(servletDef);
2461         }
2462         if (urlPatterns != null) {
2463             if (!fragment.getServletMappings().containsValue(servletName)) {
2464                 for (String urlPattern : urlPatterns) {
2465                     fragment.addServletMapping(urlPattern, servletName);
2466                 }
2467             }
2468         }
2469
2470     }
2471
2472     /**
2473      * process filter annotation and merge with existing one!
2474      * FIXME: refactoring method too long and has redundant subroutines with
2475      *        processAnnotationWebServlet!
2476      * @param className The filter class name
2477      * @param ae The filter annotation
2478      * @param fragment The corresponding fragment
2479      */

2480     protected void processAnnotationWebFilter(String className,
2481             AnnotationEntry ae, WebXml fragment) {
2482         String filterName = null;
2483         // must search for name s. Spec Servlet API 3.0 - 8.2.3.3.n.ii page 81
2484         List<ElementValuePair> evps = ae.getElementValuePairs();
2485         for (ElementValuePair evp : evps) {
2486             String name = evp.getNameString();
2487             if ("filterName".equals(name)) {
2488                 filterName = evp.getValue().stringifyValue();
2489                 break;
2490             }
2491         }
2492         if (filterName == null) {
2493             // classname is default filterName as annotation has no name!
2494             filterName = className;
2495         }
2496         FilterDef filterDef = fragment.getFilters().get(filterName);
2497         FilterMap filterMap = new FilterMap();
2498
2499         boolean isWebXMLfilterDef;
2500         if (filterDef == null) {
2501             filterDef = new FilterDef();
2502             filterDef.setFilterName(filterName);
2503             filterDef.setFilterClass(className);
2504             isWebXMLfilterDef = false;
2505         } else {
2506             isWebXMLfilterDef = true;
2507         }
2508
2509         boolean urlPatternsSet = false;
2510         boolean servletNamesSet = false;
2511         boolean dispatchTypesSet = false;
2512         String[] urlPatterns = null;
2513
2514         for (ElementValuePair evp : evps) {
2515             String name = evp.getNameString();
2516             if ("value".equals(name) || "urlPatterns".equals(name)) {
2517                 if (urlPatternsSet) {
2518                     throw new IllegalArgumentException(sm.getString(
2519                             "contextConfig.urlPatternValue""WebFilter", className));
2520                 }
2521                 urlPatterns = processAnnotationsStringArray(evp.getValue());
2522                 urlPatternsSet = urlPatterns.length > 0;
2523                 for (String urlPattern : urlPatterns) {
2524                     // % decoded (if required) using UTF-8
2525                     filterMap.addURLPattern(urlPattern);
2526                 }
2527             } else if ("servletNames".equals(name)) {
2528                 String[] servletNames = processAnnotationsStringArray(evp
2529                         .getValue());
2530                 servletNamesSet = servletNames.length > 0;
2531                 for (String servletName : servletNames) {
2532                     filterMap.addServletName(servletName);
2533                 }
2534             } else if ("dispatcherTypes".equals(name)) {
2535                 String[] dispatcherTypes = processAnnotationsStringArray(evp
2536                         .getValue());
2537                 dispatchTypesSet = dispatcherTypes.length > 0;
2538                 for (String dispatcherType : dispatcherTypes) {
2539                     filterMap.setDispatcher(dispatcherType);
2540                 }
2541             } else if ("description".equals(name)) {
2542                 if (filterDef.getDescription() == null) {
2543                     filterDef.setDescription(evp.getValue().stringifyValue());
2544                 }
2545             } else if ("displayName".equals(name)) {
2546                 if (filterDef.getDisplayName() == null) {
2547                     filterDef.setDisplayName(evp.getValue().stringifyValue());
2548                 }
2549             } else if ("largeIcon".equals(name)) {
2550                 if (filterDef.getLargeIcon() == null) {
2551                     filterDef.setLargeIcon(evp.getValue().stringifyValue());
2552                 }
2553             } else if ("smallIcon".equals(name)) {
2554                 if (filterDef.getSmallIcon() == null) {
2555                     filterDef.setSmallIcon(evp.getValue().stringifyValue());
2556                 }
2557             } else if ("asyncSupported".equals(name)) {
2558                 if (filterDef.getAsyncSupported() == null) {
2559                     filterDef
2560                             .setAsyncSupported(evp.getValue().stringifyValue());
2561                 }
2562             } else if ("initParams".equals(name)) {
2563                 Map<String, String> initParams = processAnnotationWebInitParams(evp
2564                         .getValue());
2565                 if (isWebXMLfilterDef) {
2566                     Map<String, String> webXMLInitParams = filterDef
2567                             .getParameterMap();
2568                     for (Map.Entry<String, String> entry : initParams
2569                             .entrySet()) {
2570                         if (webXMLInitParams.get(entry.getKey()) == null) {
2571                             filterDef.addInitParameter(entry.getKey(), entry
2572                                     .getValue());
2573                         }
2574                     }
2575                 } else {
2576                     for (Map.Entry<String, String> entry : initParams
2577                             .entrySet()) {
2578                         filterDef.addInitParameter(entry.getKey(), entry
2579                                 .getValue());
2580                     }
2581                 }
2582
2583             }
2584         }
2585         if (!isWebXMLfilterDef) {
2586             fragment.addFilter(filterDef);
2587             if (urlPatternsSet || servletNamesSet) {
2588                 filterMap.setFilterName(filterName);
2589                 fragment.addFilterMapping(filterMap);
2590             }
2591         }
2592         if (urlPatternsSet || dispatchTypesSet) {
2593             Set<FilterMap> fmap = fragment.getFilterMappings();
2594             FilterMap descMap = null;
2595             for (FilterMap map : fmap) {
2596                 if (filterName.equals(map.getFilterName())) {
2597                     descMap = map;
2598                     break;
2599                 }
2600             }
2601             if (descMap != null) {
2602                 String[] urlsPatterns = descMap.getURLPatterns();
2603                 if (urlPatternsSet
2604                         && (urlsPatterns == null || urlsPatterns.length == 0)) {
2605                     for (String urlPattern : filterMap.getURLPatterns()) {
2606                         // % decoded (if required) using UTF-8
2607                         descMap.addURLPattern(urlPattern);
2608                     }
2609                 }
2610                 String[] dispatcherNames = descMap.getDispatcherNames();
2611                 if (dispatchTypesSet
2612                         && (dispatcherNames == null || dispatcherNames.length == 0)) {
2613                     for (String dis : filterMap.getDispatcherNames()) {
2614                         descMap.setDispatcher(dis);
2615                     }
2616                 }
2617             }
2618         }
2619
2620     }
2621
2622     protected String[] processAnnotationsStringArray(ElementValue ev) {
2623         List<String> values = new ArrayList<>();
2624         if (ev instanceof ArrayElementValue) {
2625             ElementValue[] arrayValues =
2626                 ((ArrayElementValue) ev).getElementValuesArray();
2627             for (ElementValue value : arrayValues) {
2628                 values.add(value.stringifyValue());
2629             }
2630         } else {
2631             values.add(ev.stringifyValue());
2632         }
2633         String[] result = new String[values.size()];
2634         return values.toArray(result);
2635     }
2636
2637     protected Map<String,String> processAnnotationWebInitParams(
2638             ElementValue ev) {
2639         Map<String, String> result = new HashMap<>();
2640         if (ev instanceof ArrayElementValue) {
2641             ElementValue[] arrayValues =
2642                 ((ArrayElementValue) ev).getElementValuesArray();
2643             for (ElementValue value : arrayValues) {
2644                 if (value instanceof AnnotationElementValue) {
2645                     List<ElementValuePair> evps = ((AnnotationElementValue) value)
2646                             .getAnnotationEntry().getElementValuePairs();
2647                     String initParamName = null;
2648                     String initParamValue = null;
2649                     for (ElementValuePair evp : evps) {
2650                         if ("name".equals(evp.getNameString())) {
2651                             initParamName = evp.getValue().stringifyValue();
2652                         } else if ("value".equals(evp.getNameString())) {
2653                             initParamValue = evp.getValue().stringifyValue();
2654                         } else {
2655                             // Ignore
2656                         }
2657                     }
2658                     result.put(initParamName, initParamValue);
2659                 }
2660             }
2661         }
2662         return result;
2663     }
2664
2665     private static class DefaultWebXmlCacheEntry {
2666         private final WebXml webXml;
2667         private final long globalTimeStamp;
2668         private final long hostTimeStamp;
2669
2670         public DefaultWebXmlCacheEntry(WebXml webXml, long globalTimeStamp,
2671                 long hostTimeStamp) {
2672             this.webXml = webXml;
2673             this.globalTimeStamp = globalTimeStamp;
2674             this.hostTimeStamp = hostTimeStamp;
2675         }
2676
2677         public WebXml getWebXml() {
2678             return webXml;
2679         }
2680
2681         public long getGlobalTimeStamp() {
2682             return globalTimeStamp;
2683         }
2684
2685         public long getHostTimeStamp() {
2686             return hostTimeStamp;
2687         }
2688     }
2689
2690     private static class HostWebXmlCacheCleaner implements LifecycleListener {
2691
2692         @Override
2693         public void lifecycleEvent(LifecycleEvent event) {
2694
2695             if (Lifecycle.AFTER_DESTROY_EVENT.equals(event.getType())) {
2696                 Host host = (Host) event.getSource();
2697                 hostWebXmlCache.remove(host);
2698             }
2699         }
2700     }
2701
2702     static class JavaClassCacheEntry {
2703         public final String superclassName;
2704
2705         public final String[] interfaceNames;
2706
2707         private Set<ServletContainerInitializer> sciSet = null;
2708
2709         public JavaClassCacheEntry(JavaClass javaClass) {
2710             superclassName = javaClass.getSuperclassName();
2711             interfaceNames = javaClass.getInterfaceNames();
2712         }
2713
2714         public String getSuperclassName() {
2715             return superclassName;
2716         }
2717
2718         public String[] getInterfaceNames() {
2719             return interfaceNames;
2720         }
2721
2722         public Set<ServletContainerInitializer> getSciSet() {
2723             return sciSet;
2724         }
2725
2726         public void setSciSet(Set<ServletContainerInitializer> sciSet) {
2727             this.sciSet = sciSet;
2728         }
2729     }
2730 }
2731