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