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