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