View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.webapp;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.net.MalformedURLException;
24  import java.net.URL;
25  import java.security.PermissionCollection;
26  import java.util.ArrayList;
27  import java.util.Arrays;
28  import java.util.Collection;
29  import java.util.Collections;
30  import java.util.EventListener;
31  import java.util.HashMap;
32  import java.util.HashSet;
33  import java.util.List;
34  import java.util.ListIterator;
35  import java.util.Map;
36  import java.util.Set;
37  
38  import javax.servlet.ServletContext;
39  import javax.servlet.ServletRegistration.Dynamic;
40  import javax.servlet.ServletSecurityElement;
41  import javax.servlet.http.HttpSessionActivationListener;
42  import javax.servlet.http.HttpSessionAttributeListener;
43  import javax.servlet.http.HttpSessionBindingListener;
44  import javax.servlet.http.HttpSessionListener;
45  
46  import org.eclipse.jetty.security.ConstraintAware;
47  import org.eclipse.jetty.security.ConstraintMapping;
48  import org.eclipse.jetty.security.ConstraintSecurityHandler;
49  import org.eclipse.jetty.security.SecurityHandler;
50  import org.eclipse.jetty.server.Connector;
51  import org.eclipse.jetty.server.HandlerContainer;
52  import org.eclipse.jetty.server.Server;
53  import org.eclipse.jetty.server.handler.ContextHandler;
54  import org.eclipse.jetty.server.handler.ErrorHandler;
55  import org.eclipse.jetty.server.session.SessionHandler;
56  import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
57  import org.eclipse.jetty.servlet.ServletContextHandler;
58  import org.eclipse.jetty.servlet.ServletHandler;
59  import org.eclipse.jetty.util.Loader;
60  import org.eclipse.jetty.util.MultiException;
61  import org.eclipse.jetty.util.URIUtil;
62  import org.eclipse.jetty.util.annotation.ManagedAttribute;
63  import org.eclipse.jetty.util.annotation.ManagedObject;
64  import org.eclipse.jetty.util.annotation.Name;
65  import org.eclipse.jetty.util.log.Log;
66  import org.eclipse.jetty.util.log.Logger;
67  import org.eclipse.jetty.util.resource.Resource;
68  import org.eclipse.jetty.util.resource.ResourceCollection;
69  
70  /* ------------------------------------------------------------ */
71  /** Web Application Context Handler.
72   * The WebAppContext handler is an extension of ContextHandler that
73   * coordinates the construction and configuration of nested handlers:
74   * {@link org.eclipse.jetty.security.ConstraintSecurityHandler}, {@link org.eclipse.jetty.server.session.SessionHandler}
75   * and {@link org.eclipse.jetty.servlet.ServletHandler}.
76   * The handlers are configured by pluggable configuration classes, with
77   * the default being  {@link org.eclipse.jetty.webapp.WebXmlConfiguration} and
78   * {@link org.eclipse.jetty.webapp.JettyWebXmlConfiguration}.
79   *
80   * @org.apache.xbean.XBean description="Creates a servlet web application at a given context from a resource base"
81   *
82   */
83  @ManagedObject("Web Application ContextHandler")
84  public class WebAppContext extends ServletContextHandler implements WebAppClassLoader.Context
85  {
86      private static final Logger LOG = Log.getLogger(WebAppContext.class);
87  
88      public static final String TEMPDIR = "javax.servlet.context.tempdir";
89      public static final String BASETEMPDIR = "org.eclipse.jetty.webapp.basetempdir";
90      public final static String WEB_DEFAULTS_XML="org/eclipse/jetty/webapp/webdefault.xml";
91      public final static String ERROR_PAGE="org.eclipse.jetty.server.error_page";
92      public final static String SERVER_SYS_CLASSES = "org.eclipse.jetty.webapp.systemClasses";
93      public final static String SERVER_SRV_CLASSES = "org.eclipse.jetty.webapp.serverClasses";
94  
95      private String[] __dftProtectedTargets = {"/web-inf", "/meta-inf"};
96  
97      public static final String[] DEFAULT_CONFIGURATION_CLASSES =
98      {
99          "org.eclipse.jetty.webapp.WebInfConfiguration",
100         "org.eclipse.jetty.webapp.WebXmlConfiguration",
101         "org.eclipse.jetty.webapp.MetaInfConfiguration",
102         "org.eclipse.jetty.webapp.FragmentConfiguration",
103         "org.eclipse.jetty.webapp.JettyWebXmlConfiguration"
104     } ;
105 
106     // System classes are classes that cannot be replaced by
107     // the web application, and they are *always* loaded via
108     // system classloader.
109     public final static String[] __dftSystemClasses =
110     {
111         "java.",                            // Java SE classes (per servlet spec v2.5 / SRV.9.7.2)
112         "javax.",                           // Java SE classes (per servlet spec v2.5 / SRV.9.7.2)
113         "org.xml.",                         // needed by javax.xml
114         "org.w3c.",                         // needed by javax.xml
115         "org.eclipse.jetty.continuation.",  // webapp cannot change continuation classes
116         "org.eclipse.jetty.jndi.",          // webapp cannot change naming classes
117         "org.eclipse.jetty.jaas.",          // webapp cannot change jaas classes
118         "org.eclipse.jetty.websocket.",     // webapp cannot change / replace websocket classes
119         "org.eclipse.jetty.servlet.DefaultServlet" // webapp cannot change default servlets
120     } ;
121 
122     // Server classes are classes that are hidden from being
123     // loaded by the web application using system classloader,
124     // so if web application needs to load any of such classes,
125     // it has to include them in its distribution.
126     public final static String[] __dftServerClasses =
127     {
128         "-org.eclipse.jetty.continuation.", // don't hide continuation classes
129         "-org.eclipse.jetty.jndi.",         // don't hide naming classes
130         "-org.eclipse.jetty.jaas.",         // don't hide jaas classes
131         "-org.eclipse.jetty.servlets.",     // don't hide jetty servlets
132         "-org.eclipse.jetty.servlet.DefaultServlet", // don't hide default servlet
133         "-org.eclipse.jetty.servlet.listener.", // don't hide useful listeners
134         "-org.eclipse.jetty.websocket.",    // don't hide websocket classes from webapps (allow webapp to use ones from system classloader)
135         "org.eclipse.jetty."                // hide other jetty classes
136     } ;
137 
138     private final List<String> _configurationClasses = new ArrayList<>();
139     private ClasspathPattern _systemClasses = null;
140     private ClasspathPattern _serverClasses = null;
141 
142     private final List<Configuration> _configurations = new ArrayList<>();
143     private String _defaultsDescriptor=WEB_DEFAULTS_XML;
144     private String _descriptor=null;
145     private final List<String> _overrideDescriptors = new ArrayList<>();
146     private boolean _distributable=false;
147     private boolean _extractWAR=true;
148     private boolean _copyDir=false;
149     private boolean _copyWebInf=false;
150     private boolean _logUrlOnStart =false;
151     private boolean _parentLoaderPriority= Boolean.getBoolean("org.eclipse.jetty.server.webapp.parentLoaderPriority");
152     private PermissionCollection _permissions;
153 
154     private String[] _contextWhiteList = null;
155 
156     private File _tmpDir;
157     private String _war;
158     private String _extraClasspath;
159     private Throwable _unavailableException;
160 
161     private Map<String, String> _resourceAliases;
162     private boolean _ownClassLoader=false;
163     private boolean _configurationDiscovered=true;
164     private boolean _allowDuplicateFragmentNames = false;
165     private boolean _throwUnavailableOnStartupException = false;
166 
167 
168 
169     private MetaData _metadata=new MetaData();
170 
171     public static WebAppContext getCurrentWebAppContext()
172     {
173         ContextHandler.Context context=ContextHandler.getCurrentContext();
174         if (context!=null)
175         {
176             ContextHandler handler = context.getContextHandler();
177             if (handler instanceof WebAppContext)
178                 return (WebAppContext)handler;
179         }
180         return null;
181     }
182 
183     /* ------------------------------------------------------------ */
184     public WebAppContext()
185     {
186         this(null,null,null,null,null,new ErrorPageErrorHandler(),SESSIONS|SECURITY);
187     }
188 
189     /* ------------------------------------------------------------ */
190     /**
191      * @param contextPath The context path
192      * @param webApp The URL or filename of the webapp directory or war file.
193      */
194     public WebAppContext(String webApp,String contextPath)
195     {
196         this(null,contextPath,null,null,null,new ErrorPageErrorHandler(),SESSIONS|SECURITY);
197         setWar(webApp);
198     }
199 
200     /* ------------------------------------------------------------ */
201     /**
202      * @param parent The parent HandlerContainer.
203      * @param contextPath The context path
204      * @param webApp The URL or filename of the webapp directory or war file.
205      */
206     public WebAppContext(HandlerContainer parent, String webApp, String contextPath)
207     {
208         this(parent,contextPath,null,null,null,new ErrorPageErrorHandler(),SESSIONS|SECURITY);
209         setWar(webApp);
210     }
211 
212     /* ------------------------------------------------------------ */
213 
214     /**
215      * This constructor is used in the geronimo integration.
216      *
217      * @param sessionHandler SessionHandler for this web app
218      * @param securityHandler SecurityHandler for this web app
219      * @param servletHandler ServletHandler for this web app
220      * @param errorHandler ErrorHandler for this web app
221      */
222     public WebAppContext(SessionHandler sessionHandler, SecurityHandler securityHandler, ServletHandler servletHandler, ErrorHandler errorHandler) 
223     {
224         this(null, null, sessionHandler, securityHandler, servletHandler, errorHandler,0);
225     }
226 
227     /* ------------------------------------------------------------ */
228     /**
229      * This constructor is used in the geronimo integration.
230      *
231      * @param sessionHandler SessionHandler for this web app
232      * @param securityHandler SecurityHandler for this web app
233      * @param servletHandler ServletHandler for this web app
234      * @param errorHandler ErrorHandler for this web app
235      */
236     public WebAppContext(HandlerContainer parent, String contextPath, SessionHandler sessionHandler, SecurityHandler securityHandler, ServletHandler servletHandler, ErrorHandler errorHandler,int options) 
237     {
238         super(parent, contextPath,sessionHandler, securityHandler, servletHandler, errorHandler,options);
239         _scontext = new Context();
240         setErrorHandler(errorHandler != null ? errorHandler : new ErrorPageErrorHandler());
241         setProtectedTargets(__dftProtectedTargets);
242     }
243 
244     /* ------------------------------------------------------------ */
245     /**
246      * @param servletContextName The servletContextName to set.
247      */
248     @Override
249     public void setDisplayName(String servletContextName)
250     {
251         super.setDisplayName(servletContextName);
252         ClassLoader cl = getClassLoader();
253         if (cl!=null && cl instanceof WebAppClassLoader && servletContextName!=null)
254             ((WebAppClassLoader)cl).setName(servletContextName);
255     }
256 
257     /* ------------------------------------------------------------ */
258     /** Get an exception that caused the webapp to be unavailable
259      * @return A throwable if the webapp is unavailable or null
260      */
261     public Throwable getUnavailableException()
262     {
263         return _unavailableException;
264     }
265 
266 
267     /* ------------------------------------------------------------ */
268     /** Set Resource Alias.
269      * Resource aliases map resource uri's within a context.
270      * They may optionally be used by a handler when looking for
271      * a resource.
272      * @param alias
273      * @param uri
274      */
275     public void setResourceAlias(String alias, String uri)
276     {
277         if (_resourceAliases == null)
278             _resourceAliases= new HashMap<String, String>(5);
279         _resourceAliases.put(alias, uri);
280     }
281 
282     /* ------------------------------------------------------------ */
283     public Map<String, String> getResourceAliases()
284     {
285         if (_resourceAliases == null)
286             return null;
287         return _resourceAliases;
288     }
289 
290     /* ------------------------------------------------------------ */
291     public void setResourceAliases(Map<String, String> map)
292     {
293         _resourceAliases = map;
294     }
295 
296     /* ------------------------------------------------------------ */
297     public String getResourceAlias(String path)
298     {
299         if (_resourceAliases == null)
300             return null;
301         String alias = _resourceAliases.get(path);
302 
303         int slash=path.length();
304         while (alias==null)
305         {
306             slash=path.lastIndexOf("/",slash-1);
307             if (slash<0)
308                 break;
309             String match=_resourceAliases.get(path.substring(0,slash+1));
310             if (match!=null)
311                 alias=match+path.substring(slash+1);
312         }
313         return alias;
314     }
315 
316     /* ------------------------------------------------------------ */
317     public String removeResourceAlias(String alias)
318     {
319         if (_resourceAliases == null)
320             return null;
321         return _resourceAliases.remove(alias);
322     }
323 
324     /* ------------------------------------------------------------ */
325     /* (non-Javadoc)
326      * @see org.eclipse.jetty.server.server.handler.ContextHandler#setClassLoader(java.lang.ClassLoader)
327      */
328     @Override
329     public void setClassLoader(ClassLoader classLoader)
330     {
331         super.setClassLoader(classLoader);
332 
333 //        if ( !(classLoader instanceof WebAppClassLoader) )
334 //        {
335 //            LOG.info("NOTE: detected a classloader which is not an instance of WebAppClassLoader being set on WebAppContext, some typical class and resource locations may be missing on: " + toString() );
336 //        }
337 
338         if (classLoader!=null && classLoader instanceof WebAppClassLoader && getDisplayName()!=null)
339             ((WebAppClassLoader)classLoader).setName(getDisplayName());
340     }
341 
342     /* ------------------------------------------------------------ */
343     @Override
344     public Resource getResource(String uriInContext) throws MalformedURLException
345     {
346         if (uriInContext==null || !uriInContext.startsWith(URIUtil.SLASH))
347             throw new MalformedURLException(uriInContext);
348 
349         IOException ioe= null;
350         Resource resource= null;
351         int loop=0;
352         while (uriInContext!=null && loop++<100)
353         {
354             try
355             {
356                 resource= super.getResource(uriInContext);
357                 if (resource != null && resource.exists())
358                     return resource;
359 
360                 uriInContext = getResourceAlias(uriInContext);
361             }
362             catch (IOException e)
363             {
364                 LOG.ignore(e);
365                 if (ioe==null)
366                     ioe= e;
367             }
368         }
369 
370         if (ioe != null && ioe instanceof MalformedURLException)
371             throw (MalformedURLException)ioe;
372 
373         return resource;
374     }
375 
376 
377     /* ------------------------------------------------------------ */
378     /** Is the context Automatically configured.
379      *
380      * @return true if configuration discovery.
381      */
382     public boolean isConfigurationDiscovered()
383     {
384         return _configurationDiscovered;
385     }
386 
387     /* ------------------------------------------------------------ */
388     /** Set the configuration discovery mode.
389      * If configuration discovery is set to true, then the JSR315
390      * servlet 3.0 discovered configuration features are enabled.
391      * These are:<ul>
392      * <li>Web Fragments</li>
393      * <li>META-INF/resource directories</li>
394      * </ul>
395      * @param discovered true if configuration discovery is enabled for automatic configuration from the context
396      */
397     public void setConfigurationDiscovered(boolean discovered)
398     {
399         _configurationDiscovered = discovered;
400     }
401 
402     /* ------------------------------------------------------------ */
403     /** Pre configure the web application.
404      * <p>
405      * The method is normally called from {@link #start()}. It performs
406      * the discovery of the configurations to be applied to this context,
407      * specifically:<ul>
408      * <li>Instantiate the {@link Configuration} instances with a call to {@link #loadConfigurations()}.
409      * <li>Setup the default System classes by calling {@link #loadSystemClasses()}
410      * <li>Setup the default Server classes by calling <code>loadServerClasses()</code>
411      * <li>Instantiates a classload (if one is not already set)
412      * <li>Calls the {@link Configuration#preConfigure(WebAppContext)} method of all
413      * Configuration instances.
414      * </ul>
415      * @throws Exception
416      */
417     public void preConfigure() throws Exception
418     {
419         // Setup configurations
420         loadConfigurations();
421 
422         // Setup system classes
423         loadSystemClasses();
424 
425         // Setup server classes
426         loadServerClasses();
427 
428         // Configure classloader
429         _ownClassLoader=false;
430         if (getClassLoader()==null)
431         {
432             WebAppClassLoader classLoader = new WebAppClassLoader(this);
433             setClassLoader(classLoader);
434             _ownClassLoader=true;
435         }
436 
437         if (LOG.isDebugEnabled())
438         {
439             ClassLoader loader = getClassLoader();
440             LOG.debug("Thread Context classloader {}",loader);
441             loader=loader.getParent();
442             while(loader!=null)
443             {
444                 LOG.debug("Parent class loader: {} ",loader);
445                 loader=loader.getParent();
446             }
447         }
448 
449         // Prepare for configuration
450         for (Configuration configuration : _configurations)
451         {
452             LOG.debug("preConfigure {} with {}",this,configuration);
453             configuration.preConfigure(this);
454         }
455     }
456 
457     /* ------------------------------------------------------------ */
458     public void configure() throws Exception
459     {
460         // Configure webapp
461         for (Configuration configuration : _configurations)
462         {
463             LOG.debug("configure {} with {}",this,configuration);
464             configuration.configure(this);
465         }
466     }
467 
468     /* ------------------------------------------------------------ */
469     public void postConfigure() throws Exception
470     {
471         // Clean up after configuration
472         for (Configuration configuration : _configurations)
473         {
474             LOG.debug("postConfigure {} with {}",this,configuration);
475             configuration.postConfigure(this);
476         }
477     }
478 
479     /* ------------------------------------------------------------ */
480     /*
481      * @see org.eclipse.thread.AbstractLifeCycle#doStart()
482      */
483     @Override
484     protected void doStart() throws Exception
485     {
486         try
487         {
488             _metadata.setAllowDuplicateFragmentNames(isAllowDuplicateFragmentNames());
489             preConfigure();
490             super.doStart();
491             postConfigure();
492 
493             if (isLogUrlOnStart())
494                 dumpUrl();
495         }
496         catch (Exception e)
497         {
498             //start up of the webapp context failed, make sure it is not started
499             LOG.warn("Failed startup of context "+this, e);
500             _unavailableException=e;
501             setAvailable(false);
502             if (isThrowUnavailableOnStartupException())
503                 throw e;
504         }
505     }
506 
507     /* ------------------------------------------------------------ */
508     /*
509      * @see org.eclipse.thread.AbstractLifeCycle#doStop()
510      */
511     @Override
512     protected void doStop() throws Exception
513     {
514         super.doStop();
515 
516         try
517         {
518             for (int i=_configurations.size();i-->0;)
519                 _configurations.get(i).deconfigure(this);
520 
521             if (_metadata != null)
522                 _metadata.clear();
523             _metadata=new MetaData();
524 
525         }
526         finally
527         {
528             if (_ownClassLoader)
529                 setClassLoader(null);
530 
531             setAvailable(true);
532             _unavailableException=null;
533         }
534     }
535 
536     /* ------------------------------------------------------------ */
537     @Override
538     public void destroy()
539     {
540         // Prepare for configuration
541         MultiException mx=new MultiException();
542         if (_configurations!=null)
543         {
544             for (int i=_configurations.size();i-->0;)
545             {
546                 try
547                 {
548                     _configurations.get(i).destroy(this);
549                 }
550                 catch(Exception e)
551                 {
552                     mx.add(e);
553                 }
554             }
555         }
556         _configurations.clear();
557         super.destroy();
558         mx.ifExceptionThrowRuntime();
559     }
560 
561 
562     /* ------------------------------------------------------------ */
563     /*
564      * Dumps the current web app name and URL to the log
565      */
566     private void dumpUrl()
567     {
568         Connector[] connectors = getServer().getConnectors();
569         for (int i=0;i<connectors.length;i++)
570         {
571             String displayName = getDisplayName();
572             if (displayName == null)
573                 displayName = "WebApp@"+connectors.hashCode();
574 
575             LOG.info(displayName + " at http://" + connectors[i].toString() + getContextPath());
576         }
577     }
578 
579     /* ------------------------------------------------------------ */
580     /**
581      * @return Returns the configurations.
582      */
583     @ManagedAttribute(value="configuration classes used to configure webapp", readonly=true)
584     public String[] getConfigurationClasses()
585     {
586         return _configurationClasses.toArray(new String[_configurationClasses.size()]);
587     }
588 
589     /* ------------------------------------------------------------ */
590     /**
591      * @return Returns the configurations.
592      */
593     public Configuration[] getConfigurations()
594     {
595         return _configurations.toArray(new Configuration[_configurations.size()]);
596     }
597 
598     /* ------------------------------------------------------------ */
599     /**
600      * The default descriptor is a web.xml format file that is applied to the context before the standard WEB-INF/web.xml
601      * @return Returns the defaultsDescriptor.
602      */
603     @ManagedAttribute(value="default web.xml deascriptor applied before standard web.xml", readonly=true)
604     public String getDefaultsDescriptor()
605     {
606         return _defaultsDescriptor;
607     }
608 
609     /* ------------------------------------------------------------ */
610     /**
611      * The override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml
612      * @return Returns the Override Descriptor.
613      */
614     public String getOverrideDescriptor()
615     {
616         if (_overrideDescriptors.size()!=1)
617             return null;
618         return _overrideDescriptors.get(0);
619     }
620 
621     /* ------------------------------------------------------------ */
622     /**
623      * An override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml
624      * @return Returns the Override Descriptor list
625      */
626     @ManagedAttribute(value="web.xml deascriptors applied after standard web.xml", readonly=true)
627     public List<String> getOverrideDescriptors()
628     {
629         return Collections.unmodifiableList(_overrideDescriptors);
630     }
631 
632     /* ------------------------------------------------------------ */
633     /**
634      * @return Returns the permissions.
635      */
636     @Override
637     public PermissionCollection getPermissions()
638     {
639         return _permissions;
640     }
641 
642     /* ------------------------------------------------------------ */
643     /**
644      * @see #setServerClasses(String[])
645      * @return Returns the serverClasses.
646      */
647     @ManagedAttribute(value="classes and packages hidden by the context classloader", readonly=true)
648     public String[] getServerClasses()
649     {
650         if (_serverClasses == null)
651             loadServerClasses();
652 
653         return _serverClasses.getPatterns();
654     }
655 
656     /* ------------------------------------------------------------ */
657     /** Add to the list of Server classes.
658      * @see #setServerClasses(String[])
659      * @see http://www.eclipse.org/jetty/documentation/current/jetty-classloading.html
660      * @param classOrPackage A fully qualified class name (eg com.foo.MyClass) 
661      * or a qualified package name ending with '.' (eg com.foo.).  If the class 
662      * or package has '-' it is excluded from the server classes and order is thus
663      * important when added system class patterns. This argument may also be a comma 
664      * separated list of classOrPackage patterns.
665      */
666     public void addServerClass(String classOrPackage)
667     {
668         if (_serverClasses == null)
669             loadServerClasses();
670 
671         _serverClasses.addPattern(classOrPackage);
672     }
673 
674     /* ------------------------------------------------------------ */
675     /** Prepend to the list of Server classes.
676      * @see #setServerClasses(String[])
677      * @see http://www.eclipse.org/jetty/documentation/current/jetty-classloading.html
678      * @param classOrPackage A fully qualified class name (eg com.foo.MyClass) 
679      * or a qualified package name ending with '.' (eg com.foo.).  If the class 
680      * or package has '-' it is excluded from the server classes and order is thus
681      * important when added system class patterns. This argument may also be a comma 
682      * separated list of classOrPackage patterns.
683      */
684     public void prependServerClass(String classOrPackage)
685     {
686         if (_serverClasses == null)
687             loadServerClasses();
688 
689         _serverClasses.prependPattern(classOrPackage);
690     }
691 
692     /* ------------------------------------------------------------ */
693     /**
694      * @see #setSystemClasses(String[])
695      * @return Returns the systemClasses.
696      */
697     @ManagedAttribute(value="classes and packages given priority by context classloader", readonly=true)
698     public String[] getSystemClasses()
699     {
700         if (_systemClasses == null)
701             loadSystemClasses();
702 
703         return _systemClasses.getPatterns();
704     }
705 
706     /* ------------------------------------------------------------ */
707     /** Add to the list of System classes.
708      * @see http://www.eclipse.org/jetty/documentation/current/jetty-classloading.html
709      * @see #setSystemClasses(String[])
710      * @param classOrPackage A fully qualified class name (eg com.foo.MyClass) 
711      * or a qualified package name ending with '.' (eg com.foo.).  If the class 
712      * or package has '-' it is excluded from the system classes and order is thus
713      * important when added system class patterns.  This argument may also be a comma 
714      * separated list of classOrPackage patterns.
715      */
716     public void addSystemClass(String classOrPackage)
717     {
718         if (_systemClasses == null)
719             loadSystemClasses();
720 
721         _systemClasses.addPattern(classOrPackage);
722     }
723 
724 
725     /* ------------------------------------------------------------ */
726     /** Prepend to the list of System classes.
727      * @see #setSystemClasses(String[])
728      * @see http://www.eclipse.org/jetty/documentation/current/jetty-classloading.html
729      * @param classOrPackage A fully qualified class name (eg com.foo.MyClass) 
730      * or a qualified package name ending with '.' (eg com.foo.).  If the class 
731      * or package has '-' it is excluded from the system classes and order is thus
732      * important when added system class patterns.This argument may also be a comma 
733      * separated list of classOrPackage patterns.
734      */
735     public void prependSystemClass(String classOrPackage)
736     {
737         if (_systemClasses == null)
738             loadSystemClasses();
739 
740         _systemClasses.prependPattern(classOrPackage);
741     }
742 
743     /* ------------------------------------------------------------ */
744     @Override
745     public boolean isServerClass(String name)
746     {
747         if (_serverClasses == null)
748             loadServerClasses();
749 
750         return _serverClasses.match(name);
751     }
752 
753     /* ------------------------------------------------------------ */
754     @Override
755     public boolean isSystemClass(String name)
756     {
757         if (_systemClasses == null)
758             loadSystemClasses();
759 
760         return _systemClasses.match(name);
761     }
762 
763     /* ------------------------------------------------------------ */
764     protected void loadSystemClasses()
765     {
766         if (_systemClasses != null)
767             return;
768 
769         //look for a Server attribute with the list of System classes
770         //to apply to every web application. If not present, use our defaults.
771         Server server = getServer();
772         if (server != null)
773         {
774             Object systemClasses = server.getAttribute(SERVER_SYS_CLASSES);
775             if (systemClasses != null && systemClasses instanceof String[])
776                 _systemClasses = new ClasspathPattern((String[])systemClasses);
777         }
778 
779         if (_systemClasses == null)
780             _systemClasses = new ClasspathPattern(__dftSystemClasses);
781     }
782 
783     /* ------------------------------------------------------------ */
784     private void loadServerClasses()
785     {
786         if (_serverClasses != null)
787         {
788             return;
789         }
790 
791         // look for a Server attribute with the list of Server classes
792         // to apply to every web application. If not present, use our defaults.
793         Server server = getServer();
794         if (server != null)
795         {
796             Object serverClasses = server.getAttribute(SERVER_SRV_CLASSES);
797             if (serverClasses != null && serverClasses instanceof String[])
798             {
799                 _serverClasses = new ClasspathPattern((String[])serverClasses);
800             }
801         }
802 
803         if (_serverClasses == null)
804         {
805             _serverClasses = new ClasspathPattern(__dftServerClasses);
806         }
807     }
808 
809     /* ------------------------------------------------------------ */
810     /**
811      * @return Returns the war as a file or URL string (Resource)
812      */
813     @ManagedAttribute(value="war file location", readonly=true)
814     public String getWar()
815     {
816         if (_war==null)
817             _war=getResourceBase();
818         return _war;
819     }
820 
821     /* ------------------------------------------------------------ */
822     public Resource getWebInf() throws IOException
823     {
824         if (super.getBaseResource() == null)
825             return null;
826 
827         // Iw there a WEB-INF directory?
828         Resource web_inf= super.getBaseResource().addPath("WEB-INF/");
829         if (!web_inf.exists() || !web_inf.isDirectory())
830             return null;
831 
832         return web_inf;
833     }
834 
835     /* ------------------------------------------------------------ */
836     /**
837      * @return Returns the distributable.
838      */
839     @ManagedAttribute("web application distributable")
840     public boolean isDistributable()
841     {
842         return _distributable;
843     }
844 
845     /* ------------------------------------------------------------ */
846     /**
847      * @return Returns the extractWAR.
848      */
849     @ManagedAttribute(value="extract war", readonly=true)
850     public boolean isExtractWAR()
851     {
852         return _extractWAR;
853     }
854 
855     /* ------------------------------------------------------------ */
856     /**
857      * @return True if the webdir is copied (to allow hot replacement of jars on windows)
858      */
859     @ManagedAttribute(value="webdir copied on deploy (allows hot replacement on windows)", readonly=true)
860     public boolean isCopyWebDir()
861     {
862         return _copyDir;
863     }
864 
865     /* ------------------------------------------------------------ */
866     /**
867      * @return True if the web-inf lib and classes directories are copied (to allow hot replacement of jars on windows)
868      */
869     public boolean isCopyWebInf()
870     {
871         return _copyWebInf;
872     }
873 
874     /* ------------------------------------------------------------ */
875     /**
876      * @return True if the classloader should delegate first to the parent
877      * classloader (standard java behaviour) or false if the classloader
878      * should first try to load from WEB-INF/lib or WEB-INF/classes (servlet
879      * spec recommendation). Default is false or can be set by the system 
880      * property org.eclipse.jetty.server.webapp.parentLoaderPriority
881      */
882     @Override
883     @ManagedAttribute(value="parent classloader given priority", readonly=true)
884     public boolean isParentLoaderPriority()
885     {
886         return _parentLoaderPriority;
887     }
888 
889 
890     /* ------------------------------------------------------------ */
891     public static String[] getDefaultConfigurationClasses ()
892     {
893         return DEFAULT_CONFIGURATION_CLASSES;
894     }
895 
896     /* ------------------------------------------------------------ */
897     public String[] getDefaultServerClasses ()
898     {
899         return __dftServerClasses;
900     }
901 
902     /* ------------------------------------------------------------ */
903     public String[] getDefaultSystemClasses ()
904     {
905         return __dftSystemClasses;
906     }
907 
908     /* ------------------------------------------------------------ */
909     protected void loadConfigurations()
910     	throws Exception
911     {
912         //if the configuration instances have been set explicitly, use them
913         if (_configurations.size()>0)
914             return;
915         
916         if (_configurationClasses.size()==0)
917             _configurationClasses.addAll(Configuration.ClassList.serverDefault(getServer()));
918         for (String configClass : _configurationClasses)
919             _configurations.add((Configuration)Loader.loadClass(this.getClass(), configClass).newInstance());
920     }
921 
922     /* ------------------------------------------------------------ */
923     @Override
924     public String toString()
925     {
926         if (_war!=null)
927         {
928             String war=_war;
929             if (war.indexOf("/webapps/")>=0)
930                 war=war.substring(war.indexOf("/webapps/")+8);
931             return super.toString()+"{"+war+"}";
932         }
933         return super.toString();
934     }
935 
936     /* ------------------------------------------------------------ */
937     /**
938      * @param configurations The configuration class names.  If setConfigurations is not called
939      * these classes are used to create a configurations array.
940      */
941     public void setConfigurationClasses(String[] configurations)
942     {
943         if (isRunning())
944             throw new IllegalStateException();
945         _configurationClasses.clear();
946         if (configurations!=null)
947             _configurationClasses.addAll(Arrays.asList(configurations));
948         _configurations.clear();
949     }
950 
951     public void setConfigurationClasses(List<String> configurations)
952     {
953         setConfigurationClasses(configurations.toArray(new String[configurations.size()]));
954     }
955     
956     /* ------------------------------------------------------------ */
957     /**
958      * @param configurations The configurations to set.
959      */
960     public void setConfigurations(Configuration[] configurations)
961     {
962         if (isRunning())
963             throw new IllegalStateException();
964         _configurations.clear();
965         if (configurations!=null)
966             _configurations.addAll(Arrays.asList(configurations));
967     }
968 
969     /* ------------------------------------------------------------ */
970     /**
971      * The default descriptor is a web.xml format file that is applied to the context before the standard WEB-INF/web.xml
972      * @param defaultsDescriptor The defaultsDescriptor to set.
973      */
974     public void setDefaultsDescriptor(String defaultsDescriptor)
975     {
976         _defaultsDescriptor = defaultsDescriptor;
977     }
978 
979     /* ------------------------------------------------------------ */
980     /**
981      * The override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml
982      * @param overrideDescriptor The overrideDescritpor to set.
983      */
984     public void setOverrideDescriptor(String overrideDescriptor)
985     {
986         _overrideDescriptors.clear();
987         _overrideDescriptors.add(overrideDescriptor);
988     }
989 
990     /* ------------------------------------------------------------ */
991     /**
992      * The override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml
993      * @param overrideDescriptors The overrideDescriptors (file or URL) to set.
994      */
995     public void setOverrideDescriptors(List<String> overrideDescriptors)
996     {
997         _overrideDescriptors.clear();
998         _overrideDescriptors.addAll(overrideDescriptors);
999     }
1000 
1001     /* ------------------------------------------------------------ */
1002     /**
1003      * The override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml
1004      * @param overrideDescriptor The overrideDescriptor (file or URL) to add.
1005      */
1006     public void addOverrideDescriptor(String overrideDescriptor)
1007     {
1008         _overrideDescriptors.add(overrideDescriptor);
1009     }
1010 
1011     /* ------------------------------------------------------------ */
1012     /**
1013      * @return the web.xml descriptor to use. If set to null, WEB-INF/web.xml is used if it exists.
1014      */
1015     @ManagedAttribute(value="standard web.xml descriptor", readonly=true)
1016     public String getDescriptor()
1017     {
1018         return _descriptor;
1019     }
1020 
1021     /* ------------------------------------------------------------ */
1022     /**
1023      * @param descriptor the web.xml descriptor to use. If set to null, WEB-INF/web.xml is used if it exists.
1024      */
1025     public void setDescriptor(String descriptor)
1026     {
1027         _descriptor=descriptor;
1028     }
1029 
1030     /* ------------------------------------------------------------ */
1031     /**
1032      * @param distributable The distributable to set.
1033      */
1034     public void setDistributable(boolean distributable)
1035     {
1036         this._distributable = distributable;
1037     }
1038 
1039     /* ------------------------------------------------------------ */
1040     @Override
1041     public void setEventListeners(EventListener[] eventListeners)
1042     {
1043         if (_sessionHandler!=null)
1044             _sessionHandler.clearEventListeners();
1045 
1046         super.setEventListeners(eventListeners);
1047     }
1048 
1049     /* ------------------------------------------------------------ */
1050     /** Add EventListener
1051      * Convenience method that calls {@link #setEventListeners(EventListener[])}
1052      * @param listener
1053      */
1054     @Override
1055     public void addEventListener(EventListener listener)
1056     {
1057         super.addEventListener(listener);
1058         if ((listener instanceof HttpSessionActivationListener)
1059             || (listener instanceof HttpSessionAttributeListener)
1060             || (listener instanceof HttpSessionBindingListener)
1061             || (listener instanceof HttpSessionListener))
1062         {
1063             if (_sessionHandler!=null)
1064                 _sessionHandler.addEventListener(listener);
1065         }
1066     }
1067     
1068     @Override
1069     public void removeEventListener(EventListener listener)
1070     {
1071         super.removeEventListener(listener);
1072         if ((listener instanceof HttpSessionActivationListener)
1073             || (listener instanceof HttpSessionAttributeListener)
1074             || (listener instanceof HttpSessionBindingListener)
1075             || (listener instanceof HttpSessionListener))
1076         {
1077             if (_sessionHandler!=null)
1078                 _sessionHandler.removeEventListener(listener);
1079         }
1080         
1081     }
1082 
1083 
1084     /* ------------------------------------------------------------ */
1085     /**
1086      * @param extractWAR True if war files are extracted
1087      */
1088     public void setExtractWAR(boolean extractWAR)
1089     {
1090         _extractWAR = extractWAR;
1091     }
1092 
1093     /* ------------------------------------------------------------ */
1094     /**
1095      * @param copy True if the webdir is copied (to allow hot replacement of jars)
1096      */
1097     public void setCopyWebDir(boolean copy)
1098     {
1099         _copyDir = copy;
1100     }
1101 
1102     /* ------------------------------------------------------------ */
1103     /**
1104      * @param copyWebInf True if the web-inf lib and classes directories are copied (to allow hot replacement of jars on windows)
1105      */
1106     public void setCopyWebInf(boolean copyWebInf)
1107     {
1108         _copyWebInf = copyWebInf;
1109     }
1110 
1111     /* ------------------------------------------------------------ */
1112     /**
1113      * @param java2compliant True if the classloader should delegate first to the parent
1114      * classloader (standard java behaviour) or false if the classloader
1115      * should first try to load from WEB-INF/lib or WEB-INF/classes (servlet
1116      * spec recommendation).  Default is false or can be set by the system 
1117      * property org.eclipse.jetty.server.webapp.parentLoaderPriority
1118      */
1119     public void setParentLoaderPriority(boolean java2compliant)
1120     {
1121         _parentLoaderPriority = java2compliant;
1122     }
1123 
1124     /* ------------------------------------------------------------ */
1125     /**
1126      * @param permissions The permissions to set.
1127      */
1128     public void setPermissions(PermissionCollection permissions)
1129     {
1130         _permissions = permissions;
1131     }
1132 
1133     /**
1134      * Set the context white list
1135      *
1136      * In certain circumstances you want may want to deny access of one webapp from another
1137      * when you may not fully trust the webapp.  Setting this white list will enable a
1138      * check when a servlet called getContext(String), validating that the uriInPath
1139      * for the given webapp has been declaratively allows access to the context.
1140      * @param contextWhiteList
1141      */
1142     public void setContextWhiteList(String[] contextWhiteList)
1143     {
1144         _contextWhiteList = contextWhiteList;
1145     }
1146 
1147     /* ------------------------------------------------------------ */
1148     /**
1149      * Set the server classes patterns.
1150      * <p>
1151      * Server classes/packages are classes used to implement the server and are hidden
1152      * from the context.  If the context needs to load these classes, it must have its
1153      * own copy of them in WEB-INF/lib or WEB-INF/classes.
1154      * A class pattern is a string of one of the forms:<dl>
1155      * <dt>org.package.Classname</dt><dd>Match a specific class</dd>
1156      * <dt>org.package.</dt><dd>Match a specific package hierarchy</dd>
1157      * <dt>-org.package.Classname</dt><dd>Exclude a specific class</dd>
1158      * <dt>-org.package.</dt><dd>Exclude a specific package hierarchy</dd>
1159      * </dl>
1160      * @param serverClasses The serverClasses to set.
1161      */
1162     public void setServerClasses(String[] serverClasses)
1163     {
1164         _serverClasses = new ClasspathPattern(serverClasses);
1165     }
1166 
1167     /* ------------------------------------------------------------ */
1168     /**
1169      * Set the system classes patterns.
1170      * <p>
1171      * System classes/packages are classes provided by the JVM and that
1172      * cannot be replaced by classes of the same name from WEB-INF,
1173      * regardless of the value of {@link #setParentLoaderPriority(boolean)}.
1174      * A class pattern is a string of one of the forms:<dl>
1175      * <dt>org.package.Classname</dt><dd>Match a specific class</dd>
1176      * <dt>org.package.</dt><dd>Match a specific package hierarchy</dd>
1177      * <dt>-org.package.Classname</dt><dd>Exclude a specific class</dd>
1178      * <dt>-org.package.</dt><dd>Exclude a specific package hierarchy</dd>
1179      * </dl>
1180      * @param systemClasses The systemClasses to set.
1181      */
1182     public void setSystemClasses(String[] systemClasses)
1183     {
1184         _systemClasses = new ClasspathPattern(systemClasses);
1185     }
1186 
1187 
1188     /* ------------------------------------------------------------ */
1189     /** Set temporary directory for context.
1190      * The javax.servlet.context.tempdir attribute is also set.
1191      * @param dir Writable temporary directory.
1192      */
1193     public void setTempDirectory(File dir)
1194     {
1195         if (isStarted())
1196             throw new IllegalStateException("Started");
1197 
1198         if (dir!=null)
1199         {
1200             try{dir=new File(dir.getCanonicalPath());}
1201             catch (IOException e){LOG.warn(Log.EXCEPTION,e);}
1202         }
1203 
1204         if (dir!=null && !dir.exists())
1205         {
1206             dir.mkdir();
1207             dir.deleteOnExit();
1208         }
1209 
1210         if (dir!=null && ( !dir.exists() || !dir.isDirectory() || !dir.canWrite()))
1211             throw new IllegalArgumentException("Bad temp directory: "+dir);
1212 
1213         try
1214         {
1215             if (dir!=null)
1216                 dir=dir.getCanonicalFile();
1217         }
1218         catch(Exception e)
1219         {
1220             LOG.warn(e);
1221         }
1222         _tmpDir=dir;
1223         setAttribute(TEMPDIR,_tmpDir);
1224     }
1225 
1226     /* ------------------------------------------------------------ */
1227     @ManagedAttribute(value="temporary directory location", readonly=true)
1228     public File getTempDirectory ()
1229     {
1230         return _tmpDir;
1231     }
1232 
1233     /* ------------------------------------------------------------ */
1234     /**
1235      * @param war The war to set as a file name or URL
1236      */
1237     public void setWar(String war)
1238     {
1239         _war = war;
1240     }
1241 
1242     /* ------------------------------------------------------------ */
1243     /**
1244      * @return Comma or semicolon separated path of filenames or URLs
1245      * pointing to directories or jar files. Directories should end
1246      * with '/'.
1247      */
1248     @Override
1249     @ManagedAttribute(value="extra classpath for context classloader", readonly=true)
1250     public String getExtraClasspath()
1251     {
1252         return _extraClasspath;
1253     }
1254 
1255     /* ------------------------------------------------------------ */
1256     /**
1257      * @param extraClasspath Comma or semicolon separated path of filenames or URLs
1258      * pointing to directories or jar files. Directories should end
1259      * with '/'.
1260      */
1261     public void setExtraClasspath(String extraClasspath)
1262     {
1263         _extraClasspath=extraClasspath;
1264     }
1265 
1266     /* ------------------------------------------------------------ */
1267     public boolean isLogUrlOnStart()
1268     {
1269         return _logUrlOnStart;
1270     }
1271 
1272     /* ------------------------------------------------------------ */
1273     /**
1274      * Sets whether or not the web app name and URL is logged on startup
1275      *
1276      * @param logOnStart whether or not the log message is created
1277      */
1278     public void setLogUrlOnStart(boolean logOnStart)
1279     {
1280         this._logUrlOnStart = logOnStart;
1281     }
1282 
1283     /* ------------------------------------------------------------ */
1284     @Override
1285     public void setServer(Server server)
1286     {
1287         super.setServer(server);
1288     }
1289 
1290     /* ------------------------------------------------------------ */
1291     public boolean isAllowDuplicateFragmentNames()
1292     {
1293         return _allowDuplicateFragmentNames;
1294     }
1295 
1296     /* ------------------------------------------------------------ */
1297     public void setAllowDuplicateFragmentNames(boolean allowDuplicateFragmentNames)
1298     {
1299         _allowDuplicateFragmentNames = allowDuplicateFragmentNames;
1300     }
1301 
1302     /* ------------------------------------------------------------ */
1303     public void setThrowUnavailableOnStartupException (boolean throwIfStartupException) {
1304         _throwUnavailableOnStartupException = throwIfStartupException;
1305     }
1306 
1307     /* ------------------------------------------------------------ */
1308     public boolean isThrowUnavailableOnStartupException () {
1309         return _throwUnavailableOnStartupException;
1310     }
1311 
1312     /* ------------------------------------------------------------ */
1313     @Override
1314     protected void startContext()
1315         throws Exception
1316     {
1317         configure();
1318 
1319         //resolve the metadata
1320         _metadata.resolve(this);
1321 
1322         super.startContext();
1323     }
1324        
1325     /* ------------------------------------------------------------ */    
1326     @Override
1327     public Set<String> setServletSecurity(Dynamic registration, ServletSecurityElement servletSecurityElement)
1328     {
1329      
1330         Set<String> unchangedURLMappings = new HashSet<String>();
1331         //From javadoc for ServletSecurityElement:
1332         /*
1333         If a URL pattern of this ServletRegistration is an exact target of a security-constraint that 
1334         was established via the portable deployment descriptor, then this method does not change the 
1335         security-constraint for that pattern, and the pattern will be included in the return value.
1336 
1337         If a URL pattern of this ServletRegistration is an exact target of a security constraint 
1338         that was established via the ServletSecurity annotation or a previous call to this method, 
1339         then this method replaces the security constraint for that pattern.
1340 
1341         If a URL pattern of this ServletRegistration is neither the exact target of a security constraint 
1342         that was established via the ServletSecurity annotation or a previous call to this method, 
1343         nor the exact target of a security-constraint in the portable deployment descriptor, then 
1344         this method establishes the security constraint for that pattern from the argument ServletSecurityElement. 
1345          */
1346 
1347         Collection<String> pathMappings = registration.getMappings();
1348         if (pathMappings != null)
1349         {
1350             ConstraintSecurityHandler.createConstraint(registration.getName(), servletSecurityElement);
1351 
1352             for (String pathSpec:pathMappings)
1353             {
1354                 Origin origin = getMetaData().getOrigin("constraint.url."+pathSpec);
1355                
1356                 switch (origin)
1357                 {
1358                     case NotSet:
1359                     {
1360                         //No mapping for this url already established
1361                         List<ConstraintMapping> mappings = ConstraintSecurityHandler.createConstraintsWithMappingsForPath(registration.getName(), pathSpec, servletSecurityElement);
1362                         for (ConstraintMapping m:mappings)
1363                             ((ConstraintAware)getSecurityHandler()).addConstraintMapping(m);
1364                         getMetaData().setOrigin("constraint.url."+pathSpec, Origin.API);
1365                         break;
1366                     }
1367                     case WebXml:
1368                     case WebDefaults:
1369                     case WebOverride:
1370                     case WebFragment:
1371                     {
1372                         //a mapping for this url was created in a descriptor, which overrides everything
1373                         unchangedURLMappings.add(pathSpec);
1374                         break;
1375                     }
1376                     case Annotation:
1377                     case API:
1378                     {
1379                         //mapping established via an annotation or by previous call to this method,
1380                         //replace the security constraint for this pattern
1381                         List<ConstraintMapping> constraintMappings = ConstraintSecurityHandler.removeConstraintMappingsForPath(pathSpec, ((ConstraintAware)getSecurityHandler()).getConstraintMappings());
1382                        
1383                         List<ConstraintMapping> freshMappings = ConstraintSecurityHandler.createConstraintsWithMappingsForPath(registration.getName(), pathSpec, servletSecurityElement);
1384                         constraintMappings.addAll(freshMappings);
1385                            
1386                         ((ConstraintSecurityHandler)getSecurityHandler()).setConstraintMappings(constraintMappings);
1387                         break;
1388                     }
1389                 }
1390             }
1391         }
1392         
1393         return unchangedURLMappings;
1394     }
1395 
1396 
1397 
1398     /* ------------------------------------------------------------ */
1399     public class Context extends ServletContextHandler.Context
1400     {
1401         /* ------------------------------------------------------------ */
1402         @Override
1403         public URL getResource(String path) throws MalformedURLException
1404         {
1405             Resource resource=WebAppContext.this.getResource(path);
1406             if (resource==null || !resource.exists())
1407                 return null;
1408 
1409             // Should we go to the original war?
1410             if (resource.isDirectory() && resource instanceof ResourceCollection && !WebAppContext.this.isExtractWAR())
1411             {
1412                 Resource[] resources = ((ResourceCollection)resource).getResources();
1413                 for (int i=resources.length;i-->0;)
1414                 {
1415                     if (resources[i].getName().startsWith("jar:file"))
1416                         return resources[i].getURL();
1417                 }
1418             }
1419 
1420             return resource.getURL();
1421         }
1422 
1423         /* ------------------------------------------------------------ */
1424         @Override
1425         public ServletContext getContext(String uripath)
1426         {
1427             ServletContext servletContext = super.getContext(uripath);
1428 
1429             if ( servletContext != null && _contextWhiteList != null )
1430             {
1431                 for ( String context : _contextWhiteList )
1432                 {
1433                     if ( context.equals(uripath) )
1434                     {
1435                         return servletContext;
1436                     }
1437                 }
1438 
1439                 return null;
1440             }
1441             else
1442             {
1443                 return servletContext;
1444             }
1445         }
1446         
1447         
1448     }
1449 
1450     /* ------------------------------------------------------------ */
1451     public MetaData getMetaData()
1452     {
1453         return _metadata;
1454     }
1455 
1456 }