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