View Javadoc

1   // ========================================================================
2   // Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses.
12  // ========================================================================
13  
14  package org.eclipse.jetty.server.handler;
15  
16  import java.io.File;
17  import java.io.IOException;
18  import java.io.InputStream;
19  import java.net.MalformedURLException;
20  import java.net.URL;
21  import java.net.URLClassLoader;
22  import java.security.AccessController;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.Collections;
26  import java.util.Enumeration;
27  import java.util.EventListener;
28  import java.util.HashMap;
29  import java.util.HashSet;
30  import java.util.List;
31  import java.util.Locale;
32  import java.util.Map;
33  import java.util.Set;
34  import java.util.EnumSet;
35  
36  import javax.servlet.DispatcherType;
37  import javax.servlet.RequestDispatcher;
38  import javax.servlet.Servlet;
39  import javax.servlet.ServletContext;
40  import javax.servlet.ServletContextAttributeEvent;
41  import javax.servlet.ServletContextAttributeListener;
42  import javax.servlet.ServletContextEvent;
43  import javax.servlet.ServletContextListener;
44  import javax.servlet.ServletException;
45  import javax.servlet.ServletRegistration;
46  import javax.servlet.ServletRequestAttributeListener;
47  import javax.servlet.ServletRequestEvent;
48  import javax.servlet.ServletRequestListener;
49  import javax.servlet.SessionCookieConfig;
50  import javax.servlet.SessionTrackingMode;
51  import javax.servlet.Filter;
52  import javax.servlet.FilterRegistration;
53  import javax.servlet.FilterRegistration.Dynamic;
54  import javax.servlet.descriptor.JspConfigDescriptor;
55  import javax.servlet.http.HttpServletRequest;
56  import javax.servlet.http.HttpServletResponse;
57  
58  import org.eclipse.jetty.http.HttpException;
59  import org.eclipse.jetty.http.MimeTypes;
60  import org.eclipse.jetty.io.Buffer;
61  import org.eclipse.jetty.server.AbstractHttpConnection;
62  import org.eclipse.jetty.server.Dispatcher;
63  import org.eclipse.jetty.server.Handler;
64  import org.eclipse.jetty.server.HandlerContainer;
65  import org.eclipse.jetty.server.Request;
66  import org.eclipse.jetty.server.Server;
67  import org.eclipse.jetty.util.Attributes;
68  import org.eclipse.jetty.util.AttributesMap;
69  import org.eclipse.jetty.util.LazyList;
70  import org.eclipse.jetty.util.Loader;
71  import org.eclipse.jetty.util.TypeUtil;
72  import org.eclipse.jetty.util.URIUtil;
73  import org.eclipse.jetty.util.component.AggregateLifeCycle;
74  import org.eclipse.jetty.util.component.Dumpable;
75  import org.eclipse.jetty.util.log.Log;
76  import org.eclipse.jetty.util.log.Logger;
77  import org.eclipse.jetty.util.resource.Resource;
78  
79  /* ------------------------------------------------------------ */
80  /**
81   * ContextHandler.
82   * 
83   * This handler wraps a call to handle by setting the context and servlet path, plus setting the context classloader.
84   * 
85   * <p>
86   * 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
87   * context attribute names, which if set as attributes are passed to the servers Container so that they may be managed with JMX.
88   * 
89   * @org.apache.xbean.XBean description="Creates a basic HTTP context"
90   */
91  public class ContextHandler extends ScopedHandler implements Attributes, Server.Graceful
92  {
93      private static final Logger LOG = Log.getLogger(ContextHandler.class);
94  
95      private static final ThreadLocal<Context> __context = new ThreadLocal<Context>();
96  
97      /**
98       * 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
99       * 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
100      * for the attribute value.
101      */
102     public static final String MANAGED_ATTRIBUTES = "org.eclipse.jetty.server.context.ManagedAttributes";
103 
104     /* ------------------------------------------------------------ */
105     /**
106      * Get the current ServletContext implementation.
107      * 
108      * @return ServletContext implementation
109      */
110     public static Context getCurrentContext()
111     {
112         return __context.get();
113     }
114 
115     protected Context _scontext;
116 
117     private final AttributesMap _attributes;
118     private final AttributesMap _contextAttributes;
119     private final Map<String, String> _initParams;
120     private ClassLoader _classLoader;
121     private String _contextPath = "/";
122     private String _displayName;
123     private Resource _baseResource;
124     private MimeTypes _mimeTypes;
125     private Map<String, String> _localeEncodingMap;
126     private String[] _welcomeFiles;
127     private ErrorHandler _errorHandler;
128     private String[] _vhosts;
129     private Set<String> _connectors;
130     private EventListener[] _eventListeners;
131     private Logger _logger;
132     private boolean _allowNullPathInfo;
133     private int _maxFormContentSize = Integer.getInteger("org.eclipse.jetty.server.Request.maxFormContentSize",200000).intValue();
134     private boolean _compactPath = false;
135     private boolean _aliases = false;
136 
137     private Object _contextListeners;
138     private Object _contextAttributeListeners;
139     private Object _requestListeners;
140     private Object _requestAttributeListeners;
141     private Map<String, Object> _managedAttributes;
142 
143     private boolean _shutdown = false;
144     private boolean _available = true;
145     private volatile int _availability; // 0=STOPPED, 1=AVAILABLE, 2=SHUTDOWN, 3=UNAVAILABLE
146 
147     private final static int __STOPPED = 0, __AVAILABLE = 1, __SHUTDOWN = 2, __UNAVAILABLE = 3;
148 
149     /* ------------------------------------------------------------ */
150     /**
151      *
152      */
153     public ContextHandler()
154     {
155         super();
156         _scontext = new Context();
157         _attributes = new AttributesMap();
158         _contextAttributes = new AttributesMap();
159         _initParams = new HashMap<String, String>();
160     }
161 
162     /* ------------------------------------------------------------ */
163     /**
164      *
165      */
166     protected ContextHandler(Context context)
167     {
168         super();
169         _scontext = context;
170         _attributes = new AttributesMap();
171         _contextAttributes = new AttributesMap();
172         _initParams = new HashMap<String, String>();
173     }
174 
175     /* ------------------------------------------------------------ */
176     /**
177      *
178      */
179     public ContextHandler(String contextPath)
180     {
181         this();
182         setContextPath(contextPath);
183     }
184 
185     /* ------------------------------------------------------------ */
186     /**
187      *
188      */
189     public ContextHandler(HandlerContainer parent, String contextPath)
190     {
191         this();
192         setContextPath(contextPath);
193         if (parent instanceof HandlerWrapper)
194             ((HandlerWrapper)parent).setHandler(this);
195         else if (parent instanceof HandlerCollection)
196             ((HandlerCollection)parent).addHandler(this);
197     }
198 
199     /* ------------------------------------------------------------ */
200     @Override
201     public void dump(Appendable out, String indent) throws IOException
202     {
203         dumpThis(out);
204         dump(out,indent,Collections.singletonList(new CLDump(getClassLoader())),TypeUtil.asList(getHandlers()),getBeans(),_initParams.entrySet(),
205                 _attributes.getAttributeEntrySet(),_contextAttributes.getAttributeEntrySet());
206     }
207 
208     /* ------------------------------------------------------------ */
209     public Context getServletContext()
210     {
211         return _scontext;
212     }
213 
214     /* ------------------------------------------------------------ */
215     /**
216      * @return the allowNullPathInfo true if /context is not redirected to /context/
217      */
218     public boolean getAllowNullPathInfo()
219     {
220         return _allowNullPathInfo;
221     }
222 
223     /* ------------------------------------------------------------ */
224     /**
225      * @param allowNullPathInfo
226      *            true if /context is not redirected to /context/
227      */
228     public void setAllowNullPathInfo(boolean allowNullPathInfo)
229     {
230         _allowNullPathInfo = allowNullPathInfo;
231     }
232 
233     /* ------------------------------------------------------------ */
234     @Override
235     public void setServer(Server server)
236     {
237         if (_errorHandler != null)
238         {
239             Server old_server = getServer();
240             if (old_server != null && old_server != server)
241                 old_server.getContainer().update(this,_errorHandler,null,"error",true);
242             super.setServer(server);
243             if (server != null && server != old_server)
244                 server.getContainer().update(this,null,_errorHandler,"error",true);
245             _errorHandler.setServer(server);
246         }
247         else
248             super.setServer(server);
249     }
250 
251     /* ------------------------------------------------------------ */
252     /**
253      * 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
254      * 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
255      * matching virtual host name.
256      * 
257      * @param vhosts
258      *            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
259      *            String representation of IP addresses. Host names may start with '*.' to wildcard one level of names.
260      */
261     public void setVirtualHosts(String[] vhosts)
262     {
263         if (vhosts == null)
264         {
265             _vhosts = vhosts;
266         }
267         else
268         {
269             _vhosts = new String[vhosts.length];
270             for (int i = 0; i < vhosts.length; i++)
271                 _vhosts[i] = normalizeHostname(vhosts[i]);
272         }
273     }
274 
275     /* ------------------------------------------------------------ */
276     /** Either set virtual hosts or add to an existing set of virtual hosts.
277      *  
278      * @param virtualHosts
279      *            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
280      *            String representation of IP addresses. Host names may start with '*.' to wildcard one level of names.
281      */
282     public void addVirtualHosts(String[] virtualHosts)
283     {
284         if (virtualHosts == null)  // since this is add, we don't null the old ones
285         {
286             return;
287         }
288         else
289         {
290             List<String> currentVirtualHosts = null;
291             if (_vhosts != null)
292             {
293                 currentVirtualHosts = new ArrayList<String>(Arrays.asList(_vhosts));
294             }
295             else
296             {
297                 currentVirtualHosts = new ArrayList<String>();
298             }
299             
300             for (int i = 0; i < virtualHosts.length; i++)
301             {
302                 String normVhost = normalizeHostname(virtualHosts[i]);
303                 if (!currentVirtualHosts.contains(normVhost))
304                 {
305                     currentVirtualHosts.add(normVhost);
306                 }
307             }
308             _vhosts = currentVirtualHosts.toArray(new String[0]);
309         }
310     }
311 
312     /* ------------------------------------------------------------ */
313     /**
314      * Removes an array of virtual host entries, if this removes all entries the _vhosts will be set to null
315      * 
316      *  @param virtualHosts
317      *            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
318      *            String representation of IP addresses. Host names may start with '*.' to wildcard one level of names.
319      */
320     public void removeVirtualHosts(String[] virtualHosts)
321     {
322         if (virtualHosts == null)
323         {
324             return; // do nothing
325         }
326         else if ( _vhosts == null || _vhosts.length == 0)
327         {
328             return; // do nothing
329         }
330         else
331         {
332             List<String> existingVirtualHosts = new ArrayList<String>(Arrays.asList(_vhosts));
333             
334             for (int i = 0; i < virtualHosts.length; i++)
335             {
336                 String toRemoveVirtualHost = normalizeHostname(virtualHosts[i]);
337                 if (existingVirtualHosts.contains(toRemoveVirtualHost))
338                 {
339                     existingVirtualHosts.remove(toRemoveVirtualHost);
340                 }
341             }
342             
343             if (existingVirtualHosts.isEmpty())
344             {
345                 _vhosts = null; // if we ended up removing them all, just null out _vhosts
346             }
347             else
348             {
349                 _vhosts = existingVirtualHosts.toArray(new String[0]);
350             }
351         }
352     }
353     
354     /* ------------------------------------------------------------ */
355     /**
356      * 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
357      * 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
358      * matching virtual host name.
359      * 
360      * @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
361      *         representation of IP addresses. Host names may start with '*.' to wildcard one level of names.
362      */
363     public String[] getVirtualHosts()
364     {
365         return _vhosts;
366     }
367 
368     /* ------------------------------------------------------------ */
369     /**
370      * @return an array of connector names that this context will accept a request from.
371      */
372     public String[] getConnectorNames()
373     {
374         if (_connectors == null || _connectors.size() == 0)
375             return null;
376 
377         return _connectors.toArray(new String[_connectors.size()]);
378     }
379 
380     /* ------------------------------------------------------------ */
381     /**
382      * Set the names of accepted connectors.
383      * 
384      * Names are either "host:port" or a specific configured name for a connector.
385      * 
386      * @param connectors
387      *            If non null, an array of connector names that this context will accept a request from.
388      */
389     public void setConnectorNames(String[] connectors)
390     {
391         if (connectors == null || connectors.length == 0)
392             _connectors = null;
393         else
394             _connectors = new HashSet<String>(Arrays.asList(connectors));
395     }
396 
397     /* ------------------------------------------------------------ */
398     /*
399      * @see javax.servlet.ServletContext#getAttribute(java.lang.String)
400      */
401     public Object getAttribute(String name)
402     {
403         return _attributes.getAttribute(name);
404     }
405 
406     /* ------------------------------------------------------------ */
407     /*
408      * @see javax.servlet.ServletContext#getAttributeNames()
409      */
410     @SuppressWarnings("unchecked")
411     public Enumeration getAttributeNames()
412     {
413         return AttributesMap.getAttributeNamesCopy(_attributes);
414     }
415 
416     /* ------------------------------------------------------------ */
417     /**
418      * @return Returns the attributes.
419      */
420     public Attributes getAttributes()
421     {
422         return _attributes;
423     }
424 
425     /* ------------------------------------------------------------ */
426     /**
427      * @return Returns the classLoader.
428      */
429     public ClassLoader getClassLoader()
430     {
431         return _classLoader;
432     }
433 
434     /* ------------------------------------------------------------ */
435     /**
436      * Make best effort to extract a file classpath from the context classloader
437      * 
438      * @return Returns the classLoader.
439      */
440     public String getClassPath()
441     {
442         if (_classLoader == null || !(_classLoader instanceof URLClassLoader))
443             return null;
444         URLClassLoader loader = (URLClassLoader)_classLoader;
445         URL[] urls = loader.getURLs();
446         StringBuilder classpath = new StringBuilder();
447         for (int i = 0; i < urls.length; i++)
448         {
449             try
450             {
451                 Resource resource = newResource(urls[i]);
452                 File file = resource.getFile();
453                 if (file != null && file.exists())
454                 {
455                     if (classpath.length() > 0)
456                         classpath.append(File.pathSeparatorChar);
457                     classpath.append(file.getAbsolutePath());
458                 }
459             }
460             catch (IOException e)
461             {
462                 LOG.debug(e);
463             }
464         }
465         if (classpath.length() == 0)
466             return null;
467         return classpath.toString();
468     }
469 
470     /* ------------------------------------------------------------ */
471     /**
472      * @return Returns the _contextPath.
473      */
474     public String getContextPath()
475     {
476         return _contextPath;
477     }
478 
479     /* ------------------------------------------------------------ */
480     /*
481      * @see javax.servlet.ServletContext#getInitParameter(java.lang.String)
482      */
483     public String getInitParameter(String name)
484     {
485         return _initParams.get(name);
486     }
487 
488     /* ------------------------------------------------------------ */
489     /*
490      */
491     public String setInitParameter(String name, String value)
492     {
493         return _initParams.put(name,value);
494     }
495 
496     /* ------------------------------------------------------------ */
497     /*
498      * @see javax.servlet.ServletContext#getInitParameterNames()
499      */
500     @SuppressWarnings("rawtypes")
501     public Enumeration getInitParameterNames()
502     {
503         return Collections.enumeration(_initParams.keySet());
504     }
505 
506     /* ------------------------------------------------------------ */
507     /**
508      * @return Returns the initParams.
509      */
510     public Map<String, String> getInitParams()
511     {
512         return _initParams;
513     }
514 
515     /* ------------------------------------------------------------ */
516     /*
517      * @see javax.servlet.ServletContext#getServletContextName()
518      */
519     public String getDisplayName()
520     {
521         return _displayName;
522     }
523 
524     /* ------------------------------------------------------------ */
525     public EventListener[] getEventListeners()
526     {
527         return _eventListeners;
528     }
529 
530     /* ------------------------------------------------------------ */
531     /**
532      * Set the context event listeners.
533      * 
534      * @param eventListeners
535      *            the event listeners
536      * @see ServletContextListener
537      * @see ServletContextAttributeListener
538      * @see ServletRequestListener
539      * @see ServletRequestAttributeListener
540      */
541     public void setEventListeners(EventListener[] eventListeners)
542     {
543         _contextListeners = null;
544         _contextAttributeListeners = null;
545         _requestListeners = null;
546         _requestAttributeListeners = null;
547 
548         _eventListeners = eventListeners;
549 
550         for (int i = 0; eventListeners != null && i < eventListeners.length; i++)
551         {
552             EventListener listener = _eventListeners[i];
553 
554             if (listener instanceof ServletContextListener)
555                 _contextListeners = LazyList.add(_contextListeners,listener);
556 
557             if (listener instanceof ServletContextAttributeListener)
558                 _contextAttributeListeners = LazyList.add(_contextAttributeListeners,listener);
559 
560             if (listener instanceof ServletRequestListener)
561                 _requestListeners = LazyList.add(_requestListeners,listener);
562 
563             if (listener instanceof ServletRequestAttributeListener)
564                 _requestAttributeListeners = LazyList.add(_requestAttributeListeners,listener);
565         }
566     }
567 
568     /* ------------------------------------------------------------ */
569     /**
570      * Add a context event listeners.
571      * 
572      * @see ServletContextListener
573      * @see ServletContextAttributeListener
574      * @see ServletRequestListener
575      * @see ServletRequestAttributeListener
576      */
577     public void addEventListener(EventListener listener)
578     {
579         setEventListeners((EventListener[])LazyList.addToArray(getEventListeners(),listener,EventListener.class));
580     }
581     
582    
583     /**
584      * Apply any necessary restrictions on a programmatically added
585      * listener.
586      * 
587      * Superclasses should implement.
588      * 
589      * @param listener
590      */
591     public void restrictEventListener (EventListener listener)
592     {
593     }
594 
595     /* ------------------------------------------------------------ */
596     /**
597      * @return true if this context is accepting new requests
598      */
599     public boolean isShutdown()
600     {
601         synchronized (this)
602         {
603             return !_shutdown;
604         }
605     }
606 
607     /* ------------------------------------------------------------ */
608     /**
609      * 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
610      * requests can complete, but no new requests are accepted.
611      * 
612      * @param shutdown
613      *            true if this context is (not?) accepting new requests
614      */
615     public void setShutdown(boolean shutdown)
616     {
617         synchronized (this)
618         {
619             _shutdown = shutdown;
620             _availability = isRunning()?(_shutdown?__SHUTDOWN:_available?__AVAILABLE:__UNAVAILABLE):__STOPPED;
621         }
622     }
623 
624     /* ------------------------------------------------------------ */
625     /**
626      * @return false if this context is unavailable (sends 503)
627      */
628     public boolean isAvailable()
629     {
630         synchronized (this)
631         {
632             return _available;
633         }
634     }
635 
636     /* ------------------------------------------------------------ */
637     /**
638      * Set Available status.
639      */
640     public void setAvailable(boolean available)
641     {
642         synchronized (this)
643         {
644             _available = available;
645             _availability = isRunning()?(_shutdown?__SHUTDOWN:_available?__AVAILABLE:__UNAVAILABLE):__STOPPED;
646         }
647     }
648 
649     /* ------------------------------------------------------------ */
650     public Logger getLogger()
651     {
652         return _logger;
653     }
654 
655     /* ------------------------------------------------------------ */
656     public void setLogger(Logger logger)
657     {
658         _logger = logger;
659     }
660 
661     /* ------------------------------------------------------------ */
662     /*
663      * @see org.eclipse.thread.AbstractLifeCycle#doStart()
664      */
665     @Override
666     protected void doStart() throws Exception
667     {
668         _availability = __STOPPED;
669 
670         if (_contextPath == null)
671             throw new IllegalStateException("Null contextPath");
672 
673         _logger = Log.getLogger(getDisplayName() == null?getContextPath():getDisplayName());
674         ClassLoader old_classloader = null;
675         Thread current_thread = null;
676         Context old_context = null;
677 
678         try
679         {
680             // Set the classloader
681             if (_classLoader != null)
682             {
683                 current_thread = Thread.currentThread();
684                 old_classloader = current_thread.getContextClassLoader();
685                 current_thread.setContextClassLoader(_classLoader);
686             }
687 
688             if (_mimeTypes == null)
689                 _mimeTypes = new MimeTypes();
690 
691             old_context = __context.get();
692             __context.set(_scontext);
693 
694             // defers the calling of super.doStart()
695             startContext();
696 
697             synchronized(this)
698             {
699                 _availability = _shutdown?__SHUTDOWN:_available?__AVAILABLE:__UNAVAILABLE;
700             }
701         }
702         finally
703         {
704             __context.set(old_context);
705 
706             // reset the classloader
707             if (_classLoader != null)
708             {
709                 current_thread.setContextClassLoader(old_classloader);
710             }
711 
712         }
713     }
714 
715     /* ------------------------------------------------------------ */
716     /**
717      * Extensible startContext. this method is called from {@link ContextHandler#doStart()} instead of a call to super.doStart(). This allows derived classes to
718      * insert additional handling (Eg configuration) before the call to super.doStart by this method will start contained handlers.
719      * 
720      * @see org.eclipse.jetty.server.handler.ContextHandler.Context
721      */
722     protected void startContext() throws Exception
723     {
724         String managedAttributes = _initParams.get(MANAGED_ATTRIBUTES);
725         if (managedAttributes != null)
726         {
727             _managedAttributes = new HashMap<String, Object>();
728             String[] attributes = managedAttributes.split(",");
729             for (String attribute : attributes)
730                 _managedAttributes.put(attribute,null);
731 
732             Enumeration e = _scontext.getAttributeNames();
733             while (e.hasMoreElements())
734             {
735                 String name = (String)e.nextElement();
736                 Object value = _scontext.getAttribute(name);
737                 checkManagedAttribute(name,value);
738             }
739         }
740 
741         super.doStart();
742 
743         if (_errorHandler != null)
744             _errorHandler.start();
745 
746         // Context listeners
747         if (_contextListeners != null)
748         {
749             ServletContextEvent event = new ServletContextEvent(_scontext);
750             for (int i = 0; i < LazyList.size(_contextListeners); i++)
751             {
752                 callContextInitialized(((ServletContextListener)LazyList.get(_contextListeners, i)), event);
753             }
754         }
755     }
756 
757     /* ------------------------------------------------------------ */
758     public void callContextInitialized (ServletContextListener l, ServletContextEvent e)
759     {
760         l.contextInitialized(e);
761         LOG.info("started {}",this);
762     }
763 
764     /* ------------------------------------------------------------ */
765     public void callContextDestroyed (ServletContextListener l, ServletContextEvent e)
766     {
767         l.contextDestroyed(e);
768     }
769     
770     /* ------------------------------------------------------------ */
771     /*
772      * @see org.eclipse.thread.AbstractLifeCycle#doStop()
773      */
774     @Override
775     protected void doStop() throws Exception
776     {
777         _availability = __STOPPED;
778 
779         ClassLoader old_classloader = null;
780         Thread current_thread = null;
781 
782         Context old_context = __context.get();
783         __context.set(_scontext);
784         try
785         {
786             // Set the classloader
787             if (_classLoader != null)
788             {
789                 current_thread = Thread.currentThread();
790                 old_classloader = current_thread.getContextClassLoader();
791                 current_thread.setContextClassLoader(_classLoader);
792             }
793 
794             super.doStop();
795 
796             // Context listeners
797             if (_contextListeners != null)
798             {
799                 ServletContextEvent event = new ServletContextEvent(_scontext);
800                 for (int i = LazyList.size(_contextListeners); i-- > 0;)
801                 {
802                     ((ServletContextListener)LazyList.get(_contextListeners,i)).contextDestroyed(event);
803                 }
804             }
805 
806             if (_errorHandler != null)
807                 _errorHandler.stop();
808 
809             Enumeration e = _scontext.getAttributeNames();
810             while (e.hasMoreElements())
811             {
812                 String name = (String)e.nextElement();
813                 checkManagedAttribute(name,null);
814             }
815         }
816         finally
817         {
818             LOG.info("stopped {}",this);
819             __context.set(old_context);
820             // reset the classloader
821             if (_classLoader != null)
822                 current_thread.setContextClassLoader(old_classloader);
823         }
824 
825         _contextAttributes.clearAttributes();
826     }
827 
828     /* ------------------------------------------------------------ */
829     /*
830      * @see org.eclipse.jetty.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
831      */
832     public boolean checkContext(final String target, final Request baseRequest, final HttpServletResponse response) throws IOException, ServletException
833     {
834         DispatcherType dispatch = baseRequest.getDispatcherType();
835 
836         switch (_availability)
837         {
838             case __STOPPED:
839             case __SHUTDOWN:
840                 return false;
841             case __UNAVAILABLE:
842                 baseRequest.setHandled(true);
843                 response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
844                 return false;
845             default:
846                 if ((DispatcherType.REQUEST.equals(dispatch) && baseRequest.isHandled()))
847                     return false;
848         }
849 
850         // Check the vhosts
851         if (_vhosts != null && _vhosts.length > 0)
852         {
853             String vhost = normalizeHostname(baseRequest.getServerName());
854 
855             boolean match = false;
856 
857             // TODO non-linear lookup
858             for (int i = 0; !match && i < _vhosts.length; i++)
859             {
860                 String contextVhost = _vhosts[i];
861                 if (contextVhost == null)
862                     continue;
863                 if (contextVhost.startsWith("*."))
864                 {
865                     // wildcard only at the beginning, and only for one additional subdomain level
866                     match = contextVhost.regionMatches(true,2,vhost,vhost.indexOf(".") + 1,contextVhost.length() - 2);
867                 }
868                 else
869                     match = contextVhost.equalsIgnoreCase(vhost);
870             }
871             if (!match)
872                 return false;
873         }
874 
875         // Check the connector
876         if (_connectors != null && _connectors.size() > 0)
877         {
878             String connector = AbstractHttpConnection.getCurrentConnection().getConnector().getName();
879             if (connector == null || !_connectors.contains(connector))
880                 return false;
881         }
882 
883         // Are we not the root context?
884         if (_contextPath.length() > 1)
885         {
886             // reject requests that are not for us
887             if (!target.startsWith(_contextPath))
888                 return false;
889             if (target.length() > _contextPath.length() && target.charAt(_contextPath.length()) != '/')
890                 return false;
891 
892             // redirect null path infos
893             if (!_allowNullPathInfo && _contextPath.length() == target.length())
894             {
895                 // context request must end with /
896                 baseRequest.setHandled(true);
897                 if (baseRequest.getQueryString() != null)
898                     response.sendRedirect(URIUtil.addPaths(baseRequest.getRequestURI(),URIUtil.SLASH) + "?" + baseRequest.getQueryString());
899                 else
900                     response.sendRedirect(URIUtil.addPaths(baseRequest.getRequestURI(),URIUtil.SLASH));
901                 return false;
902             }
903         }
904 
905         return true;
906     }
907 
908     /* ------------------------------------------------------------ */
909     /**
910      * @see org.eclipse.jetty.server.handler.ScopedHandler#doScope(java.lang.String, org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest,
911      *      javax.servlet.http.HttpServletResponse)
912      */
913     @Override
914     public void doScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
915     {
916         if (LOG.isDebugEnabled())
917             LOG.debug("scope {}|{}|{} @ {}",baseRequest.getContextPath(),baseRequest.getServletPath(),baseRequest.getPathInfo(),this);
918 
919         Context old_context = null;
920         String old_context_path = null;
921         String old_servlet_path = null;
922         String old_path_info = null;
923         ClassLoader old_classloader = null;
924         Thread current_thread = null;
925         String pathInfo = target;
926 
927         DispatcherType dispatch = baseRequest.getDispatcherType();
928 
929         old_context = baseRequest.getContext();
930 
931         // Are we already in this context?
932         if (old_context != _scontext)
933         {
934             // check the target.
935             if (DispatcherType.REQUEST.equals(dispatch) || DispatcherType.ASYNC.equals(dispatch))
936             {
937                 if (_compactPath)
938                     target = URIUtil.compactPath(target);
939                 if (!checkContext(target,baseRequest,response))
940                     return;
941 
942                 if (target.length() > _contextPath.length())
943                 {
944                     if (_contextPath.length() > 1)
945                         target = target.substring(_contextPath.length());
946                     pathInfo = target;
947                 }
948                 else if (_contextPath.length() == 1)
949                 {
950                     target = URIUtil.SLASH;
951                     pathInfo = URIUtil.SLASH;
952                 }
953                 else
954                 {
955                     target = URIUtil.SLASH;
956                     pathInfo = null;
957                 }
958             }
959 
960             // Set the classloader
961             if (_classLoader != null)
962             {
963                 current_thread = Thread.currentThread();
964                 old_classloader = current_thread.getContextClassLoader();
965                 current_thread.setContextClassLoader(_classLoader);
966             }
967         }
968 
969         try
970         {
971             old_context_path = baseRequest.getContextPath();
972             old_servlet_path = baseRequest.getServletPath();
973             old_path_info = baseRequest.getPathInfo();
974 
975             // Update the paths
976             baseRequest.setContext(_scontext);
977             __context.set(_scontext);
978             if (!DispatcherType.INCLUDE.equals(dispatch) && target.startsWith("/"))
979             {
980                 if (_contextPath.length() == 1)
981                     baseRequest.setContextPath("");
982                 else
983                     baseRequest.setContextPath(_contextPath);
984                 baseRequest.setServletPath(null);
985                 baseRequest.setPathInfo(pathInfo);
986             }
987 
988             if (LOG.isDebugEnabled())
989                 LOG.debug("context={}|{}|{} @ {}",baseRequest.getContextPath(),baseRequest.getServletPath(), baseRequest.getPathInfo(),this);
990 
991             // start manual inline of nextScope(target,baseRequest,request,response);
992             if (never())
993                 nextScope(target,baseRequest,request,response);
994             else if (_nextScope != null)
995                 _nextScope.doScope(target,baseRequest,request,response);
996             else if (_outerScope != null)
997                 _outerScope.doHandle(target,baseRequest,request,response);
998             else
999                 doHandle(target,baseRequest,request,response);
1000             // end manual inline (pathentic attempt to reduce stack depth)
1001         }
1002         finally
1003         {
1004             if (old_context != _scontext)
1005             {
1006                 // reset the classloader
1007                 if (_classLoader != null)
1008                 {
1009                     current_thread.setContextClassLoader(old_classloader);
1010                 }
1011 
1012                 // reset the context and servlet path.
1013                 baseRequest.setContext(old_context);
1014                 __context.set(old_context);
1015                 baseRequest.setContextPath(old_context_path);
1016                 baseRequest.setServletPath(old_servlet_path);
1017                 baseRequest.setPathInfo(old_path_info);
1018             }
1019         }
1020     }
1021 
1022     /* ------------------------------------------------------------ */
1023     /**
1024      * @see org.eclipse.jetty.server.handler.ScopedHandler#doHandle(java.lang.String, org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest,
1025      *      javax.servlet.http.HttpServletResponse)
1026      */
1027     @Override
1028     public void doHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
1029     {
1030         final DispatcherType dispatch = baseRequest.getDispatcherType();
1031         final boolean new_context = baseRequest.takeNewContext();
1032         try
1033         {
1034             if (new_context)
1035             {
1036                 // Handle the REALLY SILLY request events!
1037                 if (_requestAttributeListeners != null)
1038                 {
1039                     final int s = LazyList.size(_requestAttributeListeners);
1040                     for (int i = 0; i < s; i++)
1041                         baseRequest.addEventListener(((EventListener)LazyList.get(_requestAttributeListeners,i)));
1042                 }
1043 
1044                 if (_requestListeners != null)
1045                 {
1046                     final int s = LazyList.size(_requestListeners);
1047                     final ServletRequestEvent sre = new ServletRequestEvent(_scontext,request);
1048                     for (int i = 0; i < s; i++)
1049                         ((ServletRequestListener)LazyList.get(_requestListeners,i)).requestInitialized(sre);
1050                 }
1051             }
1052 
1053             if (DispatcherType.REQUEST.equals(dispatch) && isProtectedTarget(target))
1054                 throw new HttpException(HttpServletResponse.SC_NOT_FOUND);
1055 
1056             // start manual inline of nextHandle(target,baseRequest,request,response);
1057             // noinspection ConstantIfStatement
1058             if (never())
1059                 nextHandle(target,baseRequest,request,response);
1060             else if (_nextScope != null && _nextScope == _handler)
1061                 _nextScope.doHandle(target,baseRequest,request,response);
1062             else if (_handler != null)
1063                 _handler.handle(target,baseRequest,request,response);
1064             // end manual inline
1065         }
1066         catch (HttpException e)
1067         {
1068             LOG.debug(e);
1069             baseRequest.setHandled(true);
1070             response.sendError(e.getStatus(),e.getReason());
1071         }
1072         finally
1073         {
1074             // Handle more REALLY SILLY request events!
1075             if (new_context)
1076             {
1077                 if (_requestListeners != null)
1078                 {
1079                     final ServletRequestEvent sre = new ServletRequestEvent(_scontext,request);
1080                     for (int i = LazyList.size(_requestListeners); i-- > 0;)
1081                         ((ServletRequestListener)LazyList.get(_requestListeners,i)).requestDestroyed(sre);
1082                 }
1083 
1084                 if (_requestAttributeListeners != null)
1085                 {
1086                     for (int i = LazyList.size(_requestAttributeListeners); i-- > 0;)
1087                         baseRequest.removeEventListener(((EventListener)LazyList.get(_requestAttributeListeners,i)));
1088                 }
1089             }
1090         }
1091     }
1092 
1093     /* ------------------------------------------------------------ */
1094     /*
1095      * Handle a runnable in this context
1096      */
1097     public void handle(Runnable runnable)
1098     {
1099         ClassLoader old_classloader = null;
1100         Thread current_thread = null;
1101         Context old_context = null;
1102         try
1103         {
1104             old_context = __context.get();
1105             __context.set(_scontext);
1106 
1107             // Set the classloader
1108             if (_classLoader != null)
1109             {
1110                 current_thread = Thread.currentThread();
1111                 old_classloader = current_thread.getContextClassLoader();
1112                 current_thread.setContextClassLoader(_classLoader);
1113             }
1114 
1115             runnable.run();
1116         }
1117         finally
1118         {
1119             __context.set(old_context);
1120             if (old_classloader != null)
1121             {
1122                 current_thread.setContextClassLoader(old_classloader);
1123             }
1124         }
1125     }
1126 
1127     /* ------------------------------------------------------------ */
1128     /**
1129      * Check the target. Called by {@link #handle(String, Request, HttpServletRequest, HttpServletResponse)} when a target within a context is determined. If
1130      * the target is protected, 404 is returned. The default implementation always returns false.
1131      */
1132     /* ------------------------------------------------------------ */
1133     protected boolean isProtectedTarget(String target)
1134     {
1135         return false;
1136     }
1137 
1138     /* ------------------------------------------------------------ */
1139     /*
1140      * @see javax.servlet.ServletContext#removeAttribute(java.lang.String)
1141      */
1142     public void removeAttribute(String name)
1143     {
1144         checkManagedAttribute(name,null);
1145         _attributes.removeAttribute(name);
1146     }
1147 
1148     /* ------------------------------------------------------------ */
1149     /*
1150      * Set a context attribute. Attributes set via this API cannot be overriden by the ServletContext.setAttribute API. Their lifecycle spans the stop/start of
1151      * a context. No attribute listener events are triggered by this API.
1152      * 
1153      * @see javax.servlet.ServletContext#setAttribute(java.lang.String, java.lang.Object)
1154      */
1155     public void setAttribute(String name, Object value)
1156     {
1157         checkManagedAttribute(name,value);
1158         _attributes.setAttribute(name,value);
1159     }
1160 
1161     /* ------------------------------------------------------------ */
1162     /**
1163      * @param attributes
1164      *            The attributes to set.
1165      */
1166     public void setAttributes(Attributes attributes)
1167     {
1168         _attributes.clearAttributes();
1169         _attributes.addAll(attributes);
1170         Enumeration e = _attributes.getAttributeNames();
1171         while (e.hasMoreElements())
1172         {
1173             String name = (String)e.nextElement();
1174             checkManagedAttribute(name,attributes.getAttribute(name));
1175         }
1176     }
1177 
1178     /* ------------------------------------------------------------ */
1179     public void clearAttributes()
1180     {
1181         Enumeration e = _attributes.getAttributeNames();
1182         while (e.hasMoreElements())
1183         {
1184             String name = (String)e.nextElement();
1185             checkManagedAttribute(name,null);
1186         }
1187         _attributes.clearAttributes();
1188     }
1189 
1190     /* ------------------------------------------------------------ */
1191     public void checkManagedAttribute(String name, Object value)
1192     {
1193         if (_managedAttributes != null && _managedAttributes.containsKey(name))
1194         {
1195             setManagedAttribute(name,value);
1196         }
1197     }
1198 
1199     /* ------------------------------------------------------------ */
1200     public void setManagedAttribute(String name, Object value)
1201     {
1202         Object old = _managedAttributes.put(name,value);
1203         getServer().getContainer().update(this,old,value,name,true);
1204     }
1205 
1206     /* ------------------------------------------------------------ */
1207     /**
1208      * @param classLoader
1209      *            The classLoader to set.
1210      */
1211     public void setClassLoader(ClassLoader classLoader)
1212     {
1213         _classLoader = classLoader;
1214     }
1215 
1216     /* ------------------------------------------------------------ */
1217     /**
1218      * @param contextPath
1219      *            The _contextPath to set.
1220      */
1221     public void setContextPath(String contextPath)
1222     {
1223         if (contextPath != null && contextPath.length() > 1 && contextPath.endsWith("/"))
1224             throw new IllegalArgumentException("ends with /");
1225         _contextPath = contextPath;
1226 
1227         if (getServer() != null && (getServer().isStarting() || getServer().isStarted()))
1228         {
1229             Handler[] contextCollections = getServer().getChildHandlersByClass(ContextHandlerCollection.class);
1230             for (int h = 0; contextCollections != null && h < contextCollections.length; h++)
1231                 ((ContextHandlerCollection)contextCollections[h]).mapContexts();
1232         }
1233     }
1234 
1235     /* ------------------------------------------------------------ */
1236     /**
1237      * @param servletContextName
1238      *            The servletContextName to set.
1239      */
1240     public void setDisplayName(String servletContextName)
1241     {
1242         _displayName = servletContextName;
1243     }
1244 
1245     /* ------------------------------------------------------------ */
1246     /**
1247      * @return Returns the resourceBase.
1248      */
1249     public Resource getBaseResource()
1250     {
1251         if (_baseResource == null)
1252             return null;
1253         return _baseResource;
1254     }
1255 
1256     /* ------------------------------------------------------------ */
1257     /**
1258      * @return Returns the base resource as a string.
1259      */
1260     public String getResourceBase()
1261     {
1262         if (_baseResource == null)
1263             return null;
1264         return _baseResource.toString();
1265     }
1266 
1267     /* ------------------------------------------------------------ */
1268     /**
1269      * @param base
1270      *            The resourceBase to set.
1271      */
1272     public void setBaseResource(Resource base)
1273     {
1274         _baseResource = base;
1275     }
1276 
1277     /* ------------------------------------------------------------ */
1278     /**
1279      * @param resourceBase
1280      *            The base resource as a string.
1281      */
1282     public void setResourceBase(String resourceBase)
1283     {
1284         try
1285         {
1286             setBaseResource(newResource(resourceBase));
1287         }
1288         catch (Exception e)
1289         {
1290             LOG.warn(e.toString());
1291             LOG.debug(e);
1292             throw new IllegalArgumentException(resourceBase);
1293         }
1294     }
1295 
1296     /* ------------------------------------------------------------ */
1297     /**
1298      * @return True if aliases are allowed
1299      */
1300     public boolean isAliases()
1301     {
1302         return _aliases;
1303     }
1304 
1305     /* ------------------------------------------------------------ */
1306     /**
1307      * @param aliases
1308      *            aliases are allowed
1309      */
1310     public void setAliases(boolean aliases)
1311     {
1312         _aliases = aliases;
1313     }
1314 
1315     /* ------------------------------------------------------------ */
1316     /**
1317      * @return Returns the mimeTypes.
1318      */
1319     public MimeTypes getMimeTypes()
1320     {
1321         if (_mimeTypes == null)
1322             _mimeTypes = new MimeTypes();
1323         return _mimeTypes;
1324     }
1325 
1326     /* ------------------------------------------------------------ */
1327     /**
1328      * @param mimeTypes
1329      *            The mimeTypes to set.
1330      */
1331     public void setMimeTypes(MimeTypes mimeTypes)
1332     {
1333         _mimeTypes = mimeTypes;
1334     }
1335 
1336     /* ------------------------------------------------------------ */
1337     /**
1338      */
1339     public void setWelcomeFiles(String[] files)
1340     {
1341         _welcomeFiles = files;
1342     }
1343 
1344     /* ------------------------------------------------------------ */
1345     /**
1346      * @return The names of the files which the server should consider to be welcome files in this context.
1347      * @see <a href="http://jcp.org/aboutJava/communityprocess/final/jsr154/index.html">The Servlet Specification</a>
1348      * @see #setWelcomeFiles
1349      */
1350     public String[] getWelcomeFiles()
1351     {
1352         return _welcomeFiles;
1353     }
1354 
1355     /* ------------------------------------------------------------ */
1356     /**
1357      * @return Returns the errorHandler.
1358      */
1359     public ErrorHandler getErrorHandler()
1360     {
1361         return _errorHandler;
1362     }
1363 
1364     /* ------------------------------------------------------------ */
1365     /**
1366      * @param errorHandler
1367      *            The errorHandler to set.
1368      */
1369     public void setErrorHandler(ErrorHandler errorHandler)
1370     {
1371         if (errorHandler != null)
1372             errorHandler.setServer(getServer());
1373         if (getServer() != null)
1374             getServer().getContainer().update(this,_errorHandler,errorHandler,"errorHandler",true);
1375         _errorHandler = errorHandler;
1376     }
1377 
1378     /* ------------------------------------------------------------ */
1379     public int getMaxFormContentSize()
1380     {
1381         return _maxFormContentSize;
1382     }
1383 
1384     /* ------------------------------------------------------------ */
1385     public void setMaxFormContentSize(int maxSize)
1386     {
1387         _maxFormContentSize = maxSize;
1388     }
1389 
1390     /* ------------------------------------------------------------ */
1391     /**
1392      * @return True if URLs are compacted to replace multiple '/'s with a single '/'
1393      */
1394     public boolean isCompactPath()
1395     {
1396         return _compactPath;
1397     }
1398 
1399     /* ------------------------------------------------------------ */
1400     /**
1401      * @param compactPath
1402      *            True if URLs are compacted to replace multiple '/'s with a single '/'
1403      */
1404     public void setCompactPath(boolean compactPath)
1405     {
1406         _compactPath = compactPath;
1407     }
1408 
1409     /* ------------------------------------------------------------ */
1410     @Override
1411     public String toString()
1412     {
1413         String[] vhosts = getVirtualHosts();
1414 
1415         StringBuilder b = new StringBuilder();
1416 
1417         String p = getClass().getPackage().getName();
1418         if (p != null && p.length() > 0)
1419         {
1420             String[] ss = p.split("\\.");
1421             for (String s : ss)
1422                 b.append(s.charAt(0)).append('.');
1423         }
1424 
1425         b.append(getClass().getSimpleName());
1426         b.append('{').append(getContextPath()).append(',').append(getBaseResource());
1427 
1428         if (vhosts != null && vhosts.length > 0)
1429             b.append(',').append(vhosts[0]);
1430         b.append('}');
1431 
1432         return b.toString();
1433     }
1434 
1435     /* ------------------------------------------------------------ */
1436     public synchronized Class<?> loadClass(String className) throws ClassNotFoundException
1437     {
1438         if (className == null)
1439             return null;
1440 
1441         if (_classLoader == null)
1442             return Loader.loadClass(this.getClass(),className);
1443 
1444         return _classLoader.loadClass(className);
1445     }
1446 
1447     /* ------------------------------------------------------------ */
1448     public void addLocaleEncoding(String locale, String encoding)
1449     {
1450         if (_localeEncodingMap == null)
1451             _localeEncodingMap = new HashMap<String, String>();
1452         _localeEncodingMap.put(locale,encoding);
1453     }
1454 
1455     /* ------------------------------------------------------------ */
1456     public String getLocaleEncoding(String locale)
1457     {
1458         if (_localeEncodingMap == null)
1459             return null;
1460         String encoding = _localeEncodingMap.get(locale);
1461         return encoding;
1462     }
1463 
1464     /* ------------------------------------------------------------ */
1465     /**
1466      * 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
1467      * language is looked up.
1468      * 
1469      * @param locale
1470      *            a <code>Locale</code> value
1471      * @return a <code>String</code> representing the character encoding for the locale or null if none found.
1472      */
1473     public String getLocaleEncoding(Locale locale)
1474     {
1475         if (_localeEncodingMap == null)
1476             return null;
1477         String encoding = _localeEncodingMap.get(locale.toString());
1478         if (encoding == null)
1479             encoding = _localeEncodingMap.get(locale.getLanguage());
1480         return encoding;
1481     }
1482 
1483     /* ------------------------------------------------------------ */
1484     /*
1485      */
1486     public Resource getResource(String path) throws MalformedURLException
1487     {
1488         if (path == null || !path.startsWith(URIUtil.SLASH))
1489             throw new MalformedURLException(path);
1490 
1491         if (_baseResource == null)
1492             return null;
1493 
1494         try
1495         {
1496             path = URIUtil.canonicalPath(path);
1497             Resource resource = _baseResource.addPath(path);
1498 
1499             if (!_aliases && resource.getAlias() != null)
1500             {
1501                 if (resource.exists())
1502                     LOG.warn("Aliased resource: " + resource + "~=" + resource.getAlias());
1503                 else if (path.endsWith("/") && resource.getAlias().toString().endsWith(path))
1504                     return resource;
1505                 else if (LOG.isDebugEnabled())
1506                     LOG.debug("Aliased resource: " + resource + "~=" + resource.getAlias());
1507                 return null;
1508             }
1509 
1510             return resource;
1511         }
1512         catch (Exception e)
1513         {
1514             LOG.ignore(e);
1515         }
1516 
1517         return null;
1518     }
1519 
1520     /* ------------------------------------------------------------ */
1521     /**
1522      * Convert URL to Resource wrapper for {@link Resource#newResource(URL)} enables extensions to provide alternate resource implementations.
1523      */
1524     public Resource newResource(URL url) throws IOException
1525     {
1526         return Resource.newResource(url);
1527     }
1528 
1529     /* ------------------------------------------------------------ */
1530     /**
1531      * Convert a URL or path to a Resource. The default implementation is a wrapper for {@link Resource#newResource(String)}.
1532      * 
1533      * @param urlOrPath
1534      *            The URL or path to convert
1535      * @return The Resource for the URL/path
1536      * @throws IOException
1537      *             The Resource could not be created.
1538      */
1539     public Resource newResource(String urlOrPath) throws IOException
1540     {
1541         return Resource.newResource(urlOrPath);
1542     }
1543 
1544     /* ------------------------------------------------------------ */
1545     /*
1546      */
1547     public Set<String> getResourcePaths(String path)
1548     {
1549         try
1550         {
1551             path = URIUtil.canonicalPath(path);
1552             Resource resource = getResource(path);
1553 
1554             if (resource != null && resource.exists())
1555             {
1556                 if (!path.endsWith(URIUtil.SLASH))
1557                     path = path + URIUtil.SLASH;
1558 
1559                 String[] l = resource.list();
1560                 if (l != null)
1561                 {
1562                     HashSet<String> set = new HashSet<String>();
1563                     for (int i = 0; i < l.length; i++)
1564                         set.add(path + l[i]);
1565                     return set;
1566                 }
1567             }
1568         }
1569         catch (Exception e)
1570         {
1571             LOG.ignore(e);
1572         }
1573         return Collections.emptySet();
1574     }
1575 
1576     /* ------------------------------------------------------------ */
1577     private String normalizeHostname(String host)
1578     {
1579         if (host == null)
1580             return null;
1581 
1582         if (host.endsWith("."))
1583             return host.substring(0,host.length() - 1);
1584 
1585         return host;
1586     }
1587 
1588     /* ------------------------------------------------------------ */
1589     /**
1590      * Context.
1591      * <p>
1592      * A partial implementation of {@link javax.servlet.ServletContext}. A complete implementation is provided by the derived {@link ContextHandler}.
1593      * </p>
1594      * 
1595      * 
1596      */
1597     public class Context implements ServletContext
1598     {
1599         protected int _majorVersion = 3;
1600         protected int _minorVersion = 0;
1601         protected boolean _enabled = true; //whether or not the dynamic API is enabled for callers
1602 
1603         /* ------------------------------------------------------------ */
1604         protected Context()
1605         {
1606         }
1607 
1608         /* ------------------------------------------------------------ */
1609         public ContextHandler getContextHandler()
1610         {
1611             // TODO reduce visibility of this method
1612             return ContextHandler.this;
1613         }
1614 
1615         /* ------------------------------------------------------------ */
1616         /*
1617          * @see javax.servlet.ServletContext#getContext(java.lang.String)
1618          */
1619         @Override
1620         public ServletContext getContext(String uripath)
1621         {
1622             List<ContextHandler> contexts = new ArrayList<ContextHandler>();
1623             Handler[] handlers = getServer().getChildHandlersByClass(ContextHandler.class);
1624             String matched_path = null;
1625 
1626             for (Handler handler : handlers)
1627             {
1628                 if (handler == null)
1629                     continue;
1630                 ContextHandler ch = (ContextHandler)handler;
1631                 String context_path = ch.getContextPath();
1632 
1633                 if (uripath.equals(context_path) || (uripath.startsWith(context_path) && uripath.charAt(context_path.length()) == '/')
1634                         || "/".equals(context_path))
1635                 {
1636                     // look first for vhost matching context only
1637                     if (getVirtualHosts() != null && getVirtualHosts().length > 0)
1638                     {
1639                         if (ch.getVirtualHosts() != null && ch.getVirtualHosts().length > 0)
1640                         {
1641                             for (String h1 : getVirtualHosts())
1642                                 for (String h2 : ch.getVirtualHosts())
1643                                     if (h1.equals(h2))
1644                                     {
1645                                         if (matched_path == null || context_path.length() > matched_path.length())
1646                                         {
1647                                             contexts.clear();
1648                                             matched_path = context_path;
1649                                         }
1650 
1651                                         if (matched_path.equals(context_path))
1652                                             contexts.add(ch);
1653                                     }
1654                         }
1655                     }
1656                     else
1657                     {
1658                         if (matched_path == null || context_path.length() > matched_path.length())
1659                         {
1660                             contexts.clear();
1661                             matched_path = context_path;
1662                         }
1663 
1664                         if (matched_path.equals(context_path))
1665                             contexts.add(ch);
1666                     }
1667                 }
1668             }
1669 
1670             if (contexts.size() > 0)
1671                 return contexts.get(0)._scontext;
1672 
1673             // try again ignoring virtual hosts
1674             matched_path = null;
1675             for (Handler handler : handlers)
1676             {
1677                 if (handler == null)
1678                     continue;
1679                 ContextHandler ch = (ContextHandler)handler;
1680                 String context_path = ch.getContextPath();
1681 
1682                 if (uripath.equals(context_path) || (uripath.startsWith(context_path) && uripath.charAt(context_path.length()) == '/')
1683                         || "/".equals(context_path))
1684                 {
1685                     if (matched_path == null || context_path.length() > matched_path.length())
1686                     {
1687                         contexts.clear();
1688                         matched_path = context_path;
1689                     }
1690 
1691                     if (matched_path.equals(context_path))
1692                         contexts.add(ch);
1693                 }
1694             }
1695 
1696             if (contexts.size() > 0)
1697                 return contexts.get(0)._scontext;
1698             return null;
1699         }
1700 
1701         /* ------------------------------------------------------------ */
1702         /*
1703          * @see javax.servlet.ServletContext#getMajorVersion()
1704          */
1705         @Override
1706         public int getMajorVersion()
1707         {
1708             return 3;
1709         }
1710       
1711 
1712         /* ------------------------------------------------------------ */
1713         /*
1714          * @see javax.servlet.ServletContext#getMimeType(java.lang.String)
1715          */
1716         @Override
1717         public String getMimeType(String file)
1718         {
1719             if (_mimeTypes == null)
1720                 return null;
1721             Buffer mime = _mimeTypes.getMimeByExtension(file);
1722             if (mime != null)
1723                 return mime.toString();
1724             return null;
1725         }
1726 
1727         /* ------------------------------------------------------------ */
1728         /*
1729          * @see javax.servlet.ServletContext#getMinorVersion()
1730          */
1731         @Override
1732         public int getMinorVersion()
1733         {
1734             return 0;
1735         }
1736 
1737         /* ------------------------------------------------------------ */
1738         /*
1739          * @see javax.servlet.ServletContext#getNamedDispatcher(java.lang.String)
1740          */
1741         @Override
1742         public RequestDispatcher getNamedDispatcher(String name)
1743         {
1744             return null;
1745         }
1746 
1747         /* ------------------------------------------------------------ */
1748         /*
1749          * @see javax.servlet.ServletContext#getRequestDispatcher(java.lang.String)
1750          */
1751         @Override
1752         public RequestDispatcher getRequestDispatcher(String uriInContext)
1753         {
1754             if (uriInContext == null)
1755                 return null;
1756 
1757             if (!uriInContext.startsWith("/"))
1758                 return null;
1759 
1760             try
1761             {
1762                 String query = null;
1763                 int q = 0;
1764                 if ((q = uriInContext.indexOf('?')) > 0)
1765                 {
1766                     query = uriInContext.substring(q + 1);
1767                     uriInContext = uriInContext.substring(0,q);
1768                 }
1769                 if ((q = uriInContext.indexOf(';')) > 0)
1770                     uriInContext = uriInContext.substring(0,q);
1771 
1772                 String pathInContext = URIUtil.canonicalPath(URIUtil.decodePath(uriInContext));
1773                 String uri = URIUtil.addPaths(getContextPath(),uriInContext);
1774                 ContextHandler context = ContextHandler.this;
1775                 return new Dispatcher(context,uri,pathInContext,query);
1776             }
1777             catch (Exception e)
1778             {
1779                 LOG.ignore(e);
1780             }
1781             return null;
1782         }
1783 
1784         /* ------------------------------------------------------------ */
1785         /*
1786          * @see javax.servlet.ServletContext#getRealPath(java.lang.String)
1787          */
1788         @Override
1789         public String getRealPath(String path)
1790         {
1791             if (path == null)
1792                 return null;
1793             if (path.length() == 0)
1794                 path = URIUtil.SLASH;
1795             else if (path.charAt(0) != '/')
1796                 path = URIUtil.SLASH + path;
1797 
1798             try
1799             {
1800                 Resource resource = ContextHandler.this.getResource(path);
1801                 if (resource != null)
1802                 {
1803                     File file = resource.getFile();
1804                     if (file != null)
1805                         return file.getCanonicalPath();
1806                 }
1807             }
1808             catch (Exception e)
1809             {
1810                 LOG.ignore(e);
1811             }
1812 
1813             return null;
1814         }
1815 
1816         /* ------------------------------------------------------------ */
1817         @Override
1818         public URL getResource(String path) throws MalformedURLException
1819         {
1820             Resource resource = ContextHandler.this.getResource(path);
1821             if (resource != null && resource.exists())
1822                 return resource.getURL();
1823             return null;
1824         }
1825 
1826         /* ------------------------------------------------------------ */
1827         /*
1828          * @see javax.servlet.ServletContext#getResourceAsStream(java.lang.String)
1829          */
1830         @Override
1831         public InputStream getResourceAsStream(String path)
1832         {
1833             try
1834             {
1835                 URL url = getResource(path);
1836                 if (url == null)
1837                     return null;
1838                 return url.openStream();
1839             }
1840             catch (Exception e)
1841             {
1842                 LOG.ignore(e);
1843                 return null;
1844             }
1845         }
1846 
1847         /* ------------------------------------------------------------ */
1848         /*
1849          * @see javax.servlet.ServletContext#getResourcePaths(java.lang.String)
1850          */
1851         @Override
1852         public Set getResourcePaths(String path)
1853         {
1854             return ContextHandler.this.getResourcePaths(path);
1855         }
1856 
1857         /* ------------------------------------------------------------ */
1858         /*
1859          * @see javax.servlet.ServletContext#getServerInfo()
1860          */
1861         @Override
1862         public String getServerInfo()
1863         {
1864             return "jetty/" + Server.getVersion();
1865         }
1866 
1867         /* ------------------------------------------------------------ */
1868         /*
1869          * @see javax.servlet.ServletContext#getServlet(java.lang.String)
1870          */
1871         @Override
1872         @Deprecated
1873         public Servlet getServlet(String name) throws ServletException
1874         {
1875             return null;
1876         }
1877 
1878         /* ------------------------------------------------------------ */
1879         /*
1880          * @see javax.servlet.ServletContext#getServletNames()
1881          */
1882         @SuppressWarnings("unchecked")
1883         @Override
1884         @Deprecated
1885         public Enumeration getServletNames()
1886         {
1887             return Collections.enumeration(Collections.EMPTY_LIST);
1888         }
1889 
1890         /* ------------------------------------------------------------ */
1891         /*
1892          * @see javax.servlet.ServletContext#getServlets()
1893          */
1894         @SuppressWarnings("unchecked")
1895         @Override
1896         @Deprecated
1897         public Enumeration getServlets()
1898         {
1899             return Collections.enumeration(Collections.EMPTY_LIST);
1900         }
1901 
1902         /* ------------------------------------------------------------ */
1903         /*
1904          * @see javax.servlet.ServletContext#log(java.lang.Exception, java.lang.String)
1905          */
1906         @Override
1907         public void log(Exception exception, String msg)
1908         {
1909             _logger.warn(msg,exception);
1910         }
1911 
1912         /* ------------------------------------------------------------ */
1913         /*
1914          * @see javax.servlet.ServletContext#log(java.lang.String)
1915          */
1916         @Override
1917         public void log(String msg)
1918         {
1919             _logger.info(msg);
1920         }
1921 
1922         /* ------------------------------------------------------------ */
1923         /*
1924          * @see javax.servlet.ServletContext#log(java.lang.String, java.lang.Throwable)
1925          */
1926         @Override
1927         public void log(String message, Throwable throwable)
1928         {
1929             _logger.warn(message,throwable);
1930         }
1931 
1932         /* ------------------------------------------------------------ */
1933         /*
1934          * @see javax.servlet.ServletContext#getInitParameter(java.lang.String)
1935          */
1936         @Override
1937         public String getInitParameter(String name)
1938         {
1939             return ContextHandler.this.getInitParameter(name);
1940         }
1941 
1942         /* ------------------------------------------------------------ */
1943         /*
1944          * @see javax.servlet.ServletContext#getInitParameterNames()
1945          */
1946         @SuppressWarnings("unchecked")
1947         @Override
1948         public Enumeration getInitParameterNames()
1949         {
1950             return ContextHandler.this.getInitParameterNames();
1951         }
1952 
1953         /* ------------------------------------------------------------ */
1954         /*
1955          * @see javax.servlet.ServletContext#getAttribute(java.lang.String)
1956          */
1957         @Override
1958         public synchronized Object getAttribute(String name)
1959         {
1960             Object o = ContextHandler.this.getAttribute(name);
1961             if (o == null && _contextAttributes != null)
1962                 o = _contextAttributes.getAttribute(name);
1963             return o;
1964         }
1965 
1966         /* ------------------------------------------------------------ */
1967         /*
1968          * @see javax.servlet.ServletContext#getAttributeNames()
1969          */
1970         @SuppressWarnings("unchecked")
1971         @Override
1972         public synchronized Enumeration getAttributeNames()
1973         {
1974             HashSet<String> set = new HashSet<String>();
1975             if (_contextAttributes != null)
1976             {
1977                 Enumeration<String> e = _contextAttributes.getAttributeNames();
1978                 while (e.hasMoreElements())
1979                     set.add(e.nextElement());
1980             }
1981             Enumeration<String> e = _attributes.getAttributeNames();
1982             while (e.hasMoreElements())
1983                 set.add(e.nextElement());
1984 
1985             return Collections.enumeration(set);
1986         }
1987 
1988         /* ------------------------------------------------------------ */
1989         /*
1990          * @see javax.servlet.ServletContext#setAttribute(java.lang.String, java.lang.Object)
1991          */
1992         @Override
1993         public synchronized void setAttribute(String name, Object value)
1994         {
1995             checkManagedAttribute(name,value);
1996             Object old_value = _contextAttributes.getAttribute(name);
1997 
1998             if (value == null)
1999                 _contextAttributes.removeAttribute(name);
2000             else
2001                 _contextAttributes.setAttribute(name,value);
2002 
2003             if (_contextAttributeListeners != null)
2004             {
2005                 ServletContextAttributeEvent event = new ServletContextAttributeEvent(_scontext,name,old_value == null?value:old_value);
2006 
2007                 for (int i = 0; i < LazyList.size(_contextAttributeListeners); i++)
2008                 {
2009                     ServletContextAttributeListener l = (ServletContextAttributeListener)LazyList.get(_contextAttributeListeners,i);
2010 
2011                     if (old_value == null)
2012                         l.attributeAdded(event);
2013                     else if (value == null)
2014                         l.attributeRemoved(event);
2015                     else
2016                         l.attributeReplaced(event);
2017                 }
2018             }
2019         }
2020 
2021         /* ------------------------------------------------------------ */
2022         /*
2023          * @see javax.servlet.ServletContext#removeAttribute(java.lang.String)
2024          */
2025         @Override
2026         public synchronized void removeAttribute(String name)
2027         {
2028             checkManagedAttribute(name,null);
2029 
2030             if (_contextAttributes == null)
2031             {
2032                 // Set it on the handler
2033                 _attributes.removeAttribute(name);
2034                 return;
2035             }
2036 
2037             Object old_value = _contextAttributes.getAttribute(name);
2038             _contextAttributes.removeAttribute(name);
2039             if (old_value != null)
2040             {
2041                 if (_contextAttributeListeners != null)
2042                 {
2043                     ServletContextAttributeEvent event = new ServletContextAttributeEvent(_scontext,name,old_value);
2044 
2045                     for (int i = 0; i < LazyList.size(_contextAttributeListeners); i++)
2046                         ((ServletContextAttributeListener)LazyList.get(_contextAttributeListeners,i)).attributeRemoved(event);
2047                 }
2048             }
2049         }
2050 
2051         /* ------------------------------------------------------------ */
2052         /*
2053          * @see javax.servlet.ServletContext#getServletContextName()
2054          */
2055         @Override
2056         public String getServletContextName()
2057         {
2058             String name = ContextHandler.this.getDisplayName();
2059             if (name == null)
2060                 name = ContextHandler.this.getContextPath();
2061             return name;
2062         }
2063 
2064         /* ------------------------------------------------------------ */
2065         @Override
2066         public String getContextPath()
2067         {
2068             if ((_contextPath != null) && _contextPath.equals(URIUtil.SLASH))
2069                 return "";
2070 
2071             return _contextPath;
2072         }
2073 
2074         /* ------------------------------------------------------------ */
2075         @Override
2076         public String toString()
2077         {
2078             return "ServletContext@" + ContextHandler.this.toString();
2079         }
2080 
2081         /* ------------------------------------------------------------ */
2082         @Override
2083         public boolean setInitParameter(String name, String value)
2084         {
2085             if (ContextHandler.this.getInitParameter(name) != null)
2086                 return false;
2087             ContextHandler.this.getInitParams().put(name,value);
2088             return true;
2089         }
2090 
2091         /* ------------------------------------------------------------ */
2092         final private static String __unimplmented="Unimplemented - use org.eclipse.jetty.servlet.ServletContextHandler";
2093 
2094         @Override
2095         public Dynamic addFilter(String filterName, Class<? extends Filter> filterClass)
2096         {
2097             LOG.warn(__unimplmented);
2098             return null;
2099         }
2100 
2101         @Override
2102         public Dynamic addFilter(String filterName, Filter filter)
2103         {
2104             LOG.warn(__unimplmented);
2105             return null;
2106         }
2107 
2108         @Override
2109         public Dynamic addFilter(String filterName, String className)
2110         {
2111             LOG.warn(__unimplmented);
2112             return null;
2113         }
2114 
2115         @Override
2116         public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, Class<? extends Servlet> servletClass)
2117         {
2118             LOG.warn(__unimplmented);
2119             return null;
2120         }
2121 
2122         @Override
2123         public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet)
2124         {
2125             LOG.warn(__unimplmented);
2126             return null;
2127         }
2128 
2129         @Override
2130         public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, String className)
2131         {
2132             LOG.warn(__unimplmented);
2133             return null;
2134         }
2135 
2136         @Override
2137         public <T extends Filter> T createFilter(Class<T> c) throws ServletException
2138         {
2139             LOG.warn(__unimplmented);
2140             return null;
2141         }
2142 
2143         @Override
2144         public <T extends Servlet> T createServlet(Class<T> c) throws ServletException
2145         {
2146             LOG.warn(__unimplmented);
2147             return null;
2148         }
2149 
2150         @Override
2151         public Set<SessionTrackingMode> getDefaultSessionTrackingModes()
2152         {
2153             LOG.warn(__unimplmented);
2154             return null;
2155         }
2156 
2157         @Override
2158         public Set<SessionTrackingMode> getEffectiveSessionTrackingModes()
2159         {
2160             LOG.warn(__unimplmented);
2161             return null;
2162         }
2163 
2164         @Override
2165         public FilterRegistration getFilterRegistration(String filterName)
2166         {
2167             LOG.warn(__unimplmented);
2168             return null;
2169         }
2170 
2171         @Override
2172         public Map<String, ? extends FilterRegistration> getFilterRegistrations()
2173         {
2174             LOG.warn(__unimplmented);
2175             return null;
2176         }
2177 
2178         @Override
2179         public ServletRegistration getServletRegistration(String servletName)
2180         {
2181             LOG.warn(__unimplmented);
2182             return null;
2183         }
2184 
2185         @Override
2186         public Map<String, ? extends ServletRegistration> getServletRegistrations()
2187         {
2188             LOG.warn(__unimplmented);
2189             return null;
2190         }
2191 
2192         @Override
2193         public SessionCookieConfig getSessionCookieConfig()
2194         {
2195             LOG.warn(__unimplmented);
2196             return null;
2197         }
2198 
2199         @Override
2200         public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes)
2201         {
2202             LOG.warn(__unimplmented);
2203         }
2204 
2205         @Override
2206         public void addListener(String className)
2207         {
2208             if (!_enabled)
2209                 throw new UnsupportedOperationException();
2210             
2211             try
2212             {
2213                 Class<? extends EventListener> clazz = _classLoader==null?Loader.loadClass(ContextHandler.class,className):_classLoader.loadClass(className);
2214                 addListener(clazz);
2215             }
2216             catch (ClassNotFoundException e)
2217             {
2218                 throw new IllegalArgumentException(e);
2219             }
2220         }
2221 
2222         @Override
2223         public <T extends EventListener> void addListener(T t)
2224         {            
2225             if (!_enabled)
2226                 throw new UnsupportedOperationException();
2227             ContextHandler.this.addEventListener(t);
2228         }
2229 
2230         @Override
2231         public void addListener(Class<? extends EventListener> listenerClass)
2232         {            
2233             if (!_enabled)
2234                 throw new UnsupportedOperationException();
2235 
2236             try
2237             {
2238                 EventListener e = createListener(listenerClass);
2239                 ContextHandler.this.addEventListener(e);
2240                 ContextHandler.this.restrictEventListener(e);
2241             }
2242             catch (ServletException e)
2243             {
2244                 throw new IllegalArgumentException(e);
2245             }
2246         }
2247 
2248         @Override
2249         public <T extends EventListener> T createListener(Class<T> clazz) throws ServletException
2250         {
2251             try
2252             {
2253                 return clazz.newInstance();
2254             }
2255             catch (InstantiationException e)
2256             {
2257                 throw new ServletException(e);
2258             }
2259             catch (IllegalAccessException e)
2260             {
2261                 throw new ServletException(e);
2262             }
2263         }
2264 
2265         @Override
2266         public ClassLoader getClassLoader()
2267         {
2268             AccessController.checkPermission(new RuntimePermission("getClassLoader"));
2269             return _classLoader;
2270         }
2271 
2272         @Override
2273         public int getEffectiveMajorVersion()
2274         {
2275             return _majorVersion;
2276         }
2277 
2278         @Override
2279         public int getEffectiveMinorVersion()
2280         {
2281             return _minorVersion;
2282         }
2283 
2284         public void setEffectiveMajorVersion (int v)
2285         {
2286             _majorVersion = v;
2287         }
2288         
2289         public void setEffectiveMinorVersion (int v)
2290         {
2291             _minorVersion = v;
2292         }
2293         
2294         @Override
2295         public JspConfigDescriptor getJspConfigDescriptor()
2296         {
2297             return null;
2298         }
2299 
2300         @Override
2301         public void declareRoles(String... roleNames)
2302         {
2303             if (!isStarting())
2304                 throw new IllegalStateException ();
2305             if (!_enabled)
2306                 throw new UnsupportedOperationException();
2307             
2308             // TODO Auto-generated method stub
2309             
2310         }
2311 
2312         public void setEnabled(boolean enabled)
2313         {
2314             _enabled = enabled;
2315         }
2316 
2317         public boolean isEnabled()
2318         {
2319             return _enabled;
2320         }
2321     }
2322 
2323     private static class CLDump implements Dumpable
2324     {
2325         final ClassLoader _loader;
2326 
2327         CLDump(ClassLoader loader)
2328         {
2329             _loader = loader;
2330         }
2331 
2332         public String dump()
2333         {
2334             return AggregateLifeCycle.dump(this);
2335         }
2336 
2337         public void dump(Appendable out, String indent) throws IOException
2338         {
2339             out.append(String.valueOf(_loader)).append("\n");
2340 
2341             if (_loader != null)
2342             {
2343                 Object parent = _loader.getParent();
2344                 if (parent != null)
2345                 {
2346                     if (!(parent instanceof Dumpable))
2347                         parent = new CLDump((ClassLoader)parent);
2348 
2349                     if (_loader instanceof URLClassLoader)
2350                         AggregateLifeCycle.dump(out,indent,TypeUtil.asList(((URLClassLoader)_loader).getURLs()),Collections.singleton(parent));
2351                     else
2352                         AggregateLifeCycle.dump(out,indent,Collections.singleton(parent));
2353                 }
2354             }
2355         }
2356 
2357     }
2358 }