View Javadoc

1   // ========================================================================
2   // Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at 
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses. 
12  // ========================================================================
13  
14  package org.eclipse.jetty.webapp;
15  
16  import java.io.File;
17  import java.io.IOException;
18  import java.net.MalformedURLException;
19  import java.security.PermissionCollection;
20  import java.util.EventListener;
21  import java.util.HashMap;
22  import java.util.Map;
23  
24  import javax.servlet.ServletContext;
25  import javax.servlet.ServletException;
26  import javax.servlet.http.HttpServletRequest;
27  import javax.servlet.http.HttpServletResponse;
28  import javax.servlet.http.HttpSessionActivationListener;
29  import javax.servlet.http.HttpSessionAttributeListener;
30  import javax.servlet.http.HttpSessionBindingListener;
31  import javax.servlet.http.HttpSessionListener;
32  
33  import org.eclipse.jetty.security.SecurityHandler;
34  import org.eclipse.jetty.server.Connector;
35  import org.eclipse.jetty.server.HandlerContainer;
36  import org.eclipse.jetty.server.handler.ContextHandler;
37  import org.eclipse.jetty.server.handler.ErrorHandler;
38  import org.eclipse.jetty.server.session.SessionHandler;
39  import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
40  import org.eclipse.jetty.servlet.ServletContextHandler;
41  import org.eclipse.jetty.servlet.ServletHandler;
42  import org.eclipse.jetty.util.IO;
43  import org.eclipse.jetty.util.LazyList;
44  import org.eclipse.jetty.util.Loader;
45  import org.eclipse.jetty.util.StringUtil;
46  import org.eclipse.jetty.util.URIUtil;
47  import org.eclipse.jetty.util.log.Log;
48  import org.eclipse.jetty.util.resource.JarResource;
49  import org.eclipse.jetty.util.resource.Resource;
50  
51  /* ------------------------------------------------------------ */
52  /** Web Application Context Handler.
53   * The WebAppContext handler is an extension of ContextHandler that
54   * coordinates the construction and configuration of nested handlers:
55   * {@link org.eclipse.jetty.security.ConstraintSecurityHandler}, {@link org.eclipse.jetty.server.session.SessionHandler}
56   * and {@link org.eclipse.jetty.servlet.ServletHandler}.
57   * The handlers are configured by pluggable configuration classes, with
58   * the default being  {@link org.eclipse.jetty.server.server.webapp.WebXmlConfiguration} and 
59   * {@link org.eclipse.jetty.server.server.webapp.JettyWebXmlConfiguration}.
60   *      
61   * @org.apache.xbean.XBean description="Creates a servlet web application at a given context from a resource base"
62   * 
63   * 
64   *
65   */
66  public class WebAppContext extends ServletContextHandler
67  {   
68      public static final String TEMPDIR = "javax.servlet.context.tempdir";
69      public final static String WEB_DEFAULTS_XML="org/eclipse/jetty/webapp/webdefault.xml";
70      public final static String ERROR_PAGE="org.eclipse.jetty.server.error_page";
71      
72      private static String[] __dftConfigurationClasses =  
73      { 
74          "org.eclipse.jetty.webapp.WebInfConfiguration", 
75          "org.eclipse.jetty.webapp.WebXmlConfiguration", 
76          "org.eclipse.jetty.webapp.MetaInfConfiguration",
77          "org.eclipse.jetty.webapp.FragmentConfiguration",
78          "org.eclipse.jetty.webapp.JettyWebXmlConfiguration",
79          "org.eclipse.jetty.webapp.TagLibConfiguration" 
80      } ;
81      private String[] _configurationClasses=__dftConfigurationClasses;
82      private Configuration[] _configurations;
83      private String _defaultsDescriptor=WEB_DEFAULTS_XML;
84      private String _descriptor=null;
85      private String _overrideDescriptor=null;
86      private boolean _distributable=false;
87      private boolean _extractWAR=true;
88      private boolean _copyDir=false;
89      private boolean _logUrlOnStart =false;
90      private boolean _parentLoaderPriority= Boolean.getBoolean("org.eclipse.jetty.server.webapp.parentLoaderPriority");
91      private PermissionCollection _permissions;
92      private String[] _systemClasses = {
93              "java.",
94              "javax.servlet.",
95              "javax.xml.",
96              "org.xml.",
97              "org.w3c.", 
98              "org.apache.commons.logging.", 
99              "org.apache.log4j.",
100             "org.eclipse.jetty.servlet.",      // webapp cannot change default servlets
101             "org.eclipse.jetty.continuation.", // webapp cannot change continuation classes
102             "org.eclipse.jetty.naming."        // webapp cannot change naming classes
103             };
104     private String[] _serverClasses = {
105             "-org.eclipse.jetty.jndi.",       // don't hide naming classes
106             "-org.eclipse.jetty.continuation.", // don't hide continuation classes
107             "-org.eclipse.jetty.plus.jaas.",    // don't hide jaas modules
108             "org.eclipse.jetty.",               // hide rest of jetty classes
109             "org.slf4j."                        // hide slf4j
110             }; 
111     private File _tmpDir;
112     private String _war;
113     private String _extraClasspath;
114     private Throwable _unavailableException;
115     
116     private Map _resourceAliases;
117     private boolean _ownClassLoader=false;
118     private boolean _configurationDiscovered=false;
119 
120     public static ContextHandler getCurrentWebAppContext()
121     {
122         ContextHandler.Context context=ContextHandler.getCurrentContext();
123         if (context!=null)
124         {
125             ContextHandler handler = context.getContextHandler();
126             if (handler instanceof WebAppContext)
127                 return (ContextHandler)handler;
128         }
129         return null;   
130     }
131     
132     /* ------------------------------------------------------------ */
133     public WebAppContext()
134     {
135         super(SESSIONS|SECURITY);
136     }
137     
138     /* ------------------------------------------------------------ */
139     /**
140      * @param contextPath The context path
141      * @param webApp The URL or filename of the webapp directory or war file.
142      */
143     public WebAppContext(String webApp,String contextPath)
144     {
145         super(null,contextPath,SESSIONS|SECURITY);
146         setContextPath(contextPath);
147         setWar(webApp);
148         setErrorHandler(new ErrorPageErrorHandler());
149     }
150     
151     /* ------------------------------------------------------------ */
152     /**
153      * @param parent The parent HandlerContainer.
154      * @param contextPath The context path
155      * @param webApp The URL or filename of the webapp directory or war file.
156      */
157     public WebAppContext(HandlerContainer parent, String webApp, String contextPath)
158     {
159         super(parent,contextPath,SESSIONS|SECURITY);
160         setWar(webApp);
161         setErrorHandler(new ErrorPageErrorHandler());
162     }
163 
164     /* ------------------------------------------------------------ */
165     /**
166      */
167     public WebAppContext(SessionHandler sessionHandler, SecurityHandler securityHandler, ServletHandler servletHandler, ErrorHandler errorHandler)
168     {
169         super(null,sessionHandler,securityHandler,servletHandler,errorHandler);
170         
171         setErrorHandler(errorHandler!=null?errorHandler:new ErrorPageErrorHandler());
172     }    
173 
174     /* ------------------------------------------------------------ */
175     /**
176      * @param servletContextName The servletContextName to set.
177      */
178     public void setDisplayName(String servletContextName)
179     {
180         super.setDisplayName(servletContextName);
181         ClassLoader cl = getClassLoader();
182         if (cl!=null && cl instanceof WebAppClassLoader)
183             ((WebAppClassLoader)cl).setName(servletContextName);
184     }
185     
186     /* ------------------------------------------------------------ */
187     /** Get an exception that caused the webapp to be unavailable
188      * @return A throwable if the webapp is unavailable or null
189      */
190     public Throwable getUnavailableException()
191     {
192         return _unavailableException;
193     }
194 
195     
196     /* ------------------------------------------------------------ */
197     /** Set Resource Alias.
198      * Resource aliases map resource uri's within a context.
199      * They may optionally be used by a handler when looking for
200      * a resource.  
201      * @param alias 
202      * @param uri 
203      */
204     public void setResourceAlias(String alias, String uri)
205     {
206         if (_resourceAliases == null)
207             _resourceAliases= new HashMap(5);
208         _resourceAliases.put(alias, uri);
209     }
210 
211     /* ------------------------------------------------------------ */
212     public Map getResourceAliases()
213     {
214         if (_resourceAliases == null)
215             return null;
216         return _resourceAliases;
217     }
218     
219     /* ------------------------------------------------------------ */
220     public void setResourceAliases(Map map)
221     {
222         _resourceAliases = map;
223     }
224     
225     /* ------------------------------------------------------------ */
226     public String getResourceAlias(String alias)
227     {
228         if (_resourceAliases == null)
229             return null;
230         return (String)_resourceAliases.get(alias);
231     }
232 
233     /* ------------------------------------------------------------ */
234     public String removeResourceAlias(String alias)
235     {
236         if (_resourceAliases == null)
237             return null;
238         return (String)_resourceAliases.remove(alias);
239     }
240 
241     /* ------------------------------------------------------------ */
242     /* (non-Javadoc)
243      * @see org.eclipse.jetty.server.server.handler.ContextHandler#setClassLoader(java.lang.ClassLoader)
244      */
245     public void setClassLoader(ClassLoader classLoader)
246     {
247         super.setClassLoader(classLoader);
248         if (classLoader!=null && classLoader instanceof WebAppClassLoader)
249             ((WebAppClassLoader)classLoader).setName(getDisplayName());
250     }
251     
252     /* ------------------------------------------------------------ */
253     public Resource getResource(String uriInContext) throws MalformedURLException
254     {
255         IOException ioe= null;
256         Resource resource= null;
257         int loop=0;
258         while (uriInContext!=null && loop++<100)
259         {
260             try
261             {
262                 resource= super.getResource(uriInContext);
263                 if (resource != null && resource.exists())
264                     return resource;
265                 
266                 uriInContext = getResourceAlias(uriInContext);
267             }
268             catch (IOException e)
269             {
270                 Log.ignore(e);
271                 if (ioe==null)
272                     ioe= e;
273             }
274         }
275 
276         if (ioe != null && ioe instanceof MalformedURLException)
277             throw (MalformedURLException)ioe;
278 
279         return resource;
280     }
281     
282 
283     /* ------------------------------------------------------------ */
284     /** Is the context Automatically configured.
285      * 
286      * @return true if configuration discovery.
287      */
288     public boolean isConfigurationDiscovered()
289     {
290         return _configurationDiscovered;
291     }
292 
293     /* ------------------------------------------------------------ */
294     /** Set the configuration discovery mode.
295      * If configuration discovery is set to true, then the JSR315
296      * servlet 3.0 discovered configuration features are enabled.
297      * These are:<ul>
298      * <li>Web Fragments</li>
299      * <li>META-INF/resource directories</li>
300      * </ul>
301      * @param servlet3autoConfig the servlet3autoConfig to set
302      */
303     public void setConfigurationDiscovered(boolean discovered)
304     {
305         _configurationDiscovered = discovered;
306     }
307 
308     /* ------------------------------------------------------------ */
309     /* 
310      * @see org.eclipse.thread.AbstractLifeCycle#doStart()
311      */
312     protected void doStart() throws Exception
313     {
314         try
315         {
316             // Setup configurations 
317             loadConfigurations();
318 
319             
320             // Configure classloader
321             _ownClassLoader=false;
322             if (getClassLoader()==null)
323             {
324                 WebAppClassLoader classLoader = new WebAppClassLoader(this);
325                 setClassLoader(classLoader);
326                 _ownClassLoader=true;
327             }
328 
329             if (Log.isDebugEnabled()) 
330             {
331                 ClassLoader loader = getClassLoader();
332                 Log.debug("Thread Context class loader is: " + loader);
333                 loader=loader.getParent();
334                 while(loader!=null)
335                 {
336                     Log.debug("Parent class loader is: " + loader); 
337                     loader=loader.getParent();
338                 }
339             }
340             
341             // Prepare for configuration
342             for (int i=0;i<_configurations.length;i++)
343                 _configurations[i].preConfigure(this);
344             
345             super.doStart();
346             
347             // Clean up after configuration
348             for (int i=0;i<_configurations.length;i++)
349                 _configurations[i].postConfigure(this);
350 
351 
352             if (isLogUrlOnStart()) 
353                 dumpUrl();
354         }
355         catch (Exception e)
356         {
357             //start up of the webapp context failed, make sure it is not started
358             Log.warn("Failed startup of context "+this, e);
359             _unavailableException=e;
360             setAvailable(false);
361         }
362     }
363 
364     /* ------------------------------------------------------------ */
365     /*
366      * Dumps the current web app name and URL to the log
367      */
368     public void dumpUrl() 
369     {
370         Connector[] connectors = getServer().getConnectors();
371         for (int i=0;i<connectors.length;i++) 
372         {
373             String connectorName = connectors[i].getName();
374             String displayName = getDisplayName();
375             if (displayName == null)
376                 displayName = "WebApp@"+connectors.hashCode();
377            
378             Log.info(displayName + " at http://" + connectorName + getContextPath());
379         }
380     }
381 
382     /* ------------------------------------------------------------ */
383     /* 
384      * @see org.eclipse.thread.AbstractLifeCycle#doStop()
385      */
386     protected void doStop() throws Exception
387     {
388         super.doStop();
389 
390         try
391         {
392             for (int i=_configurations.length;i-->0;)
393                 _configurations[i].deconfigure(this);
394             
395             _configurations=null;
396             
397             // restore security handler
398             if (_securityHandler != null && _securityHandler.getHandler()==null)
399             {
400                 _sessionHandler.setHandler(_securityHandler);
401                 _securityHandler.setHandler(_servletHandler);
402             }
403         }
404         finally
405         {
406             if (_ownClassLoader)
407                 setClassLoader(null);
408 
409             setAvailable(true);
410             _unavailableException=null;
411         }
412     }
413     
414     /* ------------------------------------------------------------ */
415     /**
416      * @return Returns the configurations.
417      */
418     public String[] getConfigurationClasses()
419     {
420         return _configurationClasses;
421     }
422     
423     /* ------------------------------------------------------------ */
424     /**
425      * @return Returns the configurations.
426      */
427     public Configuration[] getConfigurations()
428     {
429         return _configurations;
430     }
431     
432     /* ------------------------------------------------------------ */
433     /**
434      * The default descriptor is a web.xml format file that is applied to the context before the standard WEB-INF/web.xml
435      * @return Returns the defaultsDescriptor.
436      */
437     public String getDefaultsDescriptor()
438     {
439         return _defaultsDescriptor;
440     }
441     
442     /* ------------------------------------------------------------ */
443     /**
444      * The override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml
445      * @return Returns the Override Descriptor.
446      */
447     public String getOverrideDescriptor()
448     {
449         return _overrideDescriptor;
450     }
451     
452     /* ------------------------------------------------------------ */
453     /**
454      * @return Returns the permissions.
455      */
456     public PermissionCollection getPermissions()
457     {
458         return _permissions;
459     }
460     
461 
462     /* ------------------------------------------------------------ */
463     /**
464      * @see #setServerClasses(String[])
465      * @return Returns the serverClasses.
466      */
467     public String[] getServerClasses()
468     {
469         return _serverClasses;
470     }
471     
472     
473     /* ------------------------------------------------------------ */
474     /**
475      * @see #setSystemClasses(String[])
476      * @return Returns the systemClasses.
477      */
478     public String[] getSystemClasses()
479     {
480         return _systemClasses;
481     }
482     
483 
484     
485     /* ------------------------------------------------------------ */
486     public boolean isServerClass(String name)
487     {
488         name=name.replace('/','.');
489         while(name.startsWith("."))
490             name=name.substring(1);
491 
492         String[] server_classes = getServerClasses();
493         if (server_classes!=null)
494         {
495             for (int i=0;i<server_classes.length;i++)
496             {
497                 boolean result=true;
498                 String c=server_classes[i];
499                 if (c.startsWith("-"))
500                 {
501                     c=c.substring(1); // TODO cache
502                     result=false;
503                 }
504                 
505                 if (c.endsWith("."))
506                 {
507                     if (name.startsWith(c))
508                         return result;
509                 }
510                 else if (name.equals(c))
511                     return result;
512             }
513         }
514         return false;
515     }
516 
517     /* ------------------------------------------------------------ */
518     public boolean isSystemClass(String name)
519     {
520         name=name.replace('/','.');
521         while(name.startsWith("."))
522             name=name.substring(1);
523         String[] system_classes = getSystemClasses();
524         if (system_classes!=null)
525         {
526             for (int i=0;i<system_classes.length;i++)
527             {
528                 boolean result=true;
529                 String c=system_classes[i];
530                 
531                 if (c.startsWith("-"))
532                 {
533                     c=c.substring(1); // TODO cache
534                     result=false;
535                 }
536                 
537                 if (c.endsWith("."))
538                 {
539                     if (name.startsWith(c))
540                         return result;
541                 }
542                 else if (name.equals(c))
543                     return result;
544             }
545         }
546         
547         return false;
548         
549     }
550 
551     
552     
553     
554     /* ------------------------------------------------------------ */
555     /**
556      * @return Returns the war as a file or URL string (Resource)
557      */
558     public String getWar()
559     {
560         if (_war==null)
561             _war=getResourceBase();
562         return _war;
563     }
564 
565     /* ------------------------------------------------------------ */
566     public Resource getWebInf() throws IOException
567     {
568         if (super.getBaseResource() == null)
569             return null;
570 
571         // Iw there a WEB-INF directory?
572         Resource web_inf= super.getBaseResource().addPath("WEB-INF/");
573         if (!web_inf.exists() || !web_inf.isDirectory())
574             return null;
575         
576         return web_inf;
577     }
578     
579     /* ------------------------------------------------------------ */
580     /**
581      * @return Returns the distributable.
582      */
583     public boolean isDistributable()
584     {
585         return _distributable;
586     }
587 
588     /* ------------------------------------------------------------ */
589     /**
590      * @return Returns the extractWAR.
591      */
592     public boolean isExtractWAR()
593     {
594         return _extractWAR;
595     }
596 
597     /* ------------------------------------------------------------ */
598     /**
599      * @return True if the webdir is copied (to allow hot replacement of jars)
600      */
601     public boolean isCopyWebDir()
602     {
603         return _copyDir;
604     }
605     
606     /* ------------------------------------------------------------ */
607     /**
608      * @return Returns the java2compliant.
609      */
610     public boolean isParentLoaderPriority()
611     {
612         return _parentLoaderPriority;
613     }
614     
615     /* ------------------------------------------------------------ */
616     protected void loadConfigurations() 
617     	throws Exception
618     {
619         if (_configurations!=null)
620             return;
621         if (_configurationClasses==null)
622             _configurationClasses=__dftConfigurationClasses;
623         
624         _configurations = new Configuration[_configurationClasses.length];
625         for (int i=0;i<_configurations.length;i++)
626         {
627             _configurations[i]=(Configuration)Loader.loadClass(this.getClass(), _configurationClasses[i]).newInstance();
628         }
629     }
630     
631     /* ------------------------------------------------------------ */
632     protected boolean isProtectedTarget(String target)
633     {
634         while (target.startsWith("//"))
635             target=URIUtil.compactPath(target);
636          
637         return StringUtil.startsWithIgnoreCase(target, "/web-inf") || StringUtil.startsWithIgnoreCase(target, "/meta-inf");
638     }
639     
640 
641     /* ------------------------------------------------------------ */
642     public String toString()
643     {
644         return super.toString()+(_war==null?"":(","+_war));
645     }
646     
647    
648        
649 
650     /* ------------------------------------------------------------ */
651     /**
652      * @param configurations The configuration class names.  If setConfigurations is not called
653      * these classes are used to create a configurations array.
654      */
655     public void setConfigurationClasses(String[] configurations)
656     {
657         _configurationClasses = configurations==null?null:(String[])configurations.clone();
658     }
659     
660     /* ------------------------------------------------------------ */
661     /**
662      * @param configurations The configurations to set.
663      */
664     public void setConfigurations(Configuration[] configurations)
665     {
666         _configurations = configurations==null?null:(Configuration[])configurations.clone();
667     }
668 
669     /* ------------------------------------------------------------ */
670     /** 
671      * The default descriptor is a web.xml format file that is applied to the context before the standard WEB-INF/web.xml
672      * @param defaultsDescriptor The defaultsDescriptor to set.
673      */
674     public void setDefaultsDescriptor(String defaultsDescriptor)
675     {
676         _defaultsDescriptor = defaultsDescriptor;
677     }
678 
679     /* ------------------------------------------------------------ */
680     /**
681      * The override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml
682      * @param overrideDescriptor The overrideDescritpor to set.
683      */
684     public void setOverrideDescriptor(String overrideDescriptor)
685     {
686         _overrideDescriptor = overrideDescriptor;
687     }
688 
689     /* ------------------------------------------------------------ */
690     /**
691      * @return the web.xml descriptor to use. If set to null, WEB-INF/web.xml is used if it exists.
692      */
693     public String getDescriptor()
694     {
695         return _descriptor;
696     }
697 
698     /* ------------------------------------------------------------ */
699     /**
700      * @param descriptor the web.xml descriptor to use. If set to null, WEB-INF/web.xml is used if it exists.
701      */
702     public void setDescriptor(String descriptor)
703     {
704         _descriptor=descriptor;
705     }
706     
707     /* ------------------------------------------------------------ */
708     /**
709      * @param distributable The distributable to set.
710      */
711     public void setDistributable(boolean distributable)
712     {
713         this._distributable = distributable;
714     }
715 
716     /* ------------------------------------------------------------ */
717     public void setEventListeners(EventListener[] eventListeners)
718     {
719         if (_sessionHandler!=null)
720             _sessionHandler.clearEventListeners();
721             
722         super.setEventListeners(eventListeners);
723       
724         for (int i=0; eventListeners!=null && i<eventListeners.length;i ++)
725         {
726             EventListener listener = eventListeners[i];
727             
728             if ((listener instanceof HttpSessionActivationListener)
729                             || (listener instanceof HttpSessionAttributeListener)
730                             || (listener instanceof HttpSessionBindingListener)
731                             || (listener instanceof HttpSessionListener))
732             {
733                 if (_sessionHandler!=null)
734                     _sessionHandler.addEventListener(listener);
735             }
736             
737         }
738     }
739 
740     /* ------------------------------------------------------------ */
741     /** Add EventListener
742      * Conveniance method that calls {@link #setEventListeners(EventListener[])}
743      * @param listener
744      */
745     public void addEventListener(EventListener listener)
746     {
747         setEventListeners((EventListener[])LazyList.addToArray(getEventListeners(), listener, EventListener.class));   
748     }
749 
750     
751     /* ------------------------------------------------------------ */
752     /**
753      * @param extractWAR True if war files are extracted
754      */
755     public void setExtractWAR(boolean extractWAR)
756     {
757         _extractWAR = extractWAR;
758     }
759     
760     /* ------------------------------------------------------------ */
761     /**
762      * 
763      * @param copy True if the webdir is copied (to allow hot replacement of jars)
764      */
765     public void setCopyWebDir(boolean copy)
766     {
767         _copyDir = copy;
768     }
769 
770     /* ------------------------------------------------------------ */
771     /**
772      * @param java2compliant The java2compliant to set.
773      */
774     public void setParentLoaderPriority(boolean java2compliant)
775     {
776         _parentLoaderPriority = java2compliant;
777     }
778 
779     /* ------------------------------------------------------------ */
780     /**
781      * @param permissions The permissions to set.
782      */
783     public void setPermissions(PermissionCollection permissions)
784     {
785         _permissions = permissions;
786     }
787 
788     /* ------------------------------------------------------------ */
789     /** 
790      * Set the server classes patterns.
791      * <p>
792      * Server classes/packages are classes used to implement the server and are hidden
793      * from the context.  If the context needs to load these classes, it must have its
794      * own copy of them in WEB-INF/lib or WEB-INF/classes. 
795      * A class pattern is a string of one of the forms:<dl>
796      * <dt>org.package.Classname</dt><dd>Match a specific class</dd>
797      * <dt>org.package.</dt><dd>Match a specific package hierarchy</dd>
798      * <dt>-org.package.Classname</dt><dd>Exclude a specific class</dd>
799      * <dt>-org.package.</dt><dd>Exclude a specific package hierarchy</dd>
800      * </dl>
801      * @param serverClasses The serverClasses to set.
802      */
803     public void setServerClasses(String[] serverClasses) 
804     {
805         _serverClasses = serverClasses==null?null:(String[])serverClasses.clone();
806     }
807     
808     /* ------------------------------------------------------------ */
809     /**
810      * Set the system classes patterns.
811      * <p>
812      * System classes/packages are classes provided by the JVM and that
813      * cannot be replaced by classes of the same name from WEB-INF,
814      * regardless of the value of {@link #setParentLoaderPriority(boolean)}.
815      * A class pattern is a string of one of the forms:<dl>
816      * <dt>org.package.Classname</dt><dd>Match a specific class</dd>
817      * <dt>org.package.</dt><dd>Match a specific package hierarchy</dd>
818      * <dt>-org.package.Classname</dt><dd>Exclude a specific class</dd>
819      * <dt>-org.package.</dt><dd>Exclude a specific package hierarchy</dd>
820      * </dl>
821      * @param systemClasses The systemClasses to set.
822      */
823     public void setSystemClasses(String[] systemClasses)
824     {
825         _systemClasses = systemClasses==null?null:(String[])systemClasses.clone();
826     }
827     
828 
829     /* ------------------------------------------------------------ */
830     /** Set temporary directory for context.
831      * The javax.servlet.context.tempdir attribute is also set.
832      * @param dir Writable temporary directory.
833      */
834     public void setTempDirectory(File dir)
835     {
836         if (isStarted())
837             throw new IllegalStateException("Started");
838 
839         if (dir!=null)
840         {
841             try{dir=new File(dir.getCanonicalPath());}
842             catch (IOException e){Log.warn(Log.EXCEPTION,e);}
843         }
844 
845         if (dir!=null && !dir.exists())
846         {
847             dir.mkdir();
848             dir.deleteOnExit();
849         }
850 
851         if (dir!=null && ( !dir.exists() || !dir.isDirectory() || !dir.canWrite()))
852             throw new IllegalArgumentException("Bad temp directory: "+dir);
853 
854         _tmpDir=dir;
855         setAttribute(TEMPDIR,_tmpDir);
856     }
857     
858     public File getTempDirectory ()
859     {
860         return _tmpDir;
861     }
862     
863     /* ------------------------------------------------------------ */
864     /**
865      * @param war The war to set as a file name or URL
866      */
867     public void setWar(String war)
868     {
869         _war = war;
870     }
871 
872 
873     /* ------------------------------------------------------------ */
874     /**
875      * @return Comma or semicolon separated path of filenames or URLs
876      * pointing to directories or jar files. Directories should end
877      * with '/'.
878      */
879     public String getExtraClasspath()
880     {
881         return _extraClasspath;
882     }
883 
884     /* ------------------------------------------------------------ */
885     /**
886      * @param extraClasspath Comma or semicolon separated path of filenames or URLs
887      * pointing to directories or jar files. Directories should end
888      * with '/'.
889      */
890     public void setExtraClasspath(String extraClasspath)
891     {
892         _extraClasspath=extraClasspath;
893     }
894 
895     /* ------------------------------------------------------------ */
896     public boolean isLogUrlOnStart() 
897     {
898         return _logUrlOnStart;
899     }
900 
901     /* ------------------------------------------------------------ */
902     /**
903      * Sets whether or not the web app name and URL is logged on startup
904      *
905      * @param logOnStart whether or not the log message is created
906      */
907     public void setLogUrlOnStart(boolean logOnStart) 
908     {
909         this._logUrlOnStart = logOnStart;
910     }
911 
912     /* ------------------------------------------------------------ */
913     protected void startContext()
914         throws Exception
915     {
916  
917         
918         // Configure webapp
919         for (int i=0;i<_configurations.length;i++)
920             _configurations[i].configure(this);
921 
922         
923         super.startContext();
924     }
925 
926 }