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