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