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