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