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