View Javadoc

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.server.handler;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.lang.reflect.Method;
25  import java.net.MalformedURLException;
26  import java.net.URI;
27  import java.net.URL;
28  import java.net.URLClassLoader;
29  import java.security.AccessController;
30  import java.util.ArrayList;
31  import java.util.Arrays;
32  import java.util.Collections;
33  import java.util.Enumeration;
34  import java.util.EventListener;
35  import java.util.HashMap;
36  import java.util.HashSet;
37  import java.util.Iterator;
38  import java.util.List;
39  import java.util.Locale;
40  import java.util.Map;
41  import java.util.Set;
42  import java.util.concurrent.CopyOnWriteArrayList;
43  import java.util.concurrent.Future;
44  
45  import javax.servlet.DispatcherType;
46  import javax.servlet.Filter;
47  import javax.servlet.FilterRegistration;
48  import javax.servlet.FilterRegistration.Dynamic;
49  import javax.servlet.RequestDispatcher;
50  import javax.servlet.Servlet;
51  import javax.servlet.ServletContext;
52  import javax.servlet.ServletContextAttributeEvent;
53  import javax.servlet.ServletContextAttributeListener;
54  import javax.servlet.ServletContextEvent;
55  import javax.servlet.ServletContextListener;
56  import javax.servlet.ServletException;
57  import javax.servlet.ServletRegistration;
58  import javax.servlet.ServletRequestAttributeListener;
59  import javax.servlet.ServletRequestEvent;
60  import javax.servlet.ServletRequestListener;
61  import javax.servlet.SessionCookieConfig;
62  import javax.servlet.SessionTrackingMode;
63  import javax.servlet.descriptor.JspConfigDescriptor;
64  import javax.servlet.http.HttpServletRequest;
65  import javax.servlet.http.HttpServletResponse;
66  
67  import org.eclipse.jetty.http.HttpURI;
68  import org.eclipse.jetty.http.MimeTypes;
69  import org.eclipse.jetty.server.ClassLoaderDump;
70  import org.eclipse.jetty.server.Connector;
71  import org.eclipse.jetty.server.Dispatcher;
72  import org.eclipse.jetty.server.Handler;
73  import org.eclipse.jetty.server.HandlerContainer;
74  import org.eclipse.jetty.server.Request;
75  import org.eclipse.jetty.server.Server;
76  import org.eclipse.jetty.util.Attributes;
77  import org.eclipse.jetty.util.AttributesMap;
78  import org.eclipse.jetty.util.FutureCallback;
79  import org.eclipse.jetty.util.Loader;
80  import org.eclipse.jetty.util.StringUtil;
81  import org.eclipse.jetty.util.URIUtil;
82  import org.eclipse.jetty.util.annotation.ManagedAttribute;
83  import org.eclipse.jetty.util.annotation.ManagedObject;
84  import org.eclipse.jetty.util.component.DumpableCollection;
85  import org.eclipse.jetty.util.component.Graceful;
86  import org.eclipse.jetty.util.log.Log;
87  import org.eclipse.jetty.util.log.Logger;
88  import org.eclipse.jetty.util.resource.Resource;
89  
90  /* ------------------------------------------------------------ */
91  /**
92   * ContextHandler.
93   *
94   * This handler wraps a call to handle by setting the context and servlet path, plus setting the context classloader.
95   *
96   * <p>
97   * If the context init parameter "org.eclipse.jetty.server.context.ManagedAttributes" is set to a comma separated list of names, then they are treated as
98   * context attribute names, which if set as attributes are passed to the servers Container so that they may be managed with JMX.
99   * <p>
100  * The maximum size of a form that can be processed by this context is controlled by the system properties org.eclipse.jetty.server.Request.maxFormKeys
101  * and org.eclipse.jetty.server.Request.maxFormContentSize.  These can also be configured with {@link #setMaxFormContentSize(int)} and {@link #setMaxFormKeys(int)}
102  * <p>
103  * This servers executore is made available via a context attributed "org.eclipse.jetty.server.Executor".
104  * <p>
105  * By default, the context is created with alias checkers for {@link AllowSymLinkAliasChecker} (unix only) and {@link ApproveNonExistentDirectoryAliases}.
106  * If these alias checkers are not required, then {@link #clearAliasChecks()} or {@link #setAliasChecks(List)} should be called.
107  */
108 @ManagedObject("URI Context")
109 public class ContextHandler extends ScopedHandler implements Attributes, Graceful
110 {
111     public final static int SERVLET_MAJOR_VERSION=3;
112     public final static int SERVLET_MINOR_VERSION=1;
113     public static final Class<?>[] SERVLET_LISTENER_TYPES = new Class[] {ServletContextListener.class,
114         ServletContextAttributeListener.class,
115         ServletRequestListener.class,
116         ServletRequestAttributeListener.class};
117 
118     public static final int DEFAULT_LISTENER_TYPE_INDEX = 1;
119     public static final int EXTENDED_LISTENER_TYPE_INDEX = 0;
120 
121 
122     final private static String __unimplmented="Unimplemented - use org.eclipse.jetty.servlet.ServletContextHandler";
123 
124     private static final Logger LOG = Log.getLogger(ContextHandler.class);
125 
126     private static final ThreadLocal<Context> __context = new ThreadLocal<Context>();
127 
128     private static String __serverInfo = "jetty/" + Server.getVersion();
129 
130     /**
131      * If a context attribute with this name is set, it is interpreted as a comma separated list of attribute name. Any other context attributes that are set
132      * with a name from this list will result in a call to {@link #setManagedAttribute(String, Object)}, which typically initiates the creation of a JMX MBean
133      * for the attribute value.
134      */
135     public static final String MANAGED_ATTRIBUTES = "org.eclipse.jetty.server.context.ManagedAttributes";
136 
137     /* ------------------------------------------------------------ */
138     /**
139      * Get the current ServletContext implementation.
140      *
141      * @return ServletContext implementation
142      */
143     public static Context getCurrentContext()
144     {
145         return __context.get();
146     }
147 
148     /* ------------------------------------------------------------ */
149     public static ContextHandler getContextHandler(ServletContext context)
150     {
151         if(context instanceof ContextHandler.Context)
152             return ((ContextHandler.Context)context).getContextHandler();
153         Context c=  getCurrentContext();
154         if (c!=null)
155             return c.getContextHandler();
156         return null;
157     }
158 
159     /* ------------------------------------------------------------ */
160     public static String getServerInfo()
161     {
162         return __serverInfo;
163     }
164 
165     /* ------------------------------------------------------------ */
166     public static void setServerInfo(String serverInfo)
167     {
168         __serverInfo = serverInfo;
169     }
170 
171 
172 
173     protected Context _scontext;
174     private final AttributesMap _attributes;
175     private final Map<String, String> _initParams;
176     private ClassLoader _classLoader;
177     private String _contextPath = "/";
178 
179     private String _displayName;
180 
181     private Resource _baseResource;
182     private MimeTypes _mimeTypes;
183     private Map<String, String> _localeEncodingMap;
184     private String[] _welcomeFiles;
185     private ErrorHandler _errorHandler;
186     private String[] _vhosts;
187 
188     private Logger _logger;
189     private boolean _allowNullPathInfo;
190     private int _maxFormKeys = Integer.getInteger("org.eclipse.jetty.server.Request.maxFormKeys",-1).intValue();
191     private int _maxFormContentSize = Integer.getInteger("org.eclipse.jetty.server.Request.maxFormContentSize",-1).intValue();
192     private boolean _compactPath = false;
193     private boolean _usingSecurityManager = System.getSecurityManager()!=null;
194 
195     private final List<EventListener> _eventListeners=new CopyOnWriteArrayList<>();
196     private final List<EventListener> _programmaticListeners=new CopyOnWriteArrayList<>();
197     private final List<ServletContextListener> _servletContextListeners=new CopyOnWriteArrayList<>();
198     private final List<ServletContextAttributeListener> _servletContextAttributeListeners=new CopyOnWriteArrayList<>();
199     private final List<ServletRequestListener> _servletRequestListeners=new CopyOnWriteArrayList<>();
200     private final List<ServletRequestAttributeListener> _servletRequestAttributeListeners=new CopyOnWriteArrayList<>();
201     private final List<ContextScopeListener> _contextListeners = new CopyOnWriteArrayList<>();
202     private final List<EventListener> _durableListeners = new CopyOnWriteArrayList<>();
203     private Map<String, Object> _managedAttributes;
204     private String[] _protectedTargets;
205     private final CopyOnWriteArrayList<AliasCheck> _aliasChecks = new CopyOnWriteArrayList<ContextHandler.AliasCheck>();
206 
207     public enum Availability { UNAVAILABLE,STARTING,AVAILABLE,SHUTDOWN,};
208     private volatile Availability _availability;
209 
210     /* ------------------------------------------------------------ */
211     public ContextHandler()
212     {
213         this((Context)null);
214     }
215 
216     /* ------------------------------------------------------------ */
217     protected ContextHandler(Context context)
218     {
219         super();
220         _scontext = context==null?new Context():context;
221         _attributes = new AttributesMap();
222         _initParams = new HashMap<String, String>();
223         addAliasCheck(new ApproveNonExistentDirectoryAliases());
224         if (File.separatorChar=='/')
225             addAliasCheck(new AllowSymLinkAliasChecker());
226     }
227 
228     /* ------------------------------------------------------------ */
229     public ContextHandler(String contextPath)
230     {
231         this();
232         setContextPath(contextPath);
233     }
234 
235     /* ------------------------------------------------------------ */
236     public ContextHandler(HandlerContainer parent, String contextPath)
237     {
238         this();
239         setContextPath(contextPath);
240         if (parent instanceof HandlerWrapper)
241             ((HandlerWrapper)parent).setHandler(this);
242         else if (parent instanceof HandlerCollection)
243             ((HandlerCollection)parent).addHandler(this);
244     }
245 
246     /* ------------------------------------------------------------ */
247     @Override
248     public void dump(Appendable out, String indent) throws IOException
249     {
250         dumpBeans(out,indent,
251                 Collections.singletonList(new ClassLoaderDump(getClassLoader())),
252                 Collections.singletonList(new DumpableCollection("Handler attributes "+this,((AttributesMap)getAttributes()).getAttributeEntrySet())),
253                 Collections.singletonList(new DumpableCollection("Context attributes "+this,((Context)getServletContext()).getAttributeEntrySet())),
254                 Collections.singletonList(new DumpableCollection("Initparams "+this,getInitParams().entrySet()))
255                 );
256     }
257 
258     /* ------------------------------------------------------------ */
259     public Context getServletContext()
260     {
261         return _scontext;
262     }
263 
264     /* ------------------------------------------------------------ */
265     /**
266      * @return the allowNullPathInfo true if /context is not redirected to /context/
267      */
268     @ManagedAttribute("Checks if the /context is not redirected to /context/")
269     public boolean getAllowNullPathInfo()
270     {
271         return _allowNullPathInfo;
272     }
273 
274     /* ------------------------------------------------------------ */
275     /**
276      * @param allowNullPathInfo
277      *            true if /context is not redirected to /context/
278      */
279     public void setAllowNullPathInfo(boolean allowNullPathInfo)
280     {
281         _allowNullPathInfo = allowNullPathInfo;
282     }
283 
284     /* ------------------------------------------------------------ */
285     @Override
286     public void setServer(Server server)
287     {
288         super.setServer(server);
289         if (_errorHandler != null)
290             _errorHandler.setServer(server);
291     }
292 
293     /* ------------------------------------------------------------ */
294     public boolean isUsingSecurityManager()
295     {
296         return _usingSecurityManager;
297     }
298 
299     /* ------------------------------------------------------------ */
300     public void setUsingSecurityManager(boolean usingSecurityManager)
301     {
302         _usingSecurityManager = usingSecurityManager;
303     }
304 
305     /* ------------------------------------------------------------ */
306     /**
307      * Set the virtual hosts for the context. Only requests that have a matching host header or fully qualified URL will be passed to that context with a
308      * virtual host name. A context with no virtual host names or a null virtual host name is available to all requests that are not served by a context with a
309      * matching virtual host name.
310      *
311      * @param vhosts
312      *            Array of virtual hosts that this context responds to. A null host name or null/empty array means any hostname is acceptable. Host names may be
313      *            String representation of IP addresses. Host names may start with '*.' to wildcard one level of names.  Hosts may start with '@', in which case they
314      *            will match the {@link Connector#getName()} for the request.
315      */
316     public void setVirtualHosts(String[] vhosts)
317     {
318         if (vhosts == null)
319         {
320             _vhosts = vhosts;
321         }
322         else
323         {
324             _vhosts = new String[vhosts.length];
325             for (int i = 0; i < vhosts.length; i++)
326                 _vhosts[i] = normalizeHostname(vhosts[i]);
327         }
328     }
329 
330     /* ------------------------------------------------------------ */
331     /** Either set virtual hosts or add to an existing set of virtual hosts.
332      *
333      * @param virtualHosts
334      *            Array of virtual hosts that this context responds to. A null host name or null/empty array means any hostname is acceptable. Host names may be
335      *            String representation of IP addresses. Host names may start with '*.' to wildcard one level of names. Host names may start with '@', in which case they
336      *            will match the {@link Connector#getName()} for the request.
337      */
338     public void addVirtualHosts(String[] virtualHosts)
339     {
340         if (virtualHosts == null)  // since this is add, we don't null the old ones
341         {
342             return;
343         }
344         else
345         {
346             List<String> currentVirtualHosts = null;
347             if (_vhosts != null)
348             {
349                 currentVirtualHosts = new ArrayList<String>(Arrays.asList(_vhosts));
350             }
351             else
352             {
353                 currentVirtualHosts = new ArrayList<String>();
354             }
355 
356             for (int i = 0; i < virtualHosts.length; i++)
357             {
358                 String normVhost = normalizeHostname(virtualHosts[i]);
359                 if (!currentVirtualHosts.contains(normVhost))
360                 {
361                     currentVirtualHosts.add(normVhost);
362                 }
363             }
364             _vhosts = currentVirtualHosts.toArray(new String[0]);
365         }
366     }
367 
368     /* ------------------------------------------------------------ */
369     /**
370      * Removes an array of virtual host entries, if this removes all entries the _vhosts will be set to null
371      *
372      *  @param virtualHosts
373      *            Array of virtual hosts that this context responds to. A null host name or null/empty array means any hostname is acceptable. Host names may be
374      *            String representation of IP addresses. Host names may start with '*.' to wildcard one level of names.
375      */
376     public void removeVirtualHosts(String[] virtualHosts)
377     {
378         if (virtualHosts == null)
379         {
380             return; // do nothing
381         }
382         else if ( _vhosts == null || _vhosts.length == 0)
383         {
384             return; // do nothing
385         }
386         else
387         {
388             List<String> existingVirtualHosts = new ArrayList<String>(Arrays.asList(_vhosts));
389 
390             for (int i = 0; i < virtualHosts.length; i++)
391             {
392                 String toRemoveVirtualHost = normalizeHostname(virtualHosts[i]);
393                 if (existingVirtualHosts.contains(toRemoveVirtualHost))
394                 {
395                     existingVirtualHosts.remove(toRemoveVirtualHost);
396                 }
397             }
398 
399             if (existingVirtualHosts.isEmpty())
400             {
401                 _vhosts = null; // if we ended up removing them all, just null out _vhosts
402             }
403             else
404             {
405                 _vhosts = existingVirtualHosts.toArray(new String[0]);
406             }
407         }
408     }
409 
410     /* ------------------------------------------------------------ */
411     /**
412      * Get the virtual hosts for the context. Only requests that have a matching host header or fully qualified URL will be passed to that context with a
413      * virtual host name. A context with no virtual host names or a null virtual host name is available to all requests that are not served by a context with a
414      * matching virtual host name.
415      *
416      * @return Array of virtual hosts that this context responds to. A null host name or empty array means any hostname is acceptable. Host names may be String
417      *         representation of IP addresses. Host names may start with '*.' to wildcard one level of names.
418      */
419     @ManagedAttribute(value="Virtual hosts accepted by the context", readonly=true)
420     public String[] getVirtualHosts()
421     {
422         return _vhosts;
423     }
424 
425     /* ------------------------------------------------------------ */
426     /*
427      * @see javax.servlet.ServletContext#getAttribute(java.lang.String)
428      */
429     @Override
430     public Object getAttribute(String name)
431     {
432         return _attributes.getAttribute(name);
433     }
434 
435     /* ------------------------------------------------------------ */
436     /*
437      * @see javax.servlet.ServletContext#getAttributeNames()
438      */
439     @Override
440     public Enumeration<String> getAttributeNames()
441     {
442         return AttributesMap.getAttributeNamesCopy(_attributes);
443     }
444 
445     /* ------------------------------------------------------------ */
446     /**
447      * @return Returns the attributes.
448      */
449     public Attributes getAttributes()
450     {
451         return _attributes;
452     }
453 
454     /* ------------------------------------------------------------ */
455     /**
456      * @return Returns the classLoader.
457      */
458     public ClassLoader getClassLoader()
459     {
460         return _classLoader;
461     }
462 
463     /* ------------------------------------------------------------ */
464     /**
465      * Make best effort to extract a file classpath from the context classloader
466      *
467      * @return Returns the classLoader.
468      */
469     @ManagedAttribute("The file classpath")
470     public String getClassPath()
471     {
472         if (_classLoader == null || !(_classLoader instanceof URLClassLoader))
473             return null;
474         URLClassLoader loader = (URLClassLoader)_classLoader;
475         URL[] urls = loader.getURLs();
476         StringBuilder classpath = new StringBuilder();
477         for (int i = 0; i < urls.length; i++)
478         {
479             try
480             {
481                 Resource resource = newResource(urls[i]);
482                 File file = resource.getFile();
483                 if (file != null && file.exists())
484                 {
485                     if (classpath.length() > 0)
486                         classpath.append(File.pathSeparatorChar);
487                     classpath.append(file.getAbsolutePath());
488                 }
489             }
490             catch (IOException e)
491             {
492                 LOG.debug(e);
493             }
494         }
495         if (classpath.length() == 0)
496             return null;
497         return classpath.toString();
498     }
499 
500     /* ------------------------------------------------------------ */
501     /**
502      * @return Returns the _contextPath.
503      */
504     @ManagedAttribute("True if URLs are compacted to replace the multiple '/'s with a single '/'")
505     public String getContextPath()
506     {
507         return _contextPath;
508     }
509 
510     /* ------------------------------------------------------------ */
511     /*
512      * @see javax.servlet.ServletContext#getInitParameter(java.lang.String)
513      */
514     public String getInitParameter(String name)
515     {
516         return _initParams.get(name);
517     }
518 
519     /* ------------------------------------------------------------ */
520     /*
521      */
522     public String setInitParameter(String name, String value)
523     {
524         return _initParams.put(name,value);
525     }
526 
527     /* ------------------------------------------------------------ */
528     /*
529      * @see javax.servlet.ServletContext#getInitParameterNames()
530      */
531     public Enumeration<String> getInitParameterNames()
532     {
533         return Collections.enumeration(_initParams.keySet());
534     }
535 
536     /* ------------------------------------------------------------ */
537     /**
538      * @return Returns the initParams.
539      */
540     @ManagedAttribute("Initial Parameter map for the context")
541     public Map<String, String> getInitParams()
542     {
543         return _initParams;
544     }
545 
546     /* ------------------------------------------------------------ */
547     /*
548      * @see javax.servlet.ServletContext#getServletContextName()
549      */
550     @ManagedAttribute(value="Display name of the Context", readonly=true)
551     public String getDisplayName()
552     {
553         return _displayName;
554     }
555 
556     /* ------------------------------------------------------------ */
557     public EventListener[] getEventListeners()
558     {
559         return _eventListeners.toArray(new EventListener[_eventListeners.size()]);
560     }
561 
562     /* ------------------------------------------------------------ */
563     /**
564      * Set the context event listeners.
565      *
566      * @param eventListeners
567      *            the event listeners
568      * @see ServletContextListener
569      * @see ServletContextAttributeListener
570      * @see ServletRequestListener
571      * @see ServletRequestAttributeListener
572      */
573     public void setEventListeners(EventListener[] eventListeners)
574     {
575         _contextListeners.clear();
576         _servletContextListeners.clear();
577         _servletContextAttributeListeners.clear();
578         _servletRequestListeners.clear();
579         _servletRequestAttributeListeners.clear();
580         _eventListeners.clear();
581 
582         if (eventListeners!=null)
583             for (EventListener listener : eventListeners)
584                 addEventListener(listener);
585     }
586 
587     /* ------------------------------------------------------------ */
588     /**
589      * Add a context event listeners.
590      * @param listener the event listener to add
591      *
592      * @see ServletContextListener
593      * @see ServletContextAttributeListener
594      * @see ServletRequestListener
595      * @see ServletRequestAttributeListener
596      */
597     public void addEventListener(EventListener listener)
598     {
599         _eventListeners.add(listener);
600 
601         if (!(isStarted() || isStarting()))
602             _durableListeners.add(listener);
603 
604         if (listener instanceof ContextScopeListener)
605             _contextListeners.add((ContextScopeListener)listener);
606 
607         if (listener instanceof ServletContextListener)
608             _servletContextListeners.add((ServletContextListener)listener);
609 
610         if (listener instanceof ServletContextAttributeListener)
611             _servletContextAttributeListeners.add((ServletContextAttributeListener)listener);
612 
613         if (listener instanceof ServletRequestListener)
614             _servletRequestListeners.add((ServletRequestListener)listener);
615 
616         if (listener instanceof ServletRequestAttributeListener)
617             _servletRequestAttributeListeners.add((ServletRequestAttributeListener)listener);
618     }
619 
620     /* ------------------------------------------------------------ */
621     /**
622      * Remove a context event listeners.
623      * @param listener the event listener to remove
624      *
625      * @see ServletContextListener
626      * @see ServletContextAttributeListener
627      * @see ServletRequestListener
628      * @see ServletRequestAttributeListener
629      */
630     public void removeEventListener(EventListener listener)
631     {
632         _eventListeners.remove(listener);
633 
634         if (listener instanceof ContextScopeListener)
635             _contextListeners.remove(listener);
636 
637         if (listener instanceof ServletContextListener)
638             _servletContextListeners.remove(listener);
639 
640         if (listener instanceof ServletContextAttributeListener)
641             _servletContextAttributeListeners.remove(listener);
642 
643         if (listener instanceof ServletRequestListener)
644             _servletRequestListeners.remove(listener);
645 
646         if (listener instanceof ServletRequestAttributeListener)
647             _servletRequestAttributeListeners.remove(listener);
648     }
649 
650     /* ------------------------------------------------------------ */
651     /**
652      * Apply any necessary restrictions on a programmatic added listener.
653      *
654      * @param listener the programmatic listener to add
655      */
656     protected void addProgrammaticListener (EventListener listener)
657     {
658         _programmaticListeners.add(listener);
659     }
660 
661     /* ------------------------------------------------------------ */
662     protected boolean isProgrammaticListener(EventListener listener)
663     {
664         return _programmaticListeners.contains(listener);
665     }
666 
667 
668 
669     /* ------------------------------------------------------------ */
670     /**
671      * @return true if this context is accepting new requests
672      */
673     @ManagedAttribute("true for graceful shutdown, which allows existing requests to complete")
674     public boolean isShutdown()
675     {
676         switch(_availability)
677         {
678             case SHUTDOWN:
679                 return true;
680             default:
681                 return false;
682         }
683     }
684 
685     /* ------------------------------------------------------------ */
686     /**
687      * Set shutdown status. This field allows for graceful shutdown of a context. A started context may be put into non accepting state so that existing
688      * requests can complete, but no new requests are accepted.
689      *
690      */
691     @Override
692     public Future<Void> shutdown()
693     {
694         _availability = isRunning() ? Availability.SHUTDOWN : Availability.UNAVAILABLE;
695         return new FutureCallback(true);
696     }
697 
698     /* ------------------------------------------------------------ */
699     /**
700      * @return false if this context is unavailable (sends 503)
701      */
702     public boolean isAvailable()
703     {
704         return _availability==Availability.AVAILABLE;
705     }
706 
707     /* ------------------------------------------------------------ */
708     /**
709      * Set Available status.
710      * @param available true to set as enabled
711      */
712     public void setAvailable(boolean available)
713     {
714         synchronized (this)
715         {
716             if (available && isRunning())
717                 _availability = Availability.AVAILABLE;
718             else if (!available || !isRunning())
719                 _availability = Availability.UNAVAILABLE;
720         }
721     }
722 
723     /* ------------------------------------------------------------ */
724     public Logger getLogger()
725     {
726         return _logger;
727     }
728 
729     /* ------------------------------------------------------------ */
730     public void setLogger(Logger logger)
731     {
732         _logger = logger;
733     }
734 
735     /* ------------------------------------------------------------ */
736     /*
737      * @see org.eclipse.thread.AbstractLifeCycle#doStart()
738      */
739     @Override
740     protected void doStart() throws Exception
741     {
742         _availability = Availability.STARTING;
743 
744         if (_contextPath == null)
745             throw new IllegalStateException("Null contextPath");
746 
747         if (_logger==null)
748             _logger = Log.getLogger(getDisplayName() == null?getContextPath():getDisplayName());
749         ClassLoader old_classloader = null;
750         Thread current_thread = null;
751         Context old_context = null;
752 
753         _attributes.setAttribute("org.eclipse.jetty.server.Executor",getServer().getThreadPool());
754 
755         try
756         {
757             // Set the classloader
758             if (_classLoader != null)
759             {
760                 current_thread = Thread.currentThread();
761                 old_classloader = current_thread.getContextClassLoader();
762                 current_thread.setContextClassLoader(_classLoader);
763             }
764 
765             if (_mimeTypes == null)
766                 _mimeTypes = new MimeTypes();
767 
768             old_context = __context.get();
769             __context.set(_scontext);
770 
771             // defers the calling of super.doStart()
772             startContext();
773 
774             _availability = Availability.AVAILABLE;
775             LOG.info("Started {}", this);
776         }
777         finally
778         {
779             __context.set(old_context);
780 
781             // reset the classloader
782             if (_classLoader != null && current_thread!=null)
783                 current_thread.setContextClassLoader(old_classloader);
784         }
785     }
786 
787     /* ------------------------------------------------------------ */
788     /**
789      * Extensible startContext. this method is called from {@link ContextHandler#doStart()} instead of a call to super.doStart(). This allows derived classes to
790      * insert additional handling (Eg configuration) before the call to super.doStart by this method will start contained handlers.
791      * @throws Exception if unable to start the context
792      *
793      * @see org.eclipse.jetty.server.handler.ContextHandler.Context
794      */
795     protected void startContext() throws Exception
796     {
797         String managedAttributes = _initParams.get(MANAGED_ATTRIBUTES);
798         if (managedAttributes != null)
799             addEventListener(new ManagedAttributeListener(this,StringUtil.csvSplit(managedAttributes)));
800 
801         super.doStart();
802 
803         // Call context listeners
804         if (!_servletContextListeners.isEmpty())
805         {
806             ServletContextEvent event = new ServletContextEvent(_scontext);
807             for (ServletContextListener listener:_servletContextListeners)
808                 callContextInitialized(listener, event);
809         }
810     }
811 
812 
813     /* ------------------------------------------------------------ */
814     protected void stopContext () throws Exception
815     {
816         //stop all the handler hierarchy
817         super.doStop();
818 
819         //Call the context listeners
820         if (!_servletContextListeners.isEmpty())
821         {
822             ServletContextEvent event = new ServletContextEvent(_scontext);
823             for (int i = _servletContextListeners.size(); i-->0;)
824                 callContextDestroyed(_servletContextListeners.get(i),event);
825         }
826     }
827 
828 
829 
830     /* ------------------------------------------------------------ */
831     protected void callContextInitialized (ServletContextListener l, ServletContextEvent e)
832     {
833         if (LOG.isDebugEnabled())
834             LOG.debug("contextInitialized: {}->{}",e,l);
835         l.contextInitialized(e);
836     }
837 
838     /* ------------------------------------------------------------ */
839     protected void callContextDestroyed (ServletContextListener l, ServletContextEvent e)
840     {
841         if (LOG.isDebugEnabled())
842             LOG.debug("contextDestroyed: {}->{}",e,l);
843         l.contextDestroyed(e);
844     }
845 
846     /* ------------------------------------------------------------ */
847     /*
848      * @see org.eclipse.thread.AbstractLifeCycle#doStop()
849      */
850     @Override
851     protected void doStop() throws Exception
852     {
853         _availability = Availability.UNAVAILABLE;
854 
855         ClassLoader old_classloader = null;
856         ClassLoader old_webapploader = null;
857         Thread current_thread = null;
858 
859         Context old_context = __context.get();
860         __context.set(_scontext);
861         try
862         {
863             // Set the classloader
864             if (_classLoader != null)
865             {
866                 old_webapploader = _classLoader;
867                 current_thread = Thread.currentThread();
868                 old_classloader = current_thread.getContextClassLoader();
869                 current_thread.setContextClassLoader(_classLoader);
870             }
871 
872             stopContext();
873 
874             //retain only durable listeners
875             setEventListeners(_durableListeners.toArray(new EventListener[_durableListeners.size()]));
876             _durableListeners.clear();
877 
878             if (_errorHandler != null)
879                 _errorHandler.stop();
880 
881             for (EventListener l : _programmaticListeners)
882                 removeEventListener(l);
883             _programmaticListeners.clear();
884         }
885         finally
886         {
887             LOG.info("Stopped {}", this);
888             __context.set(old_context);
889             // reset the classloader
890             if ((old_classloader == null || (old_classloader != old_webapploader)) && current_thread != null)
891                 current_thread.setContextClassLoader(old_classloader);
892         }
893 
894         _scontext.clearAttributes();
895     }
896 
897 
898     /* ------------------------------------------------------------ */
899     public boolean checkVirtualHost(final Request baseRequest)
900     {
901         if (_vhosts != null && _vhosts.length > 0)
902         {
903             String vhost = normalizeHostname(baseRequest.getServerName());
904 
905             boolean match = false;
906             boolean connectorName = false;
907             boolean connectorMatch = false;
908 
909             for (String contextVhost:_vhosts)
910             {
911                 if (contextVhost == null || contextVhost.length()==0)
912                     continue;
913                 char c=contextVhost.charAt(0);
914                 switch (c)
915                 {
916                     case '*':
917                         if (contextVhost.startsWith("*."))
918                             // wildcard only at the beginning, and only for one additional subdomain level
919                             match = match || contextVhost.regionMatches(true,2,vhost,vhost.indexOf(".") + 1,contextVhost.length() - 2);
920                         break;
921                     case '@':
922                         connectorName=true;
923                         String name=baseRequest.getHttpChannel().getConnector().getName();
924                         boolean m=name!=null && contextVhost.length()==name.length()+1 && contextVhost.endsWith(name);
925                         match = match || m;
926                         connectorMatch = connectorMatch || m;
927                         break;
928                     default:
929                         match = match || contextVhost.equalsIgnoreCase(vhost);
930                 }
931 
932             }
933             if (!match || connectorName && !connectorMatch)
934                 return false;
935         }
936         return true;
937     }
938 
939 
940     /* ------------------------------------------------------------ */
941     public boolean checkContextPath(String uri)
942     {
943         // Are we not the root context?
944         if (_contextPath.length() > 1)
945         {
946             // reject requests that are not for us
947             if (!uri.startsWith(_contextPath))
948                 return false;
949             if (uri.length() > _contextPath.length() && uri.charAt(_contextPath.length()) != '/')
950                 return false;
951         }
952         return true;
953     }
954 
955     /* ------------------------------------------------------------ */
956     /*
957      * @see org.eclipse.jetty.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
958      */
959     public boolean checkContext(final String target, final Request baseRequest, final HttpServletResponse response) throws IOException
960     {
961         DispatcherType dispatch = baseRequest.getDispatcherType();
962 
963         // Check the vhosts
964         if (!checkVirtualHost(baseRequest))
965             return false;
966 
967         if (!checkContextPath(target))
968             return false;
969 
970         // Are we not the root context?
971         // redirect null path infos
972         if (!_allowNullPathInfo && _contextPath.length() == target.length() && _contextPath.length()>1)
973         {
974             // context request must end with /
975             baseRequest.setHandled(true);
976             if (baseRequest.getQueryString() != null)
977                 response.sendRedirect(URIUtil.addPaths(baseRequest.getRequestURI(),URIUtil.SLASH) + "?" + baseRequest.getQueryString());
978             else
979                 response.sendRedirect(URIUtil.addPaths(baseRequest.getRequestURI(),URIUtil.SLASH));
980             return false;
981         }
982 
983         switch (_availability)
984         {
985             case SHUTDOWN:
986             case UNAVAILABLE:
987                 baseRequest.setHandled(true);
988                 response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
989                 return false;
990             default:
991                 if ((DispatcherType.REQUEST.equals(dispatch) && baseRequest.isHandled()))
992                     return false;
993         }
994 
995         return true;
996     }
997 
998     /* ------------------------------------------------------------ */
999     /**
1000      * @see org.eclipse.jetty.server.handler.ScopedHandler#doScope(java.lang.String, org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest,
1001      *      javax.servlet.http.HttpServletResponse)
1002      */
1003     @Override
1004     public void doScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
1005     {
1006         if (LOG.isDebugEnabled())
1007             LOG.debug("scope {}|{}|{} @ {}",baseRequest.getContextPath(),baseRequest.getServletPath(),baseRequest.getPathInfo(),this);
1008 
1009         Context old_context = null;
1010         String old_context_path = null;
1011         String old_servlet_path = null;
1012         String old_path_info = null;
1013         ClassLoader old_classloader = null;
1014         Thread current_thread = null;
1015         String pathInfo = target;
1016 
1017         DispatcherType dispatch = baseRequest.getDispatcherType();
1018 
1019         old_context = baseRequest.getContext();
1020 
1021         // Are we already in this context?
1022         if (old_context != _scontext)
1023         {
1024             // check the target.
1025             if (DispatcherType.REQUEST.equals(dispatch) ||
1026                 DispatcherType.ASYNC.equals(dispatch) ||
1027                 DispatcherType.ERROR.equals(dispatch) && baseRequest.getHttpChannelState().isAsync())
1028             {
1029                 if (_compactPath)
1030                     target = URIUtil.compactPath(target);
1031                 if (!checkContext(target,baseRequest,response))
1032                     return;
1033 
1034                 if (target.length() > _contextPath.length())
1035                 {
1036                     if (_contextPath.length() > 1)
1037                         target = target.substring(_contextPath.length());
1038                     pathInfo = target;
1039                 }
1040                 else if (_contextPath.length() == 1)
1041                 {
1042                     target = URIUtil.SLASH;
1043                     pathInfo = URIUtil.SLASH;
1044                 }
1045                 else
1046                 {
1047                     target = URIUtil.SLASH;
1048                     pathInfo = null;
1049                 }
1050             }
1051 
1052             // Set the classloader
1053             if (_classLoader != null)
1054             {
1055                 current_thread = Thread.currentThread();
1056                 old_classloader = current_thread.getContextClassLoader();
1057                 current_thread.setContextClassLoader(_classLoader);
1058             }
1059         }
1060 
1061         try
1062         {
1063             old_context_path = baseRequest.getContextPath();
1064             old_servlet_path = baseRequest.getServletPath();
1065             old_path_info = baseRequest.getPathInfo();
1066 
1067             // Update the paths
1068             baseRequest.setContext(_scontext);
1069             __context.set(_scontext);
1070             if (!DispatcherType.INCLUDE.equals(dispatch) && target.startsWith("/"))
1071             {
1072                 if (_contextPath.length() == 1)
1073                     baseRequest.setContextPath("");
1074                 else
1075                     baseRequest.setContextPath(_contextPath);
1076                 baseRequest.setServletPath(null);
1077                 baseRequest.setPathInfo(pathInfo);
1078             }
1079 
1080             if (old_context != _scontext)
1081                 enterScope(baseRequest,dispatch);
1082 
1083             if (LOG.isDebugEnabled())
1084                 LOG.debug("context={}|{}|{} @ {}",baseRequest.getContextPath(),baseRequest.getServletPath(), baseRequest.getPathInfo(),this);
1085 
1086             // start manual inline of nextScope(target,baseRequest,request,response);
1087             if (never())
1088                 nextScope(target,baseRequest,request,response);
1089             else if (_nextScope != null)
1090                 _nextScope.doScope(target,baseRequest,request,response);
1091             else if (_outerScope != null)
1092                 _outerScope.doHandle(target,baseRequest,request,response);
1093             else
1094                 doHandle(target,baseRequest,request,response);
1095             // end manual inline (pathentic attempt to reduce stack depth)
1096         }
1097         finally
1098         {
1099             if (old_context != _scontext)
1100             {
1101                 exitScope(baseRequest);
1102 
1103                 // reset the classloader
1104                 if (_classLoader != null && current_thread!=null)
1105                 {
1106                     current_thread.setContextClassLoader(old_classloader);
1107                 }
1108 
1109                 // reset the context and servlet path.
1110                 baseRequest.setContext(old_context);
1111                 __context.set(old_context);
1112                 baseRequest.setContextPath(old_context_path);
1113                 baseRequest.setServletPath(old_servlet_path);
1114                 baseRequest.setPathInfo(old_path_info);
1115             }
1116         }
1117     }
1118 
1119     /* ------------------------------------------------------------ */
1120     /**
1121      * @see org.eclipse.jetty.server.handler.ScopedHandler#doHandle(java.lang.String, org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest,
1122      *      javax.servlet.http.HttpServletResponse)
1123      */
1124     @Override
1125     public void doHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
1126     {
1127         final DispatcherType dispatch = baseRequest.getDispatcherType();
1128         final boolean new_context = baseRequest.takeNewContext();
1129         try
1130         {
1131             if (new_context)
1132             {
1133                 // Handle the REALLY SILLY request events!
1134                 if (!_servletRequestAttributeListeners.isEmpty())
1135                     for (ServletRequestAttributeListener l :_servletRequestAttributeListeners)
1136                         baseRequest.addEventListener(l);
1137 
1138                 if (!_servletRequestListeners.isEmpty())
1139                 {
1140                     final ServletRequestEvent sre = new ServletRequestEvent(_scontext,request);
1141                     for (ServletRequestListener l : _servletRequestListeners)
1142                         l.requestInitialized(sre);
1143                 }
1144             }
1145 
1146             if (DispatcherType.REQUEST.equals(dispatch) && isProtectedTarget(target))
1147             {
1148                 response.sendError(HttpServletResponse.SC_NOT_FOUND);
1149                 baseRequest.setHandled(true);
1150                 return;
1151             }
1152 
1153             // start manual inline of nextHandle(target,baseRequest,request,response);
1154             // noinspection ConstantIfStatement
1155             if (never())
1156                 nextHandle(target,baseRequest,request,response);
1157             else if (_nextScope != null && _nextScope == _handler)
1158                 _nextScope.doHandle(target,baseRequest,request,response);
1159             else if (_handler != null)
1160                 _handler.handle(target,baseRequest,request,response);
1161             // end manual inline
1162         }
1163         finally
1164         {
1165             // Handle more REALLY SILLY request events!
1166             if (new_context)
1167             {
1168                 if (!_servletRequestListeners.isEmpty())
1169                 {
1170                     final ServletRequestEvent sre = new ServletRequestEvent(_scontext,request);
1171                     for (int i=_servletRequestListeners.size();i-->0;)
1172                         _servletRequestListeners.get(i).requestDestroyed(sre);
1173                 }
1174 
1175                 if (!_servletRequestAttributeListeners.isEmpty())
1176                 {
1177                     for (int i=_servletRequestAttributeListeners.size();i-->0;)
1178                         baseRequest.removeEventListener(_servletRequestAttributeListeners.get(i));
1179                 }
1180             }
1181         }
1182     }
1183 
1184 
1185 
1186 
1187     /**
1188      * @param request A request that is applicable to the scope, or null
1189      * @param reason An object that indicates the reason the scope is being entered.
1190      */
1191     protected void enterScope(Request request, Object reason)
1192     {
1193         if (!_contextListeners.isEmpty())
1194         {
1195             for (ContextScopeListener listener:_contextListeners)
1196             {
1197                 try
1198                 {
1199                     listener.enterScope(_scontext,request,reason);
1200                 }
1201                 catch(Throwable e)
1202                 {
1203                     LOG.warn(e);
1204                 }
1205             }
1206         }
1207     }
1208 
1209 
1210     /**
1211      * @param request A request that is applicable to the scope, or null
1212      */
1213     protected void exitScope(Request request)
1214     {
1215         if (!_contextListeners.isEmpty())
1216         {
1217             for (int i = _contextListeners.size(); i-->0;)
1218             {
1219                 try
1220                 {
1221                     _contextListeners.get(i).exitScope(_scontext,request);
1222                 }
1223                 catch(Throwable e)
1224                 {
1225                     LOG.warn(e);
1226                 }
1227             }
1228         }
1229     }
1230 
1231     /* ------------------------------------------------------------ */
1232     /**
1233      * Handle a runnable in the scope of this context and a particular request
1234      * @param request The request to scope the thread to (may be null if no particular request is in scope)
1235      * @param runnable The runnable to run.
1236      */
1237     public void handle(Request request, Runnable runnable)
1238     {
1239         ClassLoader old_classloader = null;
1240         Thread current_thread = null;
1241         Context old_context = __context.get();
1242 
1243         // Are we already in the scope?
1244         if (old_context==_scontext)
1245         {
1246             runnable.run();
1247             return;
1248         }
1249 
1250         // Nope, so enter the scope and then exit
1251         try
1252         {
1253             __context.set(_scontext);
1254 
1255             // Set the classloader
1256             if (_classLoader != null)
1257             {
1258                 current_thread = Thread.currentThread();
1259                 old_classloader = current_thread.getContextClassLoader();
1260                 current_thread.setContextClassLoader(_classLoader);
1261             }
1262 
1263             enterScope(request,runnable);
1264             runnable.run();
1265         }
1266         finally
1267         {
1268             exitScope(request);
1269 
1270             __context.set(old_context);
1271             if (old_classloader != null)
1272             {
1273                 current_thread.setContextClassLoader(old_classloader);
1274             }
1275         }
1276     }
1277 
1278     /* ------------------------------------------------------------ */
1279     /*
1280      * Handle a runnable in the scope of this context
1281      */
1282     public void handle(Runnable runnable)
1283     {
1284         handle(null,runnable);
1285     }
1286 
1287     /* ------------------------------------------------------------ */
1288     /**
1289      * Check the target. Called by {@link #handle(String, Request, HttpServletRequest, HttpServletResponse)} when a target within a context is determined. If
1290      * the target is protected, 404 is returned.
1291      * @param target the target to test
1292      * @return true if target is a protected target
1293      */
1294     public boolean isProtectedTarget(String target)
1295     {
1296         if (target == null || _protectedTargets == null)
1297             return false;
1298 
1299         while (target.startsWith("//"))
1300             target=URIUtil.compactPath(target);
1301 
1302         for (int i=0; i<_protectedTargets.length; i++)
1303         {
1304             String t=_protectedTargets[i];
1305             if (StringUtil.startsWithIgnoreCase(target,t))
1306             {
1307                 if (target.length()==t.length())
1308                     return true;
1309 
1310                 // Check that the target prefix really is a path segment, thus
1311                 // it can end with /, a query, a target or a parameter
1312                 char c=target.charAt(t.length());
1313                 if (c=='/'||c=='?'||c=='#'||c==';')
1314                     return true;
1315             }
1316         }
1317         return false;
1318     }
1319 
1320 
1321     /* ------------------------------------------------------------ */
1322     /**
1323      * @param targets Array of URL prefix. Each prefix is in the form /path and will match
1324      * either /path exactly or /path/anything
1325      */
1326     public void setProtectedTargets (String[] targets)
1327     {
1328         if (targets == null)
1329         {
1330             _protectedTargets = null;
1331             return;
1332         }
1333 
1334         _protectedTargets = Arrays.copyOf(targets, targets.length);
1335     }
1336 
1337     /* ------------------------------------------------------------ */
1338     public String[] getProtectedTargets()
1339     {
1340         if (_protectedTargets == null)
1341             return null;
1342 
1343         return Arrays.copyOf(_protectedTargets, _protectedTargets.length);
1344     }
1345 
1346 
1347     /* ------------------------------------------------------------ */
1348     /*
1349      * @see javax.servlet.ServletContext#removeAttribute(java.lang.String)
1350      */
1351     @Override
1352     public void removeAttribute(String name)
1353     {
1354         _attributes.removeAttribute(name);
1355     }
1356 
1357     /* ------------------------------------------------------------ */
1358     /*
1359      * Set a context attribute. Attributes set via this API cannot be overriden by the ServletContext.setAttribute API. Their lifecycle spans the stop/start of
1360      * a context. No attribute listener events are triggered by this API.
1361      *
1362      * @see javax.servlet.ServletContext#setAttribute(java.lang.String, java.lang.Object)
1363      */
1364     @Override
1365     public void setAttribute( String name, Object value)
1366     {
1367         _attributes.setAttribute(name,value);
1368     }
1369 
1370     /* ------------------------------------------------------------ */
1371     /**
1372      * @param attributes
1373      *            The attributes to set.
1374      */
1375     public void setAttributes(Attributes attributes)
1376     {
1377         _attributes.clearAttributes();
1378         _attributes.addAll(attributes);
1379     }
1380 
1381     /* ------------------------------------------------------------ */
1382     @Override
1383     public void clearAttributes()
1384     {
1385         _attributes.clearAttributes();
1386     }
1387 
1388     /* ------------------------------------------------------------ */
1389     public void setManagedAttribute(String name, Object value)
1390     {
1391         Object old = _managedAttributes.put(name,value);
1392         updateBean(old,value);
1393     }
1394 
1395     /* ------------------------------------------------------------ */
1396     /**
1397      * @param classLoader
1398      *            The classLoader to set.
1399      */
1400     public void setClassLoader(ClassLoader classLoader)
1401     {
1402         _classLoader = classLoader;
1403     }
1404 
1405     /* ------------------------------------------------------------ */
1406     /**
1407      * @param contextPath
1408      *            The _contextPath to set.
1409      */
1410     public void setContextPath(String contextPath)
1411     {
1412         if (contextPath == null)
1413             throw new IllegalArgumentException("null contextPath");
1414 
1415         if (contextPath.endsWith("/*"))
1416         {
1417             LOG.warn(this+" contextPath ends with /*");
1418             contextPath=contextPath.substring(0,contextPath.length()-2);
1419         }
1420         else if (contextPath.length()>1 && contextPath.endsWith("/"))
1421         {
1422             LOG.warn(this+" contextPath ends with /");
1423             contextPath=contextPath.substring(0,contextPath.length()-1);
1424         }
1425 
1426         if (contextPath.length()==0)
1427         {
1428             LOG.warn("Empty contextPath");
1429             contextPath="/";
1430         }
1431 
1432         _contextPath = contextPath;
1433 
1434         if (getServer() != null && (getServer().isStarting() || getServer().isStarted()))
1435         {
1436             Handler[] contextCollections = getServer().getChildHandlersByClass(ContextHandlerCollection.class);
1437             for (int h = 0; contextCollections != null && h < contextCollections.length; h++)
1438                 ((ContextHandlerCollection)contextCollections[h]).mapContexts();
1439         }
1440     }
1441 
1442     /* ------------------------------------------------------------ */
1443     /**
1444      * @param servletContextName
1445      *            The servletContextName to set.
1446      */
1447     public void setDisplayName(String servletContextName)
1448     {
1449         _displayName = servletContextName;
1450     }
1451 
1452     /* ------------------------------------------------------------ */
1453     /**
1454      * @return Returns the resourceBase.
1455      */
1456     public Resource getBaseResource()
1457     {
1458         if (_baseResource == null)
1459             return null;
1460         return _baseResource;
1461     }
1462 
1463     /* ------------------------------------------------------------ */
1464     /**
1465      * @return Returns the base resource as a string.
1466      */
1467     @ManagedAttribute("document root for context")
1468     public String getResourceBase()
1469     {
1470         if (_baseResource == null)
1471             return null;
1472         return _baseResource.toString();
1473     }
1474 
1475     /* ------------------------------------------------------------ */
1476     /**
1477      * Set the base resource for this context.
1478      * @param base The resource used as the base for all static content of this context.
1479      * @see #setResourceBase(String)
1480      */
1481     public void setBaseResource(Resource base)
1482     {
1483         _baseResource = base;
1484     }
1485 
1486     /* ------------------------------------------------------------ */
1487     /**
1488      * Set the base resource for this context.
1489      * @param resourceBase A string representing the base resource for the context. Any string accepted
1490      * by {@link Resource#newResource(String)} may be passed and the call is equivalent to
1491      * <code>setBaseResource(newResource(resourceBase));</code>
1492      */
1493     public void setResourceBase(String resourceBase)
1494     {
1495         try
1496         {
1497             setBaseResource(newResource(resourceBase));
1498         }
1499         catch (Exception e)
1500         {
1501             LOG.warn(e.toString());
1502             LOG.debug(e);
1503             throw new IllegalArgumentException(resourceBase);
1504         }
1505     }
1506 
1507     /* ------------------------------------------------------------ */
1508     /**
1509      * @return Returns the mimeTypes.
1510      */
1511     public MimeTypes getMimeTypes()
1512     {
1513         if (_mimeTypes == null)
1514             _mimeTypes = new MimeTypes();
1515         return _mimeTypes;
1516     }
1517 
1518     /* ------------------------------------------------------------ */
1519     /**
1520      * @param mimeTypes
1521      *            The mimeTypes to set.
1522      */
1523     public void setMimeTypes(MimeTypes mimeTypes)
1524     {
1525         _mimeTypes = mimeTypes;
1526     }
1527 
1528     /* ------------------------------------------------------------ */
1529     public void setWelcomeFiles(String[] files)
1530     {
1531         _welcomeFiles = files;
1532     }
1533 
1534     /* ------------------------------------------------------------ */
1535     /**
1536      * @return The names of the files which the server should consider to be welcome files in this context.
1537      * @see <a href="http://jcp.org/aboutJava/communityprocess/final/jsr154/index.html">The Servlet Specification</a>
1538      * @see #setWelcomeFiles
1539      */
1540     @ManagedAttribute(value="Partial URIs of directory welcome files", readonly=true)
1541     public String[] getWelcomeFiles()
1542     {
1543         return _welcomeFiles;
1544     }
1545 
1546     /* ------------------------------------------------------------ */
1547     /**
1548      * @return Returns the errorHandler.
1549      */
1550     @ManagedAttribute("The error handler to use for the context")
1551     public ErrorHandler getErrorHandler()
1552     {
1553         return _errorHandler;
1554     }
1555 
1556     /* ------------------------------------------------------------ */
1557     /**
1558      * @param errorHandler
1559      *            The errorHandler to set.
1560      */
1561     public void setErrorHandler(ErrorHandler errorHandler)
1562     {
1563         if (errorHandler != null)
1564             errorHandler.setServer(getServer());
1565         updateBean(_errorHandler,errorHandler,true);
1566         _errorHandler = errorHandler;
1567     }
1568 
1569     /* ------------------------------------------------------------ */
1570     @ManagedAttribute("The maximum content size")
1571     public int getMaxFormContentSize()
1572     {
1573         return _maxFormContentSize;
1574     }
1575 
1576     /* ------------------------------------------------------------ */
1577     /**
1578      * Set the maximum size of a form post, to protect against DOS attacks from large forms.
1579      * @param maxSize the maximum size of the form content (in bytes)
1580      */
1581     public void setMaxFormContentSize(int maxSize)
1582     {
1583         _maxFormContentSize = maxSize;
1584     }
1585 
1586     /* ------------------------------------------------------------ */
1587     public int getMaxFormKeys()
1588     {
1589         return _maxFormKeys;
1590     }
1591 
1592     /* ------------------------------------------------------------ */
1593     /**
1594      * Set the maximum number of form Keys to protect against DOS attack from crafted hash keys.
1595      * @param max the maximum number of form keys
1596      */
1597     public void setMaxFormKeys(int max)
1598     {
1599         _maxFormKeys = max;
1600     }
1601 
1602     /* ------------------------------------------------------------ */
1603     /**
1604      * @return True if URLs are compacted to replace multiple '/'s with a single '/'
1605      */
1606     public boolean isCompactPath()
1607     {
1608         return _compactPath;
1609     }
1610 
1611     /* ------------------------------------------------------------ */
1612     /**
1613      * @param compactPath
1614      *            True if URLs are compacted to replace multiple '/'s with a single '/'
1615      */
1616     public void setCompactPath(boolean compactPath)
1617     {
1618         _compactPath = compactPath;
1619     }
1620 
1621     /* ------------------------------------------------------------ */
1622     @Override
1623     public String toString()
1624     {
1625         String[] vhosts = getVirtualHosts();
1626 
1627         StringBuilder b = new StringBuilder();
1628 
1629         Package pkg = getClass().getPackage();
1630         if (pkg != null)
1631         {
1632             String p = pkg.getName();
1633             if (p != null && p.length() > 0)
1634             {
1635                 String[] ss = p.split("\\.");
1636                 for (String s : ss)
1637                     b.append(s.charAt(0)).append('.');
1638             }
1639         }
1640         b.append(getClass().getSimpleName()).append('@').append(Integer.toString(hashCode(),16));
1641         b.append('{').append(getContextPath()).append(',').append(getBaseResource()).append(',').append(_availability);
1642 
1643         if (vhosts != null && vhosts.length > 0)
1644             b.append(',').append(vhosts[0]);
1645         b.append('}');
1646 
1647         return b.toString();
1648     }
1649 
1650     /* ------------------------------------------------------------ */
1651     public synchronized Class<?> loadClass(String className) throws ClassNotFoundException
1652     {
1653         if (className == null)
1654             return null;
1655 
1656         if (_classLoader == null)
1657             return Loader.loadClass(this.getClass(),className);
1658 
1659         return _classLoader.loadClass(className);
1660     }
1661 
1662     /* ------------------------------------------------------------ */
1663     public void addLocaleEncoding(String locale, String encoding)
1664     {
1665         if (_localeEncodingMap == null)
1666             _localeEncodingMap = new HashMap<String, String>();
1667         _localeEncodingMap.put(locale,encoding);
1668     }
1669 
1670     /* ------------------------------------------------------------ */
1671     public String getLocaleEncoding(String locale)
1672     {
1673         if (_localeEncodingMap == null)
1674             return null;
1675         String encoding = _localeEncodingMap.get(locale);
1676         return encoding;
1677     }
1678 
1679     /* ------------------------------------------------------------ */
1680     /**
1681      * Get the character encoding for a locale. The full locale name is first looked up in the map of encodings. If no encoding is found, then the locale
1682      * language is looked up.
1683      *
1684      * @param locale
1685      *            a <code>Locale</code> value
1686      * @return a <code>String</code> representing the character encoding for the locale or null if none found.
1687      */
1688     public String getLocaleEncoding(Locale locale)
1689     {
1690         if (_localeEncodingMap == null)
1691             return null;
1692         String encoding = _localeEncodingMap.get(locale.toString());
1693         if (encoding == null)
1694             encoding = _localeEncodingMap.get(locale.getLanguage());
1695         return encoding;
1696     }
1697 
1698     /* ------------------------------------------------------------ */
1699     /**
1700      * Get all of the locale encodings
1701      *
1702      * @return a map of all the locale encodings: key is name of the locale and value is the char encoding
1703      */
1704     public Map<String,String> getLocaleEncodings()
1705     {
1706         if (_localeEncodingMap == null)
1707             return null;
1708         return Collections.unmodifiableMap(_localeEncodingMap);
1709     }
1710 
1711     /* ------------------------------------------------------------ */
1712     /*
1713      */
1714     public Resource getResource(String path) throws MalformedURLException
1715     {
1716         if (path == null || !path.startsWith(URIUtil.SLASH))
1717             throw new MalformedURLException(path);
1718 
1719         if (_baseResource == null)
1720             return null;
1721 
1722         try
1723         {
1724             path = URIUtil.canonicalPath(path);
1725             Resource resource = _baseResource.addPath(path);
1726 
1727             if (checkAlias(path,resource))
1728                 return resource;
1729             return null;
1730         }
1731         catch (Exception e)
1732         {
1733             LOG.ignore(e);
1734         }
1735 
1736         return null;
1737     }
1738 
1739     /* ------------------------------------------------------------ */
1740     /**
1741      * @param path the path to check the alias for
1742      * @param resource the resource
1743      * @return True if the alias is OK
1744      */
1745     public boolean checkAlias(String path, Resource resource)
1746     {
1747         // Is the resource aliased?
1748         if (resource.isAlias())
1749         {
1750             if (LOG.isDebugEnabled())
1751                 LOG.debug("Aliased resource: " + resource + "~=" + resource.getAlias());
1752 
1753             // alias checks
1754             for (Iterator<AliasCheck> i=_aliasChecks.iterator();i.hasNext();)
1755             {
1756                 AliasCheck check = i.next();
1757                 if (check.check(path,resource))
1758                 {
1759                     if (LOG.isDebugEnabled())
1760                         LOG.debug("Aliased resource: " + resource + " approved by " + check);
1761                     return true;
1762                 }
1763             }
1764             return false;
1765         }
1766         return true;
1767     }
1768 
1769     /* ------------------------------------------------------------ */
1770     /**
1771      * Convert URL to Resource wrapper for {@link Resource#newResource(URL)} enables extensions to provide alternate resource implementations.
1772      * @param url the url to convert to a Resource
1773      * @return the Resource for that url
1774      * @throws IOException if unable to create a Resource from the URL
1775      */
1776     public Resource newResource(URL url) throws IOException
1777     {
1778         return Resource.newResource(url);
1779     }
1780 
1781     /* ------------------------------------------------------------ */
1782     /**
1783      * Convert URL to Resource wrapper for {@link Resource#newResource(URL)} enables extensions to provide alternate resource implementations.
1784      * @param uri the URI to convert to a Resource
1785      * @return the Resource for that URI
1786      * @throws IOException if unable to create a Resource from the URL
1787      */
1788     public Resource newResource(URI uri) throws IOException
1789     {
1790         return Resource.newResource(uri);
1791     }
1792 
1793     /* ------------------------------------------------------------ */
1794     /**
1795      * Convert a URL or path to a Resource. The default implementation is a wrapper for {@link Resource#newResource(String)}.
1796      *
1797      * @param urlOrPath
1798      *            The URL or path to convert
1799      * @return The Resource for the URL/path
1800      * @throws IOException
1801      *             The Resource could not be created.
1802      */
1803     public Resource newResource(String urlOrPath) throws IOException
1804     {
1805         return Resource.newResource(urlOrPath);
1806     }
1807 
1808     /* ------------------------------------------------------------ */
1809     /*
1810      */
1811     public Set<String> getResourcePaths(String path)
1812     {
1813         try
1814         {
1815             path = URIUtil.canonicalPath(path);
1816             Resource resource = getResource(path);
1817 
1818             if (resource != null && resource.exists())
1819             {
1820                 if (!path.endsWith(URIUtil.SLASH))
1821                     path = path + URIUtil.SLASH;
1822 
1823                 String[] l = resource.list();
1824                 if (l != null)
1825                 {
1826                     HashSet<String> set = new HashSet<String>();
1827                     for (int i = 0; i < l.length; i++)
1828                         set.add(path + l[i]);
1829                     return set;
1830                 }
1831             }
1832         }
1833         catch (Exception e)
1834         {
1835             LOG.ignore(e);
1836         }
1837         return Collections.emptySet();
1838     }
1839 
1840     /* ------------------------------------------------------------ */
1841     private String normalizeHostname(String host)
1842     {
1843         if (host == null)
1844             return null;
1845 
1846         if (host.endsWith("."))
1847             return host.substring(0,host.length() - 1);
1848 
1849         return host;
1850     }
1851 
1852     /* ------------------------------------------------------------ */
1853     /**
1854      * Add an AliasCheck instance to possibly permit aliased resources
1855      * @param check The alias checker
1856      */
1857     public void addAliasCheck(AliasCheck check)
1858     {
1859         _aliasChecks.add(check);
1860     }
1861 
1862     /* ------------------------------------------------------------ */
1863     /**
1864      * @return Mutable list of Alias checks
1865      */
1866     public List<AliasCheck> getAliasChecks()
1867     {
1868         return _aliasChecks;
1869     }
1870 
1871     /* ------------------------------------------------------------ */
1872     /**
1873      * @param checks list of AliasCheck instances
1874      */
1875     public void setAliasChecks(List<AliasCheck> checks)
1876     {
1877         _aliasChecks.clear();
1878         _aliasChecks.addAll(checks);
1879     }
1880 
1881     /* ------------------------------------------------------------ */
1882     /**
1883      * clear the list of AliasChecks
1884      */
1885     public void clearAliasChecks()
1886     {
1887         _aliasChecks.clear();
1888     }
1889 
1890 
1891     /* ------------------------------------------------------------ */
1892     /**
1893      * Context.
1894      * <p>
1895      * A partial implementation of {@link javax.servlet.ServletContext}. A complete implementation is provided by the derived {@link ContextHandler}.
1896      * </p>
1897      *
1898      *
1899      */
1900     public class Context extends StaticContext
1901     {
1902         protected boolean _enabled = true; //whether or not the dynamic API is enabled for callers
1903         protected boolean _extendedListenerTypes = false;
1904 
1905 
1906         /* ------------------------------------------------------------ */
1907         protected Context()
1908         {
1909         }
1910 
1911         /* ------------------------------------------------------------ */
1912         public ContextHandler getContextHandler()
1913         {
1914             return ContextHandler.this;
1915         }
1916 
1917         /* ------------------------------------------------------------ */
1918         /*
1919          * @see javax.servlet.ServletContext#getContext(java.lang.String)
1920          */
1921         @Override
1922         public ServletContext getContext(String uripath)
1923         {
1924             List<ContextHandler> contexts = new ArrayList<ContextHandler>();
1925             Handler[] handlers = getServer().getChildHandlersByClass(ContextHandler.class);
1926             String matched_path = null;
1927 
1928             for (Handler handler : handlers)
1929             {
1930                 if (handler == null)
1931                     continue;
1932                 ContextHandler ch = (ContextHandler)handler;
1933                 String context_path = ch.getContextPath();
1934 
1935                 if (uripath.equals(context_path) || (uripath.startsWith(context_path) && uripath.charAt(context_path.length()) == '/')
1936                         || "/".equals(context_path))
1937                 {
1938                     // look first for vhost matching context only
1939                     if (getVirtualHosts() != null && getVirtualHosts().length > 0)
1940                     {
1941                         if (ch.getVirtualHosts() != null && ch.getVirtualHosts().length > 0)
1942                         {
1943                             for (String h1 : getVirtualHosts())
1944                                 for (String h2 : ch.getVirtualHosts())
1945                                     if (h1.equals(h2))
1946                                     {
1947                                         if (matched_path == null || context_path.length() > matched_path.length())
1948                                         {
1949                                             contexts.clear();
1950                                             matched_path = context_path;
1951                                         }
1952 
1953                                         if (matched_path.equals(context_path))
1954                                             contexts.add(ch);
1955                                     }
1956                         }
1957                     }
1958                     else
1959                     {
1960                         if (matched_path == null || context_path.length() > matched_path.length())
1961                         {
1962                             contexts.clear();
1963                             matched_path = context_path;
1964                         }
1965 
1966                         if (matched_path.equals(context_path))
1967                             contexts.add(ch);
1968                     }
1969                 }
1970             }
1971 
1972             if (contexts.size() > 0)
1973                 return contexts.get(0)._scontext;
1974 
1975             // try again ignoring virtual hosts
1976             matched_path = null;
1977             for (Handler handler : handlers)
1978             {
1979                 if (handler == null)
1980                     continue;
1981                 ContextHandler ch = (ContextHandler)handler;
1982                 String context_path = ch.getContextPath();
1983 
1984                 if (uripath.equals(context_path) || (uripath.startsWith(context_path) && uripath.charAt(context_path.length()) == '/')
1985                         || "/".equals(context_path))
1986                 {
1987                     if (matched_path == null || context_path.length() > matched_path.length())
1988                     {
1989                         contexts.clear();
1990                         matched_path = context_path;
1991                     }
1992 
1993                     if (matched_path != null && matched_path.equals(context_path))
1994                         contexts.add(ch);
1995                 }
1996             }
1997 
1998             if (contexts.size() > 0)
1999                 return contexts.get(0)._scontext;
2000             return null;
2001         }
2002 
2003         /* ------------------------------------------------------------ */
2004         /*
2005          * @see javax.servlet.ServletContext#getMimeType(java.lang.String)
2006          */
2007         @Override
2008         public String getMimeType(String file)
2009         {
2010             if (_mimeTypes == null)
2011                 return null;
2012             return _mimeTypes.getMimeByExtension(file);
2013         }
2014 
2015         /* ------------------------------------------------------------ */
2016         /*
2017          * @see javax.servlet.ServletContext#getRequestDispatcher(java.lang.String)
2018          */
2019         @Override
2020         public RequestDispatcher getRequestDispatcher(String uriInContext)
2021         {
2022             if (uriInContext == null)
2023                 return null;
2024 
2025             if (!uriInContext.startsWith("/"))
2026                 return null;
2027 
2028             try
2029             {
2030                 HttpURI uri = new HttpURI(null,null,0,uriInContext);
2031 
2032                 String pathInfo=URIUtil.canonicalPath(uri.getDecodedPath());
2033                 if (pathInfo==null)
2034                     return null;
2035 
2036                 String contextPath=getContextPath();
2037                 if (contextPath!=null && contextPath.length()>0)
2038                     uri.setPath(URIUtil.addPaths(contextPath,uri.getPath()));
2039 
2040                 return new Dispatcher(ContextHandler.this,uri,pathInfo);
2041             }
2042             catch (Exception e)
2043             {
2044                 LOG.ignore(e);
2045             }
2046             return null;
2047         }
2048 
2049         /* ------------------------------------------------------------ */
2050         /*
2051          * @see javax.servlet.ServletContext#getRealPath(java.lang.String)
2052          */
2053         @Override
2054         public String getRealPath(String path)
2055         {
2056             if (path == null)
2057                 return null;
2058             if (path.length() == 0)
2059                 path = URIUtil.SLASH;
2060             else if (path.charAt(0) != '/')
2061                 path = URIUtil.SLASH + path;
2062 
2063             try
2064             {
2065                 Resource resource = ContextHandler.this.getResource(path);
2066                 if (resource != null)
2067                 {
2068                     File file = resource.getFile();
2069                     if (file != null)
2070                         return file.getCanonicalPath();
2071                 }
2072             }
2073             catch (Exception e)
2074             {
2075                 LOG.ignore(e);
2076             }
2077 
2078             return null;
2079         }
2080 
2081         /* ------------------------------------------------------------ */
2082         @Override
2083         public URL getResource(String path) throws MalformedURLException
2084         {
2085             Resource resource = ContextHandler.this.getResource(path);
2086             if (resource != null && resource.exists())
2087                 return resource.getURI().toURL();
2088             return null;
2089         }
2090 
2091         /* ------------------------------------------------------------ */
2092         /*
2093          * @see javax.servlet.ServletContext#getResourceAsStream(java.lang.String)
2094          */
2095         @Override
2096         public InputStream getResourceAsStream(String path)
2097         {
2098             try
2099             {
2100                 URL url = getResource(path);
2101                 if (url == null)
2102                     return null;
2103                 Resource r = Resource.newResource(url);
2104                 return r.getInputStream();
2105             }
2106             catch (Exception e)
2107             {
2108                 LOG.ignore(e);
2109                 return null;
2110             }
2111         }
2112 
2113         /* ------------------------------------------------------------ */
2114         /*
2115          * @see javax.servlet.ServletContext#getResourcePaths(java.lang.String)
2116          */
2117         @Override
2118         public Set<String> getResourcePaths(String path)
2119         {
2120             return ContextHandler.this.getResourcePaths(path);
2121         }
2122 
2123         /* ------------------------------------------------------------ */
2124         /*
2125          * @see javax.servlet.ServletContext#log(java.lang.Exception, java.lang.String)
2126          */
2127         @Override
2128         public void log(Exception exception, String msg)
2129         {
2130             _logger.warn(msg,exception);
2131         }
2132 
2133         /* ------------------------------------------------------------ */
2134         /*
2135          * @see javax.servlet.ServletContext#log(java.lang.String)
2136          */
2137         @Override
2138         public void log(String msg)
2139         {
2140             _logger.info(msg);
2141         }
2142 
2143         /* ------------------------------------------------------------ */
2144         /*
2145          * @see javax.servlet.ServletContext#log(java.lang.String, java.lang.Throwable)
2146          */
2147         @Override
2148         public void log(String message, Throwable throwable)
2149         {
2150             _logger.warn(message,throwable);
2151         }
2152 
2153         /* ------------------------------------------------------------ */
2154         /*
2155          * @see javax.servlet.ServletContext#getInitParameter(java.lang.String)
2156          */
2157         @Override
2158         public String getInitParameter(String name)
2159         {
2160             return ContextHandler.this.getInitParameter(name);
2161         }
2162 
2163         /* ------------------------------------------------------------ */
2164         /*
2165          * @see javax.servlet.ServletContext#getInitParameterNames()
2166          */
2167         @Override
2168         public Enumeration<String> getInitParameterNames()
2169         {
2170             return ContextHandler.this.getInitParameterNames();
2171         }
2172 
2173         /* ------------------------------------------------------------ */
2174         /*
2175          * @see javax.servlet.ServletContext#getAttribute(java.lang.String)
2176          */
2177         @Override
2178         public synchronized Object getAttribute(String name)
2179         {
2180             Object o = ContextHandler.this.getAttribute(name);
2181             if (o == null)
2182                 o = super.getAttribute(name);
2183             return o;
2184         }
2185 
2186         /* ------------------------------------------------------------ */
2187         /*
2188          * @see javax.servlet.ServletContext#getAttributeNames()
2189          */
2190         @Override
2191         public synchronized Enumeration<String> getAttributeNames()
2192         {
2193             HashSet<String> set = new HashSet<String>();
2194             Enumeration<String> e = super.getAttributeNames();
2195             while (e.hasMoreElements())
2196                 set.add(e.nextElement());
2197             e = _attributes.getAttributeNames();
2198             while (e.hasMoreElements())
2199                 set.add(e.nextElement());
2200 
2201             return Collections.enumeration(set);
2202         }
2203 
2204         /* ------------------------------------------------------------ */
2205         /*
2206          * @see javax.servlet.ServletContext#setAttribute(java.lang.String, java.lang.Object)
2207          */
2208         @Override
2209         public synchronized void setAttribute(String name, Object value)
2210         {
2211             Object old_value = super.getAttribute(name);
2212 
2213             if (value == null)
2214                 super.removeAttribute(name);
2215             else
2216                 super.setAttribute(name,value);
2217 
2218             if (!_servletContextAttributeListeners.isEmpty())
2219             {
2220                 ServletContextAttributeEvent event = new ServletContextAttributeEvent(_scontext,name,old_value == null?value:old_value);
2221 
2222                 for (ServletContextAttributeListener l : _servletContextAttributeListeners)
2223                 {
2224                     if (old_value == null)
2225                         l.attributeAdded(event);
2226                     else if (value == null)
2227                         l.attributeRemoved(event);
2228                     else
2229                         l.attributeReplaced(event);
2230                 }
2231             }
2232         }
2233 
2234         /* ------------------------------------------------------------ */
2235         /*
2236          * @see javax.servlet.ServletContext#removeAttribute(java.lang.String)
2237          */
2238         @Override
2239         public synchronized void removeAttribute(String name)
2240         {
2241             Object old_value = super.getAttribute(name);
2242             super.removeAttribute(name);
2243             if (old_value != null &&!_servletContextAttributeListeners.isEmpty())
2244             {
2245                 ServletContextAttributeEvent event = new ServletContextAttributeEvent(_scontext,name,old_value);
2246 
2247                 for (ServletContextAttributeListener l : _servletContextAttributeListeners)
2248                     l.attributeRemoved(event);
2249             }
2250         }
2251 
2252         /* ------------------------------------------------------------ */
2253         /*
2254          * @see javax.servlet.ServletContext#getServletContextName()
2255          */
2256         @Override
2257         public String getServletContextName()
2258         {
2259             String name = ContextHandler.this.getDisplayName();
2260             if (name == null)
2261                 name = ContextHandler.this.getContextPath();
2262             return name;
2263         }
2264 
2265         /* ------------------------------------------------------------ */
2266         @Override
2267         public String getContextPath()
2268         {
2269             if ((_contextPath != null) && _contextPath.equals(URIUtil.SLASH))
2270                 return "";
2271 
2272             return _contextPath;
2273         }
2274 
2275         /* ------------------------------------------------------------ */
2276         @Override
2277         public String toString()
2278         {
2279             return "ServletContext@" + ContextHandler.this.toString();
2280         }
2281 
2282         /* ------------------------------------------------------------ */
2283         @Override
2284         public boolean setInitParameter(String name, String value)
2285         {
2286             if (ContextHandler.this.getInitParameter(name) != null)
2287                 return false;
2288             ContextHandler.this.getInitParams().put(name,value);
2289             return true;
2290         }
2291 
2292         @Override
2293         public void addListener(String className)
2294         {
2295             if (!_enabled)
2296                 throw new UnsupportedOperationException();
2297 
2298             try
2299             {
2300                 @SuppressWarnings({ "unchecked", "rawtypes" })
2301                 Class<? extends EventListener> clazz = _classLoader==null?Loader.loadClass(ContextHandler.class,className):(Class)_classLoader.loadClass(className);
2302                 addListener(clazz);
2303             }
2304             catch (ClassNotFoundException e)
2305             {
2306                 throw new IllegalArgumentException(e);
2307             }
2308         }
2309 
2310         @Override
2311         public <T extends EventListener> void addListener(T t)
2312         {
2313             if (!_enabled)
2314                 throw new UnsupportedOperationException();
2315 
2316             checkListener(t.getClass());
2317 
2318             ContextHandler.this.addEventListener(t);
2319             ContextHandler.this.addProgrammaticListener(t);
2320         }
2321 
2322         @Override
2323         public void addListener(Class<? extends EventListener> listenerClass)
2324         {
2325             if (!_enabled)
2326                 throw new UnsupportedOperationException();
2327 
2328             try
2329             {
2330                 EventListener e = createListener(listenerClass);
2331                 addListener(e);
2332             }
2333             catch (ServletException e)
2334             {
2335                 throw new IllegalArgumentException(e);
2336             }
2337         }
2338 
2339         @Override
2340         public <T extends EventListener> T createListener(Class<T> clazz) throws ServletException
2341         {
2342             try
2343             {
2344                 return createInstance(clazz);
2345             }
2346             catch (Exception e)
2347             {
2348                 throw new ServletException(e);
2349             }
2350         }
2351 
2352 
2353         public void checkListener (Class<? extends EventListener> listener) throws IllegalStateException
2354         {
2355             boolean ok = false;
2356             int startIndex = (isExtendedListenerTypes()?EXTENDED_LISTENER_TYPE_INDEX:DEFAULT_LISTENER_TYPE_INDEX);
2357             for (int i=startIndex;i<SERVLET_LISTENER_TYPES.length;i++)
2358             {
2359                 if (SERVLET_LISTENER_TYPES[i].isAssignableFrom(listener))
2360                 {
2361                     ok = true;
2362                     break;
2363                 }
2364             }
2365             if (!ok)
2366                 throw new IllegalArgumentException("Inappropriate listener class "+listener.getName());
2367         }
2368 
2369         public void setExtendedListenerTypes (boolean extended)
2370         {
2371             _extendedListenerTypes = extended;
2372         }
2373 
2374         public boolean isExtendedListenerTypes()
2375         {
2376             return _extendedListenerTypes;
2377         }
2378 
2379         @Override
2380         public ClassLoader getClassLoader()
2381         {
2382             if (!_enabled)
2383                 throw new UnsupportedOperationException();
2384 
2385             //no security manager just return the classloader
2386             if (!_usingSecurityManager)
2387                 return _classLoader;
2388             else
2389             {
2390                 //check to see if the classloader of the caller is the same as the context
2391                 //classloader, or a parent of it
2392                 try
2393                 {
2394                     Class<?> reflect = Loader.loadClass(getClass(), "sun.reflect.Reflection");
2395                     Method getCallerClass = reflect.getMethod("getCallerClass", Integer.TYPE);
2396                     Class<?> caller = (Class<?>)getCallerClass.invoke(null, 2);
2397 
2398                     boolean ok = false;
2399                     ClassLoader callerLoader = caller.getClassLoader();
2400                     while (!ok && callerLoader != null)
2401                     {
2402                         if (callerLoader == _classLoader)
2403                             ok = true;
2404                         else
2405                             callerLoader = callerLoader.getParent();
2406                     }
2407 
2408                     if (ok)
2409                         return _classLoader;
2410                 }
2411                 catch (Exception e)
2412                 {
2413                     LOG.warn("Unable to check classloader of caller",e);
2414                 }
2415 
2416                 AccessController.checkPermission(new RuntimePermission("getClassLoader"));
2417                 return _classLoader;
2418             }
2419         }
2420 
2421         @Override
2422         public JspConfigDescriptor getJspConfigDescriptor()
2423         {
2424             LOG.warn(__unimplmented);
2425             return null;
2426         }
2427 
2428         public void setJspConfigDescriptor(JspConfigDescriptor d)
2429         {
2430 
2431         }
2432 
2433         @Override
2434         public void declareRoles(String... roleNames)
2435         {
2436             if (!isStarting())
2437                 throw new IllegalStateException ();
2438             if (!_enabled)
2439                 throw new UnsupportedOperationException();
2440         }
2441 
2442         public void setEnabled(boolean enabled)
2443         {
2444             _enabled = enabled;
2445         }
2446 
2447         public boolean isEnabled()
2448         {
2449             return _enabled;
2450         }
2451 
2452         public <T> T createInstance (Class<T> clazz) throws Exception
2453         {
2454             T o = clazz.newInstance();
2455             return o;
2456         }
2457 
2458         @Override
2459         public String getVirtualServerName()
2460         {
2461             String[] hosts = getVirtualHosts();
2462             if (hosts!=null && hosts.length>0)
2463                 return hosts[0];
2464             return null;
2465         }
2466     }
2467 
2468     public static class StaticContext extends AttributesMap implements ServletContext
2469     {
2470         private int _effectiveMajorVersion = SERVLET_MAJOR_VERSION;
2471         private int _effectiveMinorVersion = SERVLET_MINOR_VERSION;
2472 
2473         /* ------------------------------------------------------------ */
2474         public StaticContext()
2475         {
2476         }
2477 
2478         @Override
2479         public ServletContext getContext(String uripath)
2480         {
2481             return null;
2482         }
2483 
2484         @Override
2485         public int getMajorVersion()
2486         {
2487             return SERVLET_MAJOR_VERSION;
2488         }
2489 
2490         @Override
2491         public String getMimeType(String file)
2492         {
2493             return null;
2494         }
2495 
2496         @Override
2497         public int getMinorVersion()
2498         {
2499             return SERVLET_MINOR_VERSION;
2500         }
2501 
2502         @Override
2503         public RequestDispatcher getNamedDispatcher(String name)
2504         {
2505             return null;
2506         }
2507 
2508         @Override
2509         public RequestDispatcher getRequestDispatcher(String uriInContext)
2510         {
2511             return null;
2512         }
2513 
2514         @Override
2515         public String getRealPath(String path)
2516         {
2517             return null;
2518         }
2519 
2520         @Override
2521         public URL getResource(String path) throws MalformedURLException
2522         {
2523             return null;
2524         }
2525 
2526         @Override
2527         public InputStream getResourceAsStream(String path)
2528         {
2529             return null;
2530         }
2531 
2532         @Override
2533         public Set<String> getResourcePaths(String path)
2534         {
2535             return null;
2536         }
2537 
2538         @Override
2539         public String getServerInfo()
2540         {
2541             return __serverInfo;
2542         }
2543 
2544         @Override
2545         @Deprecated
2546         public Servlet getServlet(String name) throws ServletException
2547         {
2548             return null;
2549         }
2550 
2551         @SuppressWarnings("unchecked")
2552         @Override
2553         @Deprecated
2554         public Enumeration<String> getServletNames()
2555         {
2556             return Collections.enumeration(Collections.EMPTY_LIST);
2557         }
2558 
2559         @SuppressWarnings("unchecked")
2560         @Override
2561         @Deprecated
2562         public Enumeration<Servlet> getServlets()
2563         {
2564             return Collections.enumeration(Collections.EMPTY_LIST);
2565         }
2566 
2567         @Override
2568         public void log(Exception exception, String msg)
2569         {
2570             LOG.warn(msg,exception);
2571         }
2572 
2573         @Override
2574         public void log(String msg)
2575         {
2576             LOG.info(msg);
2577         }
2578 
2579         @Override
2580         public void log(String message, Throwable throwable)
2581         {
2582             LOG.warn(message,throwable);
2583         }
2584 
2585         @Override
2586         public String getInitParameter(String name)
2587         {
2588             return null;
2589         }
2590 
2591         @SuppressWarnings("unchecked")
2592         @Override
2593         public Enumeration<String> getInitParameterNames()
2594         {
2595             return Collections.enumeration(Collections.EMPTY_LIST);
2596         }
2597 
2598 
2599         @Override
2600         public String getServletContextName()
2601         {
2602             return "No Context";
2603         }
2604 
2605         @Override
2606         public String getContextPath()
2607         {
2608             return null;
2609         }
2610 
2611         @Override
2612         public boolean setInitParameter(String name, String value)
2613         {
2614             return false;
2615         }
2616 
2617         @Override
2618         public Dynamic addFilter(String filterName, Class<? extends Filter> filterClass)
2619         {
2620             LOG.warn(__unimplmented);
2621             return null;
2622         }
2623 
2624         @Override
2625         public Dynamic addFilter(String filterName, Filter filter)
2626         {
2627             LOG.warn(__unimplmented);
2628             return null;
2629         }
2630 
2631         @Override
2632         public Dynamic addFilter(String filterName, String className)
2633         {
2634             LOG.warn(__unimplmented);
2635             return null;
2636         }
2637 
2638         @Override
2639         public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, Class<? extends Servlet> servletClass)
2640         {
2641             LOG.warn(__unimplmented);
2642             return null;
2643         }
2644 
2645         @Override
2646         public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet)
2647         {
2648             LOG.warn(__unimplmented);
2649             return null;
2650         }
2651 
2652         @Override
2653         public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, String className)
2654         {
2655             LOG.warn(__unimplmented);
2656             return null;
2657         }
2658 
2659         @Override
2660         public <T extends Filter> T createFilter(Class<T> c) throws ServletException
2661         {
2662             LOG.warn(__unimplmented);
2663             return null;
2664         }
2665 
2666         @Override
2667         public <T extends Servlet> T createServlet(Class<T> c) throws ServletException
2668         {
2669             LOG.warn(__unimplmented);
2670             return null;
2671         }
2672 
2673         @Override
2674         public Set<SessionTrackingMode> getDefaultSessionTrackingModes()
2675         {
2676             LOG.warn(__unimplmented);
2677             return null;
2678         }
2679 
2680         @Override
2681         public Set<SessionTrackingMode> getEffectiveSessionTrackingModes()
2682         {
2683             LOG.warn(__unimplmented);
2684             return null;
2685         }
2686 
2687         @Override
2688         public FilterRegistration getFilterRegistration(String filterName)
2689         {
2690             LOG.warn(__unimplmented);
2691             return null;
2692         }
2693 
2694         @Override
2695         public Map<String, ? extends FilterRegistration> getFilterRegistrations()
2696         {
2697             LOG.warn(__unimplmented);
2698             return null;
2699         }
2700 
2701         @Override
2702         public ServletRegistration getServletRegistration(String servletName)
2703         {
2704             LOG.warn(__unimplmented);
2705             return null;
2706         }
2707 
2708         @Override
2709         public Map<String, ? extends ServletRegistration> getServletRegistrations()
2710         {
2711             LOG.warn(__unimplmented);
2712             return null;
2713         }
2714 
2715         @Override
2716         public SessionCookieConfig getSessionCookieConfig()
2717         {
2718             LOG.warn(__unimplmented);
2719             return null;
2720         }
2721 
2722         @Override
2723         public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes)
2724         {
2725             LOG.warn(__unimplmented);
2726         }
2727 
2728         @Override
2729         public void addListener(String className)
2730         {
2731             LOG.warn(__unimplmented);
2732         }
2733 
2734         @Override
2735         public <T extends EventListener> void addListener(T t)
2736         {
2737             LOG.warn(__unimplmented);
2738         }
2739 
2740         @Override
2741         public void addListener(Class<? extends EventListener> listenerClass)
2742         {
2743             LOG.warn(__unimplmented);
2744         }
2745 
2746         @Override
2747         public <T extends EventListener> T createListener(Class<T> clazz) throws ServletException
2748         {
2749             try
2750             {
2751                 return clazz.newInstance();
2752             }
2753             catch (InstantiationException e)
2754             {
2755                 throw new ServletException(e);
2756             }
2757             catch (IllegalAccessException e)
2758             {
2759                 throw new ServletException(e);
2760             }
2761         }
2762 
2763         @Override
2764         public ClassLoader getClassLoader()
2765         {
2766             return ContextHandler.class.getClassLoader();
2767         }
2768 
2769         @Override
2770         public int getEffectiveMajorVersion()
2771         {
2772             return _effectiveMajorVersion;
2773         }
2774 
2775         @Override
2776         public int getEffectiveMinorVersion()
2777         {
2778             return _effectiveMinorVersion;
2779         }
2780 
2781         public void setEffectiveMajorVersion (int v)
2782         {
2783             _effectiveMajorVersion = v;
2784         }
2785 
2786         public void setEffectiveMinorVersion (int v)
2787         {
2788             _effectiveMinorVersion = v;
2789         }
2790 
2791         @Override
2792         public JspConfigDescriptor getJspConfigDescriptor()
2793         {
2794             LOG.warn(__unimplmented);
2795             return null;
2796         }
2797 
2798         @Override
2799         public void declareRoles(String... roleNames)
2800         {
2801             LOG.warn(__unimplmented);
2802         }
2803 
2804         @Override
2805         public String getVirtualServerName()
2806         {
2807             return null;
2808         }
2809     }
2810 
2811 
2812     /* ------------------------------------------------------------ */
2813     /** Interface to check aliases
2814      */
2815     public interface AliasCheck
2816     {
2817         /* ------------------------------------------------------------ */
2818         /** Check an alias
2819          * @param path The path the aliased resource was created for
2820          * @param resource The aliased resourced
2821          * @return True if the resource is OK to be served.
2822          */
2823         boolean check(String path, Resource resource);
2824     }
2825 
2826 
2827     /* ------------------------------------------------------------ */
2828     /** Approve all aliases.
2829      */
2830     public static class ApproveAliases implements AliasCheck
2831     {
2832         @Override
2833         public boolean check(String path, Resource resource)
2834         {
2835             return true;
2836         }
2837     }
2838 
2839     /* ------------------------------------------------------------ */
2840     /** Approve Aliases of a non existent directory.
2841      * If a directory "/foobar/" does not exist, then the resource is
2842      * aliased to "/foobar".  Accept such aliases.
2843      */
2844     public static class ApproveNonExistentDirectoryAliases implements AliasCheck
2845     {
2846         @Override
2847         public boolean check(String path, Resource resource)
2848         {
2849             if (resource.exists())
2850                 return false;
2851 
2852             String a=resource.getAlias().toString();
2853             String r=resource.getURI().toString();
2854 
2855             if (a.length()>r.length())
2856                 return a.startsWith(r) && a.length()==r.length()+1 && a.endsWith("/");
2857             if (a.length()<r.length())
2858                 return r.startsWith(a) && r.length()==a.length()+1 && r.endsWith("/");
2859 
2860             return a.equals(r);
2861         }
2862     }
2863 
2864 
2865     /** Listener for all threads entering context scope, including async IO callbacks
2866      */
2867     public static interface ContextScopeListener extends EventListener
2868     {
2869         /**
2870          * @param context The context being entered
2871          * @param request A request that is applicable to the scope, or null
2872          * @param reason An object that indicates the reason the scope is being entered
2873          */
2874         void enterScope(Context context, Request request, Object reason);
2875 
2876 
2877         /**
2878          * @param context The context being exited
2879          * @param request A request that is applicable to the scope, or null
2880          */
2881         void exitScope(Context context, Request request);
2882     }
2883 }