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