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