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