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         if (LOG.isDebugEnabled())
829             LOG.debug("scope {}|{}|{} @ {}",baseRequest.getContextPath(),baseRequest.getServletPath(),baseRequest.getPathInfo(),this);
830 
831         Context old_context = null;
832         String old_context_path = null;
833         String old_servlet_path = null;
834         String old_path_info = null;
835         ClassLoader old_classloader = null;
836         Thread current_thread = null;
837         String pathInfo = target;
838 
839         DispatcherType dispatch = baseRequest.getDispatcherType();
840 
841         old_context = baseRequest.getContext();
842 
843         // Are we already in this context?
844         if (old_context != _scontext)
845         {
846             // check the target.
847             if (DispatcherType.REQUEST.equals(dispatch) || DispatcherType.ASYNC.equals(dispatch))
848             {
849                 if (_compactPath)
850                     target = URIUtil.compactPath(target);
851                 if (!checkContext(target,baseRequest,response))
852                     return;
853 
854                 if (target.length() > _contextPath.length())
855                 {
856                     if (_contextPath.length() > 1)
857                         target = target.substring(_contextPath.length());
858                     pathInfo = target;
859                 }
860                 else if (_contextPath.length() == 1)
861                 {
862                     target = URIUtil.SLASH;
863                     pathInfo = URIUtil.SLASH;
864                 }
865                 else
866                 {
867                     target = URIUtil.SLASH;
868                     pathInfo = null;
869                 }
870             }
871 
872             // Set the classloader
873             if (_classLoader != null)
874             {
875                 current_thread = Thread.currentThread();
876                 old_classloader = current_thread.getContextClassLoader();
877                 current_thread.setContextClassLoader(_classLoader);
878             }
879         }
880 
881         try
882         {
883             old_context_path = baseRequest.getContextPath();
884             old_servlet_path = baseRequest.getServletPath();
885             old_path_info = baseRequest.getPathInfo();
886 
887             // Update the paths
888             baseRequest.setContext(_scontext);
889             __context.set(_scontext);
890             if (!DispatcherType.INCLUDE.equals(dispatch) && target.startsWith("/"))
891             {
892                 if (_contextPath.length() == 1)
893                     baseRequest.setContextPath("");
894                 else
895                     baseRequest.setContextPath(_contextPath);
896                 baseRequest.setServletPath(null);
897                 baseRequest.setPathInfo(pathInfo);
898             }
899 
900             if (LOG.isDebugEnabled())
901                 LOG.debug("context={}|{}|{} @ {}",baseRequest.getContextPath(),baseRequest.getServletPath(), baseRequest.getPathInfo(),this);
902 
903             // start manual inline of nextScope(target,baseRequest,request,response);
904             if (never())
905                 nextScope(target,baseRequest,request,response);
906             else if (_nextScope != null)
907                 _nextScope.doScope(target,baseRequest,request,response);
908             else if (_outerScope != null)
909                 _outerScope.doHandle(target,baseRequest,request,response);
910             else
911                 doHandle(target,baseRequest,request,response);
912             // end manual inline (pathentic attempt to reduce stack depth)
913         }
914         finally
915         {
916             if (old_context != _scontext)
917             {
918                 // reset the classloader
919                 if (_classLoader != null)
920                 {
921                     current_thread.setContextClassLoader(old_classloader);
922                 }
923 
924                 // reset the context and servlet path.
925                 baseRequest.setContext(old_context);
926                 __context.set(old_context);
927                 baseRequest.setContextPath(old_context_path);
928                 baseRequest.setServletPath(old_servlet_path);
929                 baseRequest.setPathInfo(old_path_info);
930             }
931         }
932     }
933 
934     /* ------------------------------------------------------------ */
935     /**
936      * @see org.eclipse.jetty.server.handler.ScopedHandler#doHandle(java.lang.String, org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest,
937      *      javax.servlet.http.HttpServletResponse)
938      */
939     @Override
940     public void doHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
941     {
942         final DispatcherType dispatch = baseRequest.getDispatcherType();
943         final boolean new_context = baseRequest.takeNewContext();
944         try
945         {
946             if (new_context)
947             {
948                 // Handle the REALLY SILLY request events!
949                 if (_requestAttributeListeners != null)
950                 {
951                     final int s = LazyList.size(_requestAttributeListeners);
952                     for (int i = 0; i < s; i++)
953                         baseRequest.addEventListener(((EventListener)LazyList.get(_requestAttributeListeners,i)));
954                 }
955 
956                 if (_requestListeners != null)
957                 {
958                     final int s = LazyList.size(_requestListeners);
959                     final ServletRequestEvent sre = new ServletRequestEvent(_scontext,request);
960                     for (int i = 0; i < s; i++)
961                         ((ServletRequestListener)LazyList.get(_requestListeners,i)).requestInitialized(sre);
962                 }
963             }
964 
965             if (DispatcherType.REQUEST.equals(dispatch) && isProtectedTarget(target))
966                 throw new HttpException(HttpServletResponse.SC_NOT_FOUND);
967 
968             // start manual inline of nextHandle(target,baseRequest,request,response);
969             // noinspection ConstantIfStatement
970             if (never())
971                 nextHandle(target,baseRequest,request,response);
972             else if (_nextScope != null && _nextScope == _handler)
973                 _nextScope.doHandle(target,baseRequest,request,response);
974             else if (_handler != null)
975                 _handler.handle(target,baseRequest,request,response);
976             // end manual inline
977         }
978         catch (HttpException e)
979         {
980             LOG.debug(e);
981             baseRequest.setHandled(true);
982             response.sendError(e.getStatus(),e.getReason());
983         }
984         finally
985         {
986             // Handle more REALLY SILLY request events!
987             if (new_context)
988             {
989                 if (_requestListeners != null)
990                 {
991                     final ServletRequestEvent sre = new ServletRequestEvent(_scontext,request);
992                     for (int i = LazyList.size(_requestListeners); i-- > 0;)
993                         ((ServletRequestListener)LazyList.get(_requestListeners,i)).requestDestroyed(sre);
994                 }
995 
996                 if (_requestAttributeListeners != null)
997                 {
998                     for (int i = LazyList.size(_requestAttributeListeners); i-- > 0;)
999                         baseRequest.removeEventListener(((EventListener)LazyList.get(_requestAttributeListeners,i)));
1000                 }
1001             }
1002         }
1003     }
1004 
1005     /* ------------------------------------------------------------ */
1006     /*
1007      * Handle a runnable in this context
1008      */
1009     public void handle(Runnable runnable)
1010     {
1011         ClassLoader old_classloader = null;
1012         Thread current_thread = null;
1013         Context old_context = null;
1014         try
1015         {
1016             old_context = __context.get();
1017             __context.set(_scontext);
1018 
1019             // Set the classloader
1020             if (_classLoader != null)
1021             {
1022                 current_thread = Thread.currentThread();
1023                 old_classloader = current_thread.getContextClassLoader();
1024                 current_thread.setContextClassLoader(_classLoader);
1025             }
1026 
1027             runnable.run();
1028         }
1029         finally
1030         {
1031             __context.set(old_context);
1032             if (old_classloader != null)
1033             {
1034                 current_thread.setContextClassLoader(old_classloader);
1035             }
1036         }
1037     }
1038 
1039     /* ------------------------------------------------------------ */
1040     /**
1041      * Check the target. Called by {@link #handle(String, Request, HttpServletRequest, HttpServletResponse)} when a target within a context is determined. If
1042      * the target is protected, 404 is returned. The default implementation always returns false.
1043      */
1044     /* ------------------------------------------------------------ */
1045     protected boolean isProtectedTarget(String target)
1046     {
1047         return false;
1048     }
1049 
1050     /* ------------------------------------------------------------ */
1051     /*
1052      * @see javax.servlet.ServletContext#removeAttribute(java.lang.String)
1053      */
1054     public void removeAttribute(String name)
1055     {
1056         checkManagedAttribute(name,null);
1057         _attributes.removeAttribute(name);
1058     }
1059 
1060     /* ------------------------------------------------------------ */
1061     /*
1062      * Set a context attribute. Attributes set via this API cannot be overriden by the ServletContext.setAttribute API. Their lifecycle spans the stop/start of
1063      * a context. No attribute listener events are triggered by this API.
1064      * 
1065      * @see javax.servlet.ServletContext#setAttribute(java.lang.String, java.lang.Object)
1066      */
1067     public void setAttribute(String name, Object value)
1068     {
1069         checkManagedAttribute(name,value);
1070         _attributes.setAttribute(name,value);
1071     }
1072 
1073     /* ------------------------------------------------------------ */
1074     /**
1075      * @param attributes
1076      *            The attributes to set.
1077      */
1078     public void setAttributes(Attributes attributes)
1079     {
1080         _attributes.clearAttributes();
1081         _attributes.addAll(attributes);
1082         Enumeration e = _attributes.getAttributeNames();
1083         while (e.hasMoreElements())
1084         {
1085             String name = (String)e.nextElement();
1086             checkManagedAttribute(name,attributes.getAttribute(name));
1087         }
1088     }
1089 
1090     /* ------------------------------------------------------------ */
1091     public void clearAttributes()
1092     {
1093         Enumeration e = _attributes.getAttributeNames();
1094         while (e.hasMoreElements())
1095         {
1096             String name = (String)e.nextElement();
1097             checkManagedAttribute(name,null);
1098         }
1099         _attributes.clearAttributes();
1100     }
1101 
1102     /* ------------------------------------------------------------ */
1103     public void checkManagedAttribute(String name, Object value)
1104     {
1105         if (_managedAttributes != null && _managedAttributes.containsKey(name))
1106         {
1107             setManagedAttribute(name,value);
1108         }
1109     }
1110 
1111     /* ------------------------------------------------------------ */
1112     public void setManagedAttribute(String name, Object value)
1113     {
1114         Object old = _managedAttributes.put(name,value);
1115         getServer().getContainer().update(this,old,value,name,true);
1116     }
1117 
1118     /* ------------------------------------------------------------ */
1119     /**
1120      * @param classLoader
1121      *            The classLoader to set.
1122      */
1123     public void setClassLoader(ClassLoader classLoader)
1124     {
1125         _classLoader = classLoader;
1126     }
1127 
1128     /* ------------------------------------------------------------ */
1129     /**
1130      * @param contextPath
1131      *            The _contextPath to set.
1132      */
1133     public void setContextPath(String contextPath)
1134     {
1135         if (contextPath != null && contextPath.length() > 1 && contextPath.endsWith("/"))
1136             throw new IllegalArgumentException("ends with /");
1137         _contextPath = contextPath;
1138 
1139         if (getServer() != null && (getServer().isStarting() || getServer().isStarted()))
1140         {
1141             Handler[] contextCollections = getServer().getChildHandlersByClass(ContextHandlerCollection.class);
1142             for (int h = 0; contextCollections != null && h < contextCollections.length; h++)
1143                 ((ContextHandlerCollection)contextCollections[h]).mapContexts();
1144         }
1145     }
1146 
1147     /* ------------------------------------------------------------ */
1148     /**
1149      * @param servletContextName
1150      *            The servletContextName to set.
1151      */
1152     public void setDisplayName(String servletContextName)
1153     {
1154         _displayName = servletContextName;
1155     }
1156 
1157     /* ------------------------------------------------------------ */
1158     /**
1159      * @return Returns the resourceBase.
1160      */
1161     public Resource getBaseResource()
1162     {
1163         if (_baseResource == null)
1164             return null;
1165         return _baseResource;
1166     }
1167 
1168     /* ------------------------------------------------------------ */
1169     /**
1170      * @return Returns the base resource as a string.
1171      */
1172     public String getResourceBase()
1173     {
1174         if (_baseResource == null)
1175             return null;
1176         return _baseResource.toString();
1177     }
1178 
1179     /* ------------------------------------------------------------ */
1180     /**
1181      * @param base
1182      *            The resourceBase to set.
1183      */
1184     public void setBaseResource(Resource base)
1185     {
1186         _baseResource = base;
1187     }
1188 
1189     /* ------------------------------------------------------------ */
1190     /**
1191      * @param resourceBase
1192      *            The base resource as a string.
1193      */
1194     public void setResourceBase(String resourceBase)
1195     {
1196         try
1197         {
1198             setBaseResource(newResource(resourceBase));
1199         }
1200         catch (Exception e)
1201         {
1202             LOG.warn(e.toString());
1203             LOG.debug(e);
1204             throw new IllegalArgumentException(resourceBase);
1205         }
1206     }
1207 
1208     /* ------------------------------------------------------------ */
1209     /**
1210      * @return True if aliases are allowed
1211      */
1212     public boolean isAliases()
1213     {
1214         return _aliases;
1215     }
1216 
1217     /* ------------------------------------------------------------ */
1218     /**
1219      * @param aliases
1220      *            aliases are allowed
1221      */
1222     public void setAliases(boolean aliases)
1223     {
1224         _aliases = aliases;
1225     }
1226 
1227     /* ------------------------------------------------------------ */
1228     /**
1229      * @return Returns the mimeTypes.
1230      */
1231     public MimeTypes getMimeTypes()
1232     {
1233         if (_mimeTypes == null)
1234             _mimeTypes = new MimeTypes();
1235         return _mimeTypes;
1236     }
1237 
1238     /* ------------------------------------------------------------ */
1239     /**
1240      * @param mimeTypes
1241      *            The mimeTypes to set.
1242      */
1243     public void setMimeTypes(MimeTypes mimeTypes)
1244     {
1245         _mimeTypes = mimeTypes;
1246     }
1247 
1248     /* ------------------------------------------------------------ */
1249     /**
1250      */
1251     public void setWelcomeFiles(String[] files)
1252     {
1253         _welcomeFiles = files;
1254     }
1255 
1256     /* ------------------------------------------------------------ */
1257     /**
1258      * @return The names of the files which the server should consider to be welcome files in this context.
1259      * @see <a href="http://jcp.org/aboutJava/communityprocess/final/jsr154/index.html">The Servlet Specification</a>
1260      * @see #setWelcomeFiles
1261      */
1262     public String[] getWelcomeFiles()
1263     {
1264         return _welcomeFiles;
1265     }
1266 
1267     /* ------------------------------------------------------------ */
1268     /**
1269      * @return Returns the errorHandler.
1270      */
1271     public ErrorHandler getErrorHandler()
1272     {
1273         return _errorHandler;
1274     }
1275 
1276     /* ------------------------------------------------------------ */
1277     /**
1278      * @param errorHandler
1279      *            The errorHandler to set.
1280      */
1281     public void setErrorHandler(ErrorHandler errorHandler)
1282     {
1283         if (errorHandler != null)
1284             errorHandler.setServer(getServer());
1285         if (getServer() != null)
1286             getServer().getContainer().update(this,_errorHandler,errorHandler,"errorHandler",true);
1287         _errorHandler = errorHandler;
1288     }
1289 
1290     /* ------------------------------------------------------------ */
1291     public int getMaxFormContentSize()
1292     {
1293         return _maxFormContentSize;
1294     }
1295 
1296     /* ------------------------------------------------------------ */
1297     public void setMaxFormContentSize(int maxSize)
1298     {
1299         _maxFormContentSize = maxSize;
1300     }
1301 
1302     /* ------------------------------------------------------------ */
1303     /**
1304      * @return True if URLs are compacted to replace multiple '/'s with a single '/'
1305      */
1306     public boolean isCompactPath()
1307     {
1308         return _compactPath;
1309     }
1310 
1311     /* ------------------------------------------------------------ */
1312     /**
1313      * @param compactPath
1314      *            True if URLs are compacted to replace multiple '/'s with a single '/'
1315      */
1316     public void setCompactPath(boolean compactPath)
1317     {
1318         _compactPath = compactPath;
1319     }
1320 
1321     /* ------------------------------------------------------------ */
1322     @Override
1323     public String toString()
1324     {
1325         String[] vhosts = getVirtualHosts();
1326 
1327         StringBuilder b = new StringBuilder();
1328 
1329         String p = getClass().getPackage().getName();
1330         if (p != null && p.length() > 0)
1331         {
1332             String[] ss = p.split("\\.");
1333             for (String s : ss)
1334                 b.append(s.charAt(0)).append('.');
1335         }
1336 
1337         b.append(getClass().getSimpleName());
1338         b.append('{').append(getContextPath()).append(',').append(getBaseResource());
1339 
1340         if (vhosts != null && vhosts.length > 0)
1341             b.append(',').append(vhosts[0]);
1342         b.append('}');
1343 
1344         return b.toString();
1345     }
1346 
1347     /* ------------------------------------------------------------ */
1348     public synchronized Class<?> loadClass(String className) throws ClassNotFoundException
1349     {
1350         if (className == null)
1351             return null;
1352 
1353         if (_classLoader == null)
1354             return Loader.loadClass(this.getClass(),className);
1355 
1356         return _classLoader.loadClass(className);
1357     }
1358 
1359     /* ------------------------------------------------------------ */
1360     public void addLocaleEncoding(String locale, String encoding)
1361     {
1362         if (_localeEncodingMap == null)
1363             _localeEncodingMap = new HashMap<String, String>();
1364         _localeEncodingMap.put(locale,encoding);
1365     }
1366 
1367     /* ------------------------------------------------------------ */
1368     public String getLocaleEncoding(String locale)
1369     {
1370         if (_localeEncodingMap == null)
1371             return null;
1372         String encoding = _localeEncodingMap.get(locale);
1373         return encoding;
1374     }
1375 
1376     /* ------------------------------------------------------------ */
1377     /**
1378      * 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
1379      * language is looked up.
1380      * 
1381      * @param locale
1382      *            a <code>Locale</code> value
1383      * @return a <code>String</code> representing the character encoding for the locale or null if none found.
1384      */
1385     public String getLocaleEncoding(Locale locale)
1386     {
1387         if (_localeEncodingMap == null)
1388             return null;
1389         String encoding = _localeEncodingMap.get(locale.toString());
1390         if (encoding == null)
1391             encoding = _localeEncodingMap.get(locale.getLanguage());
1392         return encoding;
1393     }
1394 
1395     /* ------------------------------------------------------------ */
1396     /*
1397      */
1398     public Resource getResource(String path) throws MalformedURLException
1399     {
1400         if (path == null || !path.startsWith(URIUtil.SLASH))
1401             throw new MalformedURLException(path);
1402 
1403         if (_baseResource == null)
1404             return null;
1405 
1406         try
1407         {
1408             path = URIUtil.canonicalPath(path);
1409             Resource resource = _baseResource.addPath(path);
1410 
1411             if (!_aliases && resource.getAlias() != null)
1412             {
1413                 if (resource.exists())
1414                     LOG.warn("Aliased resource: " + resource + "~=" + resource.getAlias());
1415                 else if (path.endsWith("/") && resource.getAlias().toString().endsWith(path))
1416                     return resource;
1417                 else if (LOG.isDebugEnabled())
1418                     LOG.debug("Aliased resource: " + resource + "~=" + resource.getAlias());
1419                 return null;
1420             }
1421 
1422             return resource;
1423         }
1424         catch (Exception e)
1425         {
1426             LOG.ignore(e);
1427         }
1428 
1429         return null;
1430     }
1431 
1432     /* ------------------------------------------------------------ */
1433     /**
1434      * Convert URL to Resource wrapper for {@link Resource#newResource(URL)} enables extensions to provide alternate resource implementations.
1435      */
1436     public Resource newResource(URL url) throws IOException
1437     {
1438         return Resource.newResource(url);
1439     }
1440 
1441     /* ------------------------------------------------------------ */
1442     /**
1443      * Convert a URL or path to a Resource. The default implementation is a wrapper for {@link Resource#newResource(String)}.
1444      * 
1445      * @param urlOrPath
1446      *            The URL or path to convert
1447      * @return The Resource for the URL/path
1448      * @throws IOException
1449      *             The Resource could not be created.
1450      */
1451     public Resource newResource(String urlOrPath) throws IOException
1452     {
1453         return Resource.newResource(urlOrPath);
1454     }
1455 
1456     /* ------------------------------------------------------------ */
1457     /*
1458      */
1459     public Set<String> getResourcePaths(String path)
1460     {
1461         try
1462         {
1463             path = URIUtil.canonicalPath(path);
1464             Resource resource = getResource(path);
1465 
1466             if (resource != null && resource.exists())
1467             {
1468                 if (!path.endsWith(URIUtil.SLASH))
1469                     path = path + URIUtil.SLASH;
1470 
1471                 String[] l = resource.list();
1472                 if (l != null)
1473                 {
1474                     HashSet<String> set = new HashSet<String>();
1475                     for (int i = 0; i < l.length; i++)
1476                         set.add(path + l[i]);
1477                     return set;
1478                 }
1479             }
1480         }
1481         catch (Exception e)
1482         {
1483             LOG.ignore(e);
1484         }
1485         return Collections.emptySet();
1486     }
1487 
1488     /* ------------------------------------------------------------ */
1489     private String normalizeHostname(String host)
1490     {
1491         if (host == null)
1492             return null;
1493 
1494         if (host.endsWith("."))
1495             return host.substring(0,host.length() - 1);
1496 
1497         return host;
1498     }
1499 
1500     /* ------------------------------------------------------------ */
1501     /**
1502      * Context.
1503      * <p>
1504      * A partial implementation of {@link javax.servlet.ServletContext}. A complete implementation is provided by the derived {@link ContextHandler}.
1505      * </p>
1506      * 
1507      * 
1508      */
1509     public class Context implements ServletContext
1510     {
1511         protected int _majorVersion = 3;
1512         protected int _minorVersion = 0;
1513         protected boolean _enabled = true; //whether or not the dynamic API is enabled for callers
1514 
1515         /* ------------------------------------------------------------ */
1516         protected Context()
1517         {
1518         }
1519 
1520         /* ------------------------------------------------------------ */
1521         public ContextHandler getContextHandler()
1522         {
1523             // TODO reduce visibility of this method
1524             return ContextHandler.this;
1525         }
1526 
1527         /* ------------------------------------------------------------ */
1528         /*
1529          * @see javax.servlet.ServletContext#getContext(java.lang.String)
1530          */
1531         @Override
1532         public ServletContext getContext(String uripath)
1533         {
1534             List<ContextHandler> contexts = new ArrayList<ContextHandler>();
1535             Handler[] handlers = getServer().getChildHandlersByClass(ContextHandler.class);
1536             String matched_path = null;
1537 
1538             for (Handler handler : handlers)
1539             {
1540                 if (handler == null)
1541                     continue;
1542                 ContextHandler ch = (ContextHandler)handler;
1543                 String context_path = ch.getContextPath();
1544 
1545                 if (uripath.equals(context_path) || (uripath.startsWith(context_path) && uripath.charAt(context_path.length()) == '/')
1546                         || "/".equals(context_path))
1547                 {
1548                     // look first for vhost matching context only
1549                     if (getVirtualHosts() != null && getVirtualHosts().length > 0)
1550                     {
1551                         if (ch.getVirtualHosts() != null && ch.getVirtualHosts().length > 0)
1552                         {
1553                             for (String h1 : getVirtualHosts())
1554                                 for (String h2 : ch.getVirtualHosts())
1555                                     if (h1.equals(h2))
1556                                     {
1557                                         if (matched_path == null || context_path.length() > matched_path.length())
1558                                         {
1559                                             contexts.clear();
1560                                             matched_path = context_path;
1561                                         }
1562 
1563                                         if (matched_path.equals(context_path))
1564                                             contexts.add(ch);
1565                                     }
1566                         }
1567                     }
1568                     else
1569                     {
1570                         if (matched_path == null || context_path.length() > matched_path.length())
1571                         {
1572                             contexts.clear();
1573                             matched_path = context_path;
1574                         }
1575 
1576                         if (matched_path.equals(context_path))
1577                             contexts.add(ch);
1578                     }
1579                 }
1580             }
1581 
1582             if (contexts.size() > 0)
1583                 return contexts.get(0)._scontext;
1584 
1585             // try again ignoring virtual hosts
1586             matched_path = null;
1587             for (Handler handler : handlers)
1588             {
1589                 if (handler == null)
1590                     continue;
1591                 ContextHandler ch = (ContextHandler)handler;
1592                 String context_path = ch.getContextPath();
1593 
1594                 if (uripath.equals(context_path) || (uripath.startsWith(context_path) && uripath.charAt(context_path.length()) == '/')
1595                         || "/".equals(context_path))
1596                 {
1597                     if (matched_path == null || context_path.length() > matched_path.length())
1598                     {
1599                         contexts.clear();
1600                         matched_path = context_path;
1601                     }
1602 
1603                     if (matched_path.equals(context_path))
1604                         contexts.add(ch);
1605                 }
1606             }
1607 
1608             if (contexts.size() > 0)
1609                 return contexts.get(0)._scontext;
1610             return null;
1611         }
1612 
1613         /* ------------------------------------------------------------ */
1614         /*
1615          * @see javax.servlet.ServletContext#getMajorVersion()
1616          */
1617         @Override
1618         public int getMajorVersion()
1619         {
1620             return 3;
1621         }
1622       
1623 
1624         /* ------------------------------------------------------------ */
1625         /*
1626          * @see javax.servlet.ServletContext#getMimeType(java.lang.String)
1627          */
1628         @Override
1629         public String getMimeType(String file)
1630         {
1631             if (_mimeTypes == null)
1632                 return null;
1633             Buffer mime = _mimeTypes.getMimeByExtension(file);
1634             if (mime != null)
1635                 return mime.toString();
1636             return null;
1637         }
1638 
1639         /* ------------------------------------------------------------ */
1640         /*
1641          * @see javax.servlet.ServletContext#getMinorVersion()
1642          */
1643         @Override
1644         public int getMinorVersion()
1645         {
1646             return 0;
1647         }
1648 
1649         /* ------------------------------------------------------------ */
1650         /*
1651          * @see javax.servlet.ServletContext#getNamedDispatcher(java.lang.String)
1652          */
1653         @Override
1654         public RequestDispatcher getNamedDispatcher(String name)
1655         {
1656             return null;
1657         }
1658 
1659         /* ------------------------------------------------------------ */
1660         /*
1661          * @see javax.servlet.ServletContext#getRequestDispatcher(java.lang.String)
1662          */
1663         @Override
1664         public RequestDispatcher getRequestDispatcher(String uriInContext)
1665         {
1666             if (uriInContext == null)
1667                 return null;
1668 
1669             if (!uriInContext.startsWith("/"))
1670                 return null;
1671 
1672             try
1673             {
1674                 String query = null;
1675                 int q = 0;
1676                 if ((q = uriInContext.indexOf('?')) > 0)
1677                 {
1678                     query = uriInContext.substring(q + 1);
1679                     uriInContext = uriInContext.substring(0,q);
1680                 }
1681                 if ((q = uriInContext.indexOf(';')) > 0)
1682                     uriInContext = uriInContext.substring(0,q);
1683 
1684                 String pathInContext = URIUtil.canonicalPath(URIUtil.decodePath(uriInContext));
1685                 String uri = URIUtil.addPaths(getContextPath(),uriInContext);
1686                 ContextHandler context = ContextHandler.this;
1687                 return new Dispatcher(context,uri,pathInContext,query);
1688             }
1689             catch (Exception e)
1690             {
1691                 LOG.ignore(e);
1692             }
1693             return null;
1694         }
1695 
1696         /* ------------------------------------------------------------ */
1697         /*
1698          * @see javax.servlet.ServletContext#getRealPath(java.lang.String)
1699          */
1700         @Override
1701         public String getRealPath(String path)
1702         {
1703             if (path == null)
1704                 return null;
1705             if (path.length() == 0)
1706                 path = URIUtil.SLASH;
1707             else if (path.charAt(0) != '/')
1708                 path = URIUtil.SLASH + path;
1709 
1710             try
1711             {
1712                 Resource resource = ContextHandler.this.getResource(path);
1713                 if (resource != null)
1714                 {
1715                     File file = resource.getFile();
1716                     if (file != null)
1717                         return file.getCanonicalPath();
1718                 }
1719             }
1720             catch (Exception e)
1721             {
1722                 LOG.ignore(e);
1723             }
1724 
1725             return null;
1726         }
1727 
1728         /* ------------------------------------------------------------ */
1729         @Override
1730         public URL getResource(String path) throws MalformedURLException
1731         {
1732             Resource resource = ContextHandler.this.getResource(path);
1733             if (resource != null && resource.exists())
1734                 return resource.getURL();
1735             return null;
1736         }
1737 
1738         /* ------------------------------------------------------------ */
1739         /*
1740          * @see javax.servlet.ServletContext#getResourceAsStream(java.lang.String)
1741          */
1742         @Override
1743         public InputStream getResourceAsStream(String path)
1744         {
1745             try
1746             {
1747                 URL url = getResource(path);
1748                 if (url == null)
1749                     return null;
1750                 return url.openStream();
1751             }
1752             catch (Exception e)
1753             {
1754                 LOG.ignore(e);
1755                 return null;
1756             }
1757         }
1758 
1759         /* ------------------------------------------------------------ */
1760         /*
1761          * @see javax.servlet.ServletContext#getResourcePaths(java.lang.String)
1762          */
1763         @Override
1764         public Set getResourcePaths(String path)
1765         {
1766             return ContextHandler.this.getResourcePaths(path);
1767         }
1768 
1769         /* ------------------------------------------------------------ */
1770         /*
1771          * @see javax.servlet.ServletContext#getServerInfo()
1772          */
1773         @Override
1774         public String getServerInfo()
1775         {
1776             return "jetty/" + Server.getVersion();
1777         }
1778 
1779         /* ------------------------------------------------------------ */
1780         /*
1781          * @see javax.servlet.ServletContext#getServlet(java.lang.String)
1782          */
1783         @Override
1784         @Deprecated
1785         public Servlet getServlet(String name) throws ServletException
1786         {
1787             return null;
1788         }
1789 
1790         /* ------------------------------------------------------------ */
1791         /*
1792          * @see javax.servlet.ServletContext#getServletNames()
1793          */
1794         @SuppressWarnings("unchecked")
1795         @Override
1796         @Deprecated
1797         public Enumeration getServletNames()
1798         {
1799             return Collections.enumeration(Collections.EMPTY_LIST);
1800         }
1801 
1802         /* ------------------------------------------------------------ */
1803         /*
1804          * @see javax.servlet.ServletContext#getServlets()
1805          */
1806         @SuppressWarnings("unchecked")
1807         @Override
1808         @Deprecated
1809         public Enumeration getServlets()
1810         {
1811             return Collections.enumeration(Collections.EMPTY_LIST);
1812         }
1813 
1814         /* ------------------------------------------------------------ */
1815         /*
1816          * @see javax.servlet.ServletContext#log(java.lang.Exception, java.lang.String)
1817          */
1818         @Override
1819         public void log(Exception exception, String msg)
1820         {
1821             _logger.warn(msg,exception);
1822         }
1823 
1824         /* ------------------------------------------------------------ */
1825         /*
1826          * @see javax.servlet.ServletContext#log(java.lang.String)
1827          */
1828         @Override
1829         public void log(String msg)
1830         {
1831             _logger.info(msg);
1832         }
1833 
1834         /* ------------------------------------------------------------ */
1835         /*
1836          * @see javax.servlet.ServletContext#log(java.lang.String, java.lang.Throwable)
1837          */
1838         @Override
1839         public void log(String message, Throwable throwable)
1840         {
1841             _logger.warn(message,throwable);
1842         }
1843 
1844         /* ------------------------------------------------------------ */
1845         /*
1846          * @see javax.servlet.ServletContext#getInitParameter(java.lang.String)
1847          */
1848         @Override
1849         public String getInitParameter(String name)
1850         {
1851             return ContextHandler.this.getInitParameter(name);
1852         }
1853 
1854         /* ------------------------------------------------------------ */
1855         /*
1856          * @see javax.servlet.ServletContext#getInitParameterNames()
1857          */
1858         @SuppressWarnings("unchecked")
1859         @Override
1860         public Enumeration getInitParameterNames()
1861         {
1862             return ContextHandler.this.getInitParameterNames();
1863         }
1864 
1865         /* ------------------------------------------------------------ */
1866         /*
1867          * @see javax.servlet.ServletContext#getAttribute(java.lang.String)
1868          */
1869         @Override
1870         public synchronized Object getAttribute(String name)
1871         {
1872             Object o = ContextHandler.this.getAttribute(name);
1873             if (o == null && _contextAttributes != null)
1874                 o = _contextAttributes.getAttribute(name);
1875             return o;
1876         }
1877 
1878         /* ------------------------------------------------------------ */
1879         /*
1880          * @see javax.servlet.ServletContext#getAttributeNames()
1881          */
1882         @SuppressWarnings("unchecked")
1883         @Override
1884         public synchronized Enumeration getAttributeNames()
1885         {
1886             HashSet<String> set = new HashSet<String>();
1887             if (_contextAttributes != null)
1888             {
1889                 Enumeration<String> e = _contextAttributes.getAttributeNames();
1890                 while (e.hasMoreElements())
1891                     set.add(e.nextElement());
1892             }
1893             Enumeration<String> e = _attributes.getAttributeNames();
1894             while (e.hasMoreElements())
1895                 set.add(e.nextElement());
1896 
1897             return Collections.enumeration(set);
1898         }
1899 
1900         /* ------------------------------------------------------------ */
1901         /*
1902          * @see javax.servlet.ServletContext#setAttribute(java.lang.String, java.lang.Object)
1903          */
1904         @Override
1905         public synchronized void setAttribute(String name, Object value)
1906         {
1907             checkManagedAttribute(name,value);
1908             Object old_value = _contextAttributes.getAttribute(name);
1909 
1910             if (value == null)
1911                 _contextAttributes.removeAttribute(name);
1912             else
1913                 _contextAttributes.setAttribute(name,value);
1914 
1915             if (_contextAttributeListeners != null)
1916             {
1917                 ServletContextAttributeEvent event = new ServletContextAttributeEvent(_scontext,name,old_value == null?value:old_value);
1918 
1919                 for (int i = 0; i < LazyList.size(_contextAttributeListeners); i++)
1920                 {
1921                     ServletContextAttributeListener l = (ServletContextAttributeListener)LazyList.get(_contextAttributeListeners,i);
1922 
1923                     if (old_value == null)
1924                         l.attributeAdded(event);
1925                     else if (value == null)
1926                         l.attributeRemoved(event);
1927                     else
1928                         l.attributeReplaced(event);
1929                 }
1930             }
1931         }
1932 
1933         /* ------------------------------------------------------------ */
1934         /*
1935          * @see javax.servlet.ServletContext#removeAttribute(java.lang.String)
1936          */
1937         @Override
1938         public synchronized void removeAttribute(String name)
1939         {
1940             checkManagedAttribute(name,null);
1941 
1942             if (_contextAttributes == null)
1943             {
1944                 // Set it on the handler
1945                 _attributes.removeAttribute(name);
1946                 return;
1947             }
1948 
1949             Object old_value = _contextAttributes.getAttribute(name);
1950             _contextAttributes.removeAttribute(name);
1951             if (old_value != null)
1952             {
1953                 if (_contextAttributeListeners != null)
1954                 {
1955                     ServletContextAttributeEvent event = new ServletContextAttributeEvent(_scontext,name,old_value);
1956 
1957                     for (int i = 0; i < LazyList.size(_contextAttributeListeners); i++)
1958                         ((ServletContextAttributeListener)LazyList.get(_contextAttributeListeners,i)).attributeRemoved(event);
1959                 }
1960             }
1961         }
1962 
1963         /* ------------------------------------------------------------ */
1964         /*
1965          * @see javax.servlet.ServletContext#getServletContextName()
1966          */
1967         @Override
1968         public String getServletContextName()
1969         {
1970             String name = ContextHandler.this.getDisplayName();
1971             if (name == null)
1972                 name = ContextHandler.this.getContextPath();
1973             return name;
1974         }
1975 
1976         /* ------------------------------------------------------------ */
1977         @Override
1978         public String getContextPath()
1979         {
1980             if ((_contextPath != null) && _contextPath.equals(URIUtil.SLASH))
1981                 return "";
1982 
1983             return _contextPath;
1984         }
1985 
1986         /* ------------------------------------------------------------ */
1987         @Override
1988         public String toString()
1989         {
1990             return "ServletContext@" + ContextHandler.this.toString();
1991         }
1992 
1993         /* ------------------------------------------------------------ */
1994         @Override
1995         public boolean setInitParameter(String name, String value)
1996         {
1997             if (ContextHandler.this.getInitParameter(name) != null)
1998                 return false;
1999             ContextHandler.this.getInitParams().put(name,value);
2000             return true;
2001         }
2002 
2003         /* ------------------------------------------------------------ */
2004         final private static String __unimplmented="Unimplemented - use org.eclipse.jetty.servlet.ServletContextHandler";
2005 
2006         @Override
2007         public Dynamic addFilter(String filterName, Class<? extends Filter> filterClass)
2008         {
2009             LOG.warn(__unimplmented);
2010             return null;
2011         }
2012 
2013         @Override
2014         public Dynamic addFilter(String filterName, Filter filter)
2015         {
2016             LOG.warn(__unimplmented);
2017             return null;
2018         }
2019 
2020         @Override
2021         public Dynamic addFilter(String filterName, String className)
2022         {
2023             LOG.warn(__unimplmented);
2024             return null;
2025         }
2026 
2027         @Override
2028         public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, Class<? extends Servlet> servletClass)
2029         {
2030             LOG.warn(__unimplmented);
2031             return null;
2032         }
2033 
2034         @Override
2035         public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet)
2036         {
2037             LOG.warn(__unimplmented);
2038             return null;
2039         }
2040 
2041         @Override
2042         public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, String className)
2043         {
2044             LOG.warn(__unimplmented);
2045             return null;
2046         }
2047 
2048         @Override
2049         public <T extends Filter> T createFilter(Class<T> c) throws ServletException
2050         {
2051             LOG.warn(__unimplmented);
2052             return null;
2053         }
2054 
2055         @Override
2056         public <T extends Servlet> T createServlet(Class<T> c) throws ServletException
2057         {
2058             LOG.warn(__unimplmented);
2059             return null;
2060         }
2061 
2062         @Override
2063         public Set<SessionTrackingMode> getDefaultSessionTrackingModes()
2064         {
2065             LOG.warn(__unimplmented);
2066             return null;
2067         }
2068 
2069         @Override
2070         public Set<SessionTrackingMode> getEffectiveSessionTrackingModes()
2071         {
2072             LOG.warn(__unimplmented);
2073             return null;
2074         }
2075 
2076         @Override
2077         public FilterRegistration getFilterRegistration(String filterName)
2078         {
2079             LOG.warn(__unimplmented);
2080             return null;
2081         }
2082 
2083         @Override
2084         public Map<String, ? extends FilterRegistration> getFilterRegistrations()
2085         {
2086             LOG.warn(__unimplmented);
2087             return null;
2088         }
2089 
2090         @Override
2091         public ServletRegistration getServletRegistration(String servletName)
2092         {
2093             LOG.warn(__unimplmented);
2094             return null;
2095         }
2096 
2097         @Override
2098         public Map<String, ? extends ServletRegistration> getServletRegistrations()
2099         {
2100             LOG.warn(__unimplmented);
2101             return null;
2102         }
2103 
2104         @Override
2105         public SessionCookieConfig getSessionCookieConfig()
2106         {
2107             LOG.warn(__unimplmented);
2108             return null;
2109         }
2110 
2111         @Override
2112         public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes)
2113         {
2114             LOG.warn(__unimplmented);
2115         }
2116 
2117         @Override
2118         public void addListener(String className)
2119         {
2120             if (!_enabled)
2121                 throw new UnsupportedOperationException();
2122             
2123             try
2124             {
2125                 Class<? extends EventListener> clazz = _classLoader==null?Loader.loadClass(ContextHandler.class,className):_classLoader.loadClass(className);
2126                 addListener(clazz);
2127             }
2128             catch (ClassNotFoundException e)
2129             {
2130                 throw new IllegalArgumentException(e);
2131             }
2132         }
2133 
2134         @Override
2135         public <T extends EventListener> void addListener(T t)
2136         {            
2137             if (!_enabled)
2138                 throw new UnsupportedOperationException();
2139             ContextHandler.this.addEventListener(t);
2140         }
2141 
2142         @Override
2143         public void addListener(Class<? extends EventListener> listenerClass)
2144         {            
2145             if (!_enabled)
2146                 throw new UnsupportedOperationException();
2147 
2148             try
2149             {
2150                 EventListener e = createListener(listenerClass);
2151                 ContextHandler.this.addEventListener(e);
2152                 ContextHandler.this.restrictEventListener(e);
2153             }
2154             catch (ServletException e)
2155             {
2156                 throw new IllegalArgumentException(e);
2157             }
2158         }
2159 
2160         @Override
2161         public <T extends EventListener> T createListener(Class<T> clazz) throws ServletException
2162         {
2163             try
2164             {
2165                 return clazz.newInstance();
2166             }
2167             catch (InstantiationException e)
2168             {
2169                 throw new ServletException(e);
2170             }
2171             catch (IllegalAccessException e)
2172             {
2173                 throw new ServletException(e);
2174             }
2175         }
2176 
2177         @Override
2178         public ClassLoader getClassLoader()
2179         {
2180             AccessController.checkPermission(new RuntimePermission("getClassLoader"));
2181             return _classLoader;
2182         }
2183 
2184         @Override
2185         public int getEffectiveMajorVersion()
2186         {
2187             return _majorVersion;
2188         }
2189 
2190         @Override
2191         public int getEffectiveMinorVersion()
2192         {
2193             return _minorVersion;
2194         }
2195 
2196         public void setEffectiveMajorVersion (int v)
2197         {
2198             _majorVersion = v;
2199         }
2200         
2201         public void setEffectiveMinorVersion (int v)
2202         {
2203             _minorVersion = v;
2204         }
2205         
2206         @Override
2207         public JspConfigDescriptor getJspConfigDescriptor()
2208         {
2209             return null;
2210         }
2211 
2212         @Override
2213         public void declareRoles(String... roleNames)
2214         {
2215             if (!isStarting())
2216                 throw new IllegalStateException ();
2217             if (!_enabled)
2218                 throw new UnsupportedOperationException();
2219             
2220             // TODO Auto-generated method stub
2221             
2222         }
2223 
2224         public void setEnabled(boolean enabled)
2225         {
2226             _enabled = enabled;
2227         }
2228 
2229         public boolean isEnabled()
2230         {
2231             return _enabled;
2232         }
2233     }
2234 
2235     private static class CLDump implements Dumpable
2236     {
2237         final ClassLoader _loader;
2238 
2239         CLDump(ClassLoader loader)
2240         {
2241             _loader = loader;
2242         }
2243 
2244         public String dump()
2245         {
2246             return AggregateLifeCycle.dump(this);
2247         }
2248 
2249         public void dump(Appendable out, String indent) throws IOException
2250         {
2251             out.append(String.valueOf(_loader)).append("\n");
2252 
2253             if (_loader != null)
2254             {
2255                 Object parent = _loader.getParent();
2256                 if (parent != null)
2257                 {
2258                     if (!(parent instanceof Dumpable))
2259                         parent = new CLDump((ClassLoader)parent);
2260 
2261                     if (_loader instanceof URLClassLoader)
2262                         AggregateLifeCycle.dump(out,indent,TypeUtil.asList(((URLClassLoader)_loader).getURLs()),Collections.singleton(parent));
2263                     else
2264                         AggregateLifeCycle.dump(out,indent,Collections.singleton(parent));
2265                 }
2266             }
2267         }
2268 
2269     }
2270 }