View Javadoc

1   //========================================================================
2   //$Id: ContextHandler.java,v 1.16 2005/11/17 11:19:45 gregwilkins Exp $
3   //Copyright 2004-2005 Mort Bay Consulting Pty. Ltd.
4   //------------------------------------------------------------------------
5   //Licensed under the Apache License, Version 2.0 (the "License");
6   //you may not use this file except in compliance with the License.
7   //You may obtain a copy of the License at 
8   //http://www.apache.org/licenses/LICENSE-2.0
9   //Unless required by applicable law or agreed to in writing, software
10  //distributed under the License is distributed on an "AS IS" BASIS,
11  //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  //See the License for the specific language governing permissions and
13  //limitations under the License.
14  //========================================================================
15  
16  package org.mortbay.jetty.handler;
17  
18  import java.io.File;
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.net.MalformedURLException;
22  import java.net.URL;
23  import java.net.URLClassLoader;
24  import java.util.Arrays;
25  import java.util.Collections;
26  import java.util.Enumeration;
27  import java.util.EventListener;
28  import java.util.HashMap;
29  import java.util.HashSet;
30  import java.util.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.mortbay.io.Buffer;
49  import org.mortbay.jetty.Handler;
50  import org.mortbay.jetty.HandlerContainer;
51  import org.mortbay.jetty.HttpConnection;
52  import org.mortbay.jetty.HttpException;
53  import org.mortbay.jetty.MimeTypes;
54  import org.mortbay.jetty.Request;
55  import org.mortbay.jetty.Server;
56  import org.mortbay.jetty.webapp.WebAppClassLoader;
57  import org.mortbay.log.Log;
58  import org.mortbay.log.Logger;
59  import org.mortbay.resource.Resource;
60  import org.mortbay.util.Attributes;
61  import org.mortbay.util.AttributesMap;
62  import org.mortbay.util.LazyList;
63  import org.mortbay.util.Loader;
64  import org.mortbay.util.URIUtil;
65  
66  /* ------------------------------------------------------------ */
67  /** ContextHandler.
68   * 
69   * This handler wraps a call to handle by setting the context and
70   * servlet path, plus setting the context classloader.
71   * 
72   * <p>
73   * If the context init parameter "org.mortbay.jetty.servlet.ManagedAttributes"
74   * is set to a coma separated list of names, then they are treated as context
75   * attribute names, which if set as attributes are passed to the servers Container
76   * so that they may be managed with JMX.
77   * 
78   * @org.apache.xbean.XBean description="Creates a basic HTTP context"
79   *
80   * @author gregw
81   *
82   */
83  public class ContextHandler extends HandlerWrapper implements Attributes, Server.Graceful
84  {
85      private static ThreadLocal __context=new ThreadLocal();
86      public static final String MANAGED_ATTRIBUTES = "org.mortbay.jetty.servlet.ManagedAttributes";
87      
88      /* ------------------------------------------------------------ */
89      /** Get the current ServletContext implementation.
90       * This call is only valid during a call to doStart and is available to
91       * nested handlers to access the context.
92       * 
93       * @return ServletContext implementation
94       */
95      public static SContext getCurrentContext()
96      {
97          SContext context = (SContext)__context.get();
98          return context;
99      }
100 
101     protected SContext _scontext;
102     
103     private AttributesMap _attributes;
104     private AttributesMap _contextAttributes;
105     private ClassLoader _classLoader;
106     private String _contextPath="/";
107     private Map _initParams;
108     private String _displayName;
109     private Resource _baseResource;  
110     private MimeTypes _mimeTypes;
111     private Map _localeEncodingMap;
112     private String[] _welcomeFiles;
113     private ErrorHandler _errorHandler;
114     private String[] _vhosts;
115     private Set _connectors;
116     private EventListener[] _eventListeners;
117     private Logger _logger;
118     private boolean _shutdown;
119     private boolean _allowNullPathInfo;
120     private int _maxFormContentSize=Integer.getInteger("org.mortbay.jetty.Request.maxFormContentSize",200000).intValue();
121     private boolean _compactPath=false;
122 
123     private Object _contextListeners;
124     private Object _contextAttributeListeners;
125     private Object _requestListeners;
126     private Object _requestAttributeListeners;
127     private Set _managedAttributes;
128     
129     /* ------------------------------------------------------------ */
130     /**
131      * 
132      */
133     public ContextHandler()
134     {
135         super();
136         _scontext=new SContext();
137         _attributes=new AttributesMap();
138         _initParams=new HashMap();
139     }
140     
141     /* ------------------------------------------------------------ */
142     /**
143      * 
144      */
145     protected ContextHandler(SContext context)
146     {
147         super();
148         _scontext=context;
149         _attributes=new AttributesMap();
150         _initParams=new HashMap();
151     }
152     
153     /* ------------------------------------------------------------ */
154     /**
155      * 
156      */
157     public ContextHandler(String contextPath)
158     {
159         this();
160         setContextPath(contextPath);
161     }
162     
163     /* ------------------------------------------------------------ */
164     /**
165      * 
166      */
167     public ContextHandler(HandlerContainer parent, String contextPath)
168     {
169         this();
170         setContextPath(contextPath);
171         parent.addHandler(this);
172     }
173 
174     /* ------------------------------------------------------------ */
175     public SContext getServletContext()
176     {
177         return _scontext;
178     }
179     
180     /* ------------------------------------------------------------ */
181     /**
182      * @return the allowNullPathInfo true if /context is not redirected to /context/
183      */
184     public boolean getAllowNullPathInfo()
185     {
186         return _allowNullPathInfo;
187     }
188 
189     /* ------------------------------------------------------------ */
190     /**
191      * @param allowNullPathInfo  true if /context is not redirected to /context/
192      */
193     public void setAllowNullPathInfo(boolean allowNullPathInfo)
194     {
195         _allowNullPathInfo=allowNullPathInfo;
196     }
197 
198     /* ------------------------------------------------------------ */
199     public void setServer(Server server)
200     {
201         if (_errorHandler!=null)
202         {
203             Server old_server=getServer();
204             if (old_server!=null && old_server!=server)
205                 old_server.getContainer().update(this, _errorHandler, null, "error",true);
206             super.setServer(server); 
207             if (server!=null && server!=old_server)
208                 server.getContainer().update(this, null, _errorHandler, "error",true);
209             _errorHandler.setServer(server); 
210         }
211         else
212             super.setServer(server); 
213     }
214 
215     /* ------------------------------------------------------------ */
216     /** Set the virtual hosts for the context.
217      * Only requests that have a matching host header or fully qualified
218      * URL will be passed to that context with a virtual host name.
219      * A context with no virtual host names or a null virtual host name is
220      * available to all requests that are not served by a context with a
221      * matching virtual host name.
222      * @param vhosts Array of virtual hosts that this context responds to. A
223      * null host name or null/empty array means any hostname is acceptable.
224      * Host names may be String representation of IP addresses. Host names may
225      * start with '*.' to wildcard one level of names.
226      */
227     public void setVirtualHosts( String[] vhosts )
228     {
229         if ( vhosts == null )
230         {
231             _vhosts = vhosts;
232         } 
233         else 
234         {
235             _vhosts = new String[vhosts.length];
236             for ( int i = 0; i < vhosts.length; i++ )
237                 _vhosts[i] = normalizeHostname( vhosts[i]);
238         }
239     }
240 
241     /* ------------------------------------------------------------ */
242     /** Get the virtual hosts for the context.
243      * Only requests that have a matching host header or fully qualified
244      * URL will be passed to that context with a virtual host name.
245      * A context with no virtual host names or a null virtual host name is
246      * available to all requests that are not served by a context with a
247      * matching virtual host name.
248      * @return Array of virtual hosts that this context responds to. A
249      * null host name or empty array means any hostname is acceptable.
250      * Host names may be String representation of IP addresses.
251      * Host names may start with '*.' to wildcard one level of names.
252      */
253     public String[] getVirtualHosts()
254     {
255         return _vhosts;
256     }
257 
258     /* ------------------------------------------------------------ */
259     /** 
260      * @deprecated use {@link #setConnectorNames(String[])} 
261      */
262     public void setHosts(String[] hosts)
263     {
264         setConnectorNames(hosts);
265     }
266 
267     /* ------------------------------------------------------------ */
268     /** Get the hosts for the context.
269      * @deprecated
270      */
271     public String[] getHosts()
272     {
273         return getConnectorNames();
274     }
275 
276     /* ------------------------------------------------------------ */
277     /**
278      * @return an array of connector names that this context
279      * will accept a request from.
280      */
281     public String[] getConnectorNames()
282     {
283         if (_connectors==null || _connectors.size()==0)
284             return null;
285             
286         return (String[])_connectors.toArray(new String[_connectors.size()]);
287     }
288 
289     /* ------------------------------------------------------------ */
290     /** Set the names of accepted connectors.
291      * 
292      * Names are either "host:port" or a specific configured name for a connector.
293      * 
294      * @param connectors If non null, an array of connector names that this context
295      * will accept a request from.
296      */
297     public void setConnectorNames(String[] connectors)
298     {
299         if (connectors==null || connectors.length==0)
300             _connectors=null;
301         else
302             _connectors= new HashSet(Arrays.asList(connectors));
303     }
304     
305     /* ------------------------------------------------------------ */
306     /* 
307      * @see javax.servlet.ServletContext#getAttribute(java.lang.String)
308      */
309     public Object getAttribute(String name)
310     {
311         return _attributes.getAttribute(name);
312     }
313 
314     /* ------------------------------------------------------------ */
315     /* 
316      * @see javax.servlet.ServletContext#getAttributeNames()
317      */
318     public Enumeration getAttributeNames()
319     {
320         return AttributesMap.getAttributeNamesCopy(_attributes);
321     }
322     
323     /* ------------------------------------------------------------ */
324     /**
325      * @return Returns the attributes.
326      */
327     public Attributes getAttributes()
328     {
329         return _attributes;
330     }
331     
332     /* ------------------------------------------------------------ */
333     /**
334      * @return Returns the classLoader.
335      */
336     public ClassLoader getClassLoader()
337     {
338         return _classLoader;
339     }
340 
341     /* ------------------------------------------------------------ */
342     /**
343      * Make best effort to extract a file classpath from the context classloader
344      * @return Returns the classLoader.
345      */
346     public String getClassPath()
347     {
348         if ( _classLoader==null || !(_classLoader instanceof URLClassLoader))
349             return null;
350         URLClassLoader loader = (URLClassLoader)_classLoader;
351         URL[] urls =loader.getURLs();
352         StringBuffer classpath=new StringBuffer();
353         for (int i=0;i<urls.length;i++)
354         {
355             try
356             {
357                 Resource resource = Resource.newResource(urls[i]);
358                 File file=resource.getFile();
359                 if (file.exists())
360                 {
361                     if (classpath.length()>0)
362                         classpath.append(File.pathSeparatorChar);
363                     classpath.append(file.getAbsolutePath());
364                 }
365             }
366             catch (IOException e)
367             {
368                 Log.debug(e);
369             }
370         }
371         if (classpath.length()==0)
372             return null;
373         return classpath.toString();
374     }
375 
376     /* ------------------------------------------------------------ */
377     /**
378      * @return Returns the _contextPath.
379      */
380     public String getContextPath()
381     {
382         return _contextPath;
383     }
384    
385     /* ------------------------------------------------------------ */
386     /* 
387      * @see javax.servlet.ServletContext#getInitParameter(java.lang.String)
388      */
389     public String getInitParameter(String name)
390     {
391         return (String)_initParams.get(name);
392     }
393 
394     /* ------------------------------------------------------------ */
395     /* 
396      * @see javax.servlet.ServletContext#getInitParameterNames()
397      */
398     public Enumeration getInitParameterNames()
399     {
400         return Collections.enumeration(_initParams.keySet());
401     }
402     
403     /* ------------------------------------------------------------ */
404     /**
405      * @return Returns the initParams.
406      */
407     public Map getInitParams()
408     {
409         return _initParams;
410     }
411 
412     /* ------------------------------------------------------------ */
413     /* 
414      * @see javax.servlet.ServletContext#getServletContextName()
415      */
416     public String getDisplayName()
417     {
418         return _displayName;
419     }
420 
421     /* ------------------------------------------------------------ */
422     public EventListener[] getEventListeners()
423     {
424         return _eventListeners;
425     }
426     
427     /* ------------------------------------------------------------ */
428     public void setEventListeners(EventListener[] eventListeners)
429     {
430         _contextListeners=null;
431         _contextAttributeListeners=null;
432         _requestListeners=null;
433         _requestAttributeListeners=null;
434         
435         _eventListeners=eventListeners;
436         
437         for (int i=0; eventListeners!=null && i<eventListeners.length;i ++)
438         {
439             EventListener listener = _eventListeners[i];
440             
441             if (listener instanceof ServletContextListener)
442                 _contextListeners= LazyList.add(_contextListeners, listener);
443             
444             if (listener instanceof ServletContextAttributeListener)
445                 _contextAttributeListeners= LazyList.add(_contextAttributeListeners, listener);
446             
447             if (listener instanceof ServletRequestListener)
448                 _requestListeners= LazyList.add(_requestListeners, listener);
449             
450             if (listener instanceof ServletRequestAttributeListener)
451                 _requestAttributeListeners= LazyList.add(_requestAttributeListeners, listener);
452         }
453     }     
454 
455     /* ------------------------------------------------------------ */
456     public void addEventListener(EventListener listener) 
457     {
458         setEventListeners((EventListener[])LazyList.addToArray(getEventListeners(), listener, EventListener.class));
459     }
460 
461     /* ------------------------------------------------------------ */
462     /**
463      * @return true if this context is accepting new requests
464      */
465     public boolean isShutdown()
466     {
467         return !_shutdown;
468     }
469 
470     /* ------------------------------------------------------------ */
471     /** Set shutdown status.
472      * This field allows for graceful shutdown of a context. A started context may be put into non accepting state so
473      * that existing requests can complete, but no new requests are accepted.
474      * @param accepting true if this context is accepting new requests
475      */
476     public void setShutdown(boolean shutdown)
477     {
478         _shutdown = shutdown;
479     }
480     
481     /* ------------------------------------------------------------ */
482     /* 
483      * @see org.mortbay.thread.AbstractLifeCycle#doStart()
484      */
485     protected void doStart() throws Exception
486     {
487         if (_contextPath==null)
488             throw new IllegalStateException("Null contextPath");
489         
490         _logger=Log.getLogger(getDisplayName()==null?getContextPath():getDisplayName());
491         ClassLoader old_classloader=null;
492         Thread current_thread=null;
493         SContext old_context=null;
494 
495         _contextAttributes=new AttributesMap();
496         try
497         {
498             
499             // Set the classloader
500             if (_classLoader!=null)
501             {
502                 current_thread=Thread.currentThread();
503                 old_classloader=current_thread.getContextClassLoader();
504                 current_thread.setContextClassLoader(_classLoader);
505             }
506             
507 
508             if (_mimeTypes==null)
509                 _mimeTypes=new MimeTypes();
510             
511             old_context=(SContext)__context.get();
512             __context.set(_scontext);
513             
514             if (_errorHandler==null)
515                 setErrorHandler(new ErrorHandler());
516             
517             startContext();
518             
519            
520         }
521         finally
522         {
523             __context.set(old_context);
524             
525             // reset the classloader
526             if (_classLoader!=null)
527             {
528                 current_thread.setContextClassLoader(old_classloader);
529             }
530         }
531     }
532 
533     /* ------------------------------------------------------------ */
534     protected void startContext()
535     	throws Exception
536     {
537         super.doStart();
538 
539         if (_errorHandler!=null)
540             _errorHandler.start();
541         
542         // Context listeners
543         if (_contextListeners != null )
544         {
545             ServletContextEvent event= new ServletContextEvent(_scontext);
546             for (int i= 0; i < LazyList.size(_contextListeners); i++)
547             {
548                 ((ServletContextListener)LazyList.get(_contextListeners, i)).contextInitialized(event);
549             }
550         }
551 
552         String managedAttributes = (String)_initParams.get(MANAGED_ATTRIBUTES);
553         if (managedAttributes!=null)
554         {
555             _managedAttributes=new HashSet();
556             String[] attributes = managedAttributes.toString().split(",");
557 	    for (int  i=0;i<attributes.length;i++)
558                 _managedAttributes.add(attributes[i]);
559 
560             Enumeration e = _scontext.getAttributeNames();
561             while(e.hasMoreElements())
562             {
563                 String name = (String)e.nextElement();
564                 Object value = _scontext.getAttribute(name);
565                 setManagedAttribute(name,value);
566             }
567         }       
568     }
569     
570     /* ------------------------------------------------------------ */
571     /* 
572      * @see org.mortbay.thread.AbstractLifeCycle#doStop()
573      */
574     protected void doStop() throws Exception
575     {
576         ClassLoader old_classloader=null;
577         Thread current_thread=null;
578 
579         SContext old_context=(SContext)__context.get();
580         __context.set(_scontext);
581         try
582         {
583             // Set the classloader
584             if (_classLoader!=null)
585             {
586                 current_thread=Thread.currentThread();
587                 old_classloader=current_thread.getContextClassLoader();
588                 current_thread.setContextClassLoader(_classLoader);
589             }
590             
591             super.doStop();
592             
593             // Context listeners
594             if (_contextListeners != null )
595             {
596                 ServletContextEvent event= new ServletContextEvent(_scontext);
597                 for (int i=LazyList.size(_contextListeners); i-->0;)
598                 {
599                     ((ServletContextListener)LazyList.get(_contextListeners, i)).contextDestroyed(event);
600                 }
601             }
602 
603             if (_errorHandler!=null)
604                 _errorHandler.stop();
605             
606             Enumeration e = _scontext.getAttributeNames();
607             while(e.hasMoreElements())
608             {
609                 String name = (String)e.nextElement();
610                 setManagedAttribute(name,null);
611             }
612         }
613         finally
614         {
615             __context.set(old_context);
616             // reset the classloader
617             if (_classLoader!=null)
618                 current_thread.setContextClassLoader(old_classloader);
619         }
620 
621         if (_contextAttributes!=null)
622             _contextAttributes.clearAttributes();
623         _contextAttributes=null;
624     }
625     
626     /* ------------------------------------------------------------ */
627     /* 
628      * @see org.mortbay.jetty.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
629      */
630     public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch)
631             throws IOException, ServletException
632     {   
633         boolean new_context=false;
634         SContext old_context=null;
635         String old_context_path=null;
636         String old_servlet_path=null;
637         String old_path_info=null;
638         ClassLoader old_classloader=null;
639         Thread current_thread=null;
640         
641         Request base_request=(request instanceof Request)?(Request)request:HttpConnection.getCurrentConnection().getRequest();
642         if( !isStarted() || _shutdown || (dispatch==REQUEST && base_request.isHandled()))
643             return;
644         
645         old_context=base_request.getContext();
646         
647         // Are we already in this context?
648         if (old_context!=_scontext)
649         {
650             new_context=true;
651             
652             // Check the vhosts
653             if (_vhosts!=null && _vhosts.length>0)
654             {
655                 String vhost = normalizeHostname( request.getServerName());
656 
657                 boolean match=false;
658                 
659                 // TODO non-linear lookup
660                 for (int i=0;!match && i<_vhosts.length;i++)
661                 {
662                     String contextVhost = _vhosts[i];
663                     if(contextVhost==null) continue;
664                     if(contextVhost.startsWith("*.")) {
665                         // wildcard only at the beginning, and only for one additional subdomain level
666                         match=contextVhost.regionMatches(true,2,vhost,vhost.indexOf(".")+1,contextVhost.length()-2);
667                     } else
668                         match=contextVhost.equalsIgnoreCase(vhost);
669                 }
670                 if (!match)
671                     return;
672             }
673             
674             // Check the connector
675             if (_connectors!=null && _connectors.size()>0)
676             {
677                 String connector=HttpConnection.getCurrentConnection().getConnector().getName();
678                 if (connector==null || !_connectors.contains(connector))
679                     return;
680             }
681             
682             // Nope - so check the target.
683             if (dispatch==REQUEST)
684             {
685                 if (_compactPath)
686                     target=URIUtil.compactPath(target);
687                 
688                 if (target.equals(_contextPath))
689                 {
690                     if (!_allowNullPathInfo && !target.endsWith(URIUtil.SLASH))
691                     {
692                         base_request.setHandled(true);
693                         if (request.getQueryString()!=null)
694                             response.sendRedirect(URIUtil.addPaths(request.getRequestURI(),URIUtil.SLASH)+"?"+request.getQueryString());
695                         else 
696                             response.sendRedirect(URIUtil.addPaths(request.getRequestURI(),URIUtil.SLASH));
697                         return;
698                     }
699                     if (_contextPath.length()>1)
700                     {
701                         target=URIUtil.SLASH;
702                         request.setAttribute("org.mortbay.jetty.nullPathInfo",target);
703                     }
704                 }
705                 else if (target.startsWith(_contextPath) && (_contextPath.length()==1 || target.charAt(_contextPath.length())=='/'))
706                 {
707                     if (_contextPath.length()>1)
708                         target=target.substring(_contextPath.length());
709                 }
710                 else 
711                 {
712                     // Not for this context!
713                     return;
714                 }
715             }
716         }
717         
718         try
719         {
720             old_context_path=base_request.getContextPath();
721             old_servlet_path=base_request.getServletPath();
722             old_path_info=base_request.getPathInfo();
723             
724             // Update the paths
725             base_request.setContext(_scontext);
726             if (dispatch!=INCLUDE && target.startsWith("/"))
727             {
728                 if (_contextPath.length()==1)
729                     base_request.setContextPath("");
730                 else
731                     base_request.setContextPath(_contextPath);
732                 base_request.setServletPath(null);
733                 base_request.setPathInfo(target);
734             }
735 
736             ServletRequestEvent event=null;
737             if (new_context)
738             {
739                 // Set the classloader
740                 if (_classLoader!=null)
741                 {
742                     current_thread=Thread.currentThread();
743                     old_classloader=current_thread.getContextClassLoader();
744                     current_thread.setContextClassLoader(_classLoader);
745                 }
746                 
747                 // Handle the REALLY SILLY request events!
748                 if (_requestListeners!=null)
749                 {
750                     event = new ServletRequestEvent(_scontext,request);
751                     for(int i=0;i<LazyList.size(_requestListeners);i++)
752                         ((ServletRequestListener)LazyList.get(_requestListeners,i)).requestInitialized(event);
753                 }
754                 for(int i=0;i<LazyList.size(_requestAttributeListeners);i++)
755                     base_request.addEventListener(((EventListener)LazyList.get(_requestAttributeListeners,i)));
756             }
757             
758             // Handle the request
759             try
760             {
761                 if (dispatch==REQUEST && isProtectedTarget(target))
762                     throw new HttpException(HttpServletResponse.SC_NOT_FOUND);
763                 
764                 Handler handler = getHandler();
765                 if (handler!=null)
766                     handler.handle(target, request, response, dispatch);
767             }
768             catch(HttpException e)
769             {
770                 Log.debug(e);
771                 response.sendError(e.getStatus(), e.getReason());
772             }
773             finally
774             {
775                 // Handle more REALLY SILLY request events!
776                 if (new_context)
777                 {
778                     for(int i=LazyList.size(_requestListeners);i-->0;)
779                         ((ServletRequestListener)LazyList.get(_requestListeners,i)).requestDestroyed(event);
780                     
781                     for(int i=0;i<LazyList.size(_requestAttributeListeners);i++)
782                         base_request.removeEventListener(((EventListener)LazyList.get(_requestAttributeListeners,i)));
783                 }
784             }
785         }
786         finally
787         {
788             if (old_context!=_scontext)
789             {
790                 // reset the classloader
791                 if (_classLoader!=null)
792                 {
793                     current_thread.setContextClassLoader(old_classloader);
794                 }
795                 
796                 // reset the context and servlet path.
797                 base_request.setContext(old_context);
798                 base_request.setContextPath(old_context_path);
799                 base_request.setServletPath(old_servlet_path);
800                 base_request.setPathInfo(old_path_info); 
801             }
802         }
803     }
804 
805     /* ------------------------------------------------------------ */
806     /** Check the target.
807      * Called by {@link #handle(String, HttpServletRequest, HttpServletResponse, int)} when a
808      * target within a context is determined.  If the target is protected, 404 is returned.
809      * The default implementation always returns false.
810      * @see org.mortbay.jetty.webapp.WebAppContext#isProtectedTarget(String)
811      */
812     /* ------------------------------------------------------------ */
813     protected boolean isProtectedTarget(String target)
814     { 
815         return false;
816     }
817 
818     /* ------------------------------------------------------------ */
819     /* 
820      * @see javax.servlet.ServletContext#removeAttribute(java.lang.String)
821      */
822     public void removeAttribute(String name)
823     {
824         setManagedAttribute(name,null);
825         _attributes.removeAttribute(name);
826     }
827 
828     /* ------------------------------------------------------------ */
829     /* Set a context attribute.
830      * Attributes set via this API cannot be overriden by the ServletContext.setAttribute API.
831      * Their lifecycle spans the stop/start of a context.  No attribute listener events are 
832      * triggered by this API.
833      * @see javax.servlet.ServletContext#setAttribute(java.lang.String, java.lang.Object)
834      */
835     public void setAttribute(String name, Object value)
836     {
837         setManagedAttribute(name,value);
838         _attributes.setAttribute(name,value);
839     }
840     
841     /* ------------------------------------------------------------ */
842     /**
843      * @param attributes The attributes to set.
844      */
845     public void setAttributes(Attributes attributes)
846     {
847         if (attributes instanceof AttributesMap)
848         {
849             _attributes = (AttributesMap)attributes;
850             Enumeration e = _attributes.getAttributeNames();
851             while (e.hasMoreElements())
852             {
853                 String name = (String)e.nextElement();
854                 setManagedAttribute(name,attributes.getAttribute(name));
855             }
856         }
857         else
858         {
859             _attributes=new AttributesMap();
860             Enumeration e = attributes.getAttributeNames();
861             while (e.hasMoreElements())
862             {
863                 String name = (String)e.nextElement();
864                 Object value=attributes.getAttribute(name);
865                 setManagedAttribute(name,value);
866                 _attributes.setAttribute(name,value);
867             }
868         }
869     }
870 
871     /* ------------------------------------------------------------ */
872     public void clearAttributes()
873     {
874         Enumeration e = _attributes.getAttributeNames();
875         while (e.hasMoreElements())
876         {
877             String name = (String)e.nextElement();
878             setManagedAttribute(name,null);
879         }
880         _attributes.clearAttributes();
881     }
882 
883     /* ------------------------------------------------------------ */
884     private void setManagedAttribute(String name, Object value)
885     {   
886         if (_managedAttributes!=null && _managedAttributes.contains(name))
887         {
888             Object o =_scontext.getAttribute(name);
889             if (o!=null)
890                 getServer().getContainer().removeBean(o);
891             if (value!=null)
892                 getServer().getContainer().addBean(value);
893         }
894     }
895     
896     /* ------------------------------------------------------------ */
897     /**
898      * @param classLoader The classLoader to set.
899      */
900     public void setClassLoader(ClassLoader classLoader)
901     {
902         _classLoader = classLoader;
903     }
904     
905     /* ------------------------------------------------------------ */
906     /**
907      * @param contextPath The _contextPath to set.
908      */
909     public void setContextPath(String contextPath)
910     {
911         if (contextPath!=null && contextPath.length()>1 && contextPath.endsWith("/"))
912             throw new IllegalArgumentException("ends with /");
913         _contextPath = contextPath;
914         
915         if (getServer()!=null && (getServer().isStarting() || getServer().isStarted()))
916         {
917             Handler[] contextCollections = getServer().getChildHandlersByClass(ContextHandlerCollection.class);
918             for (int h=0;contextCollections!=null&& h<contextCollections.length;h++)
919                 ((ContextHandlerCollection)contextCollections[h]).mapContexts();
920         }
921     }
922     
923     /* ------------------------------------------------------------ */
924     /**
925      * @param initParams The initParams to set.
926      */
927     public void setInitParams(Map initParams)
928     {
929         if (initParams == null)
930             return;
931         _initParams = new HashMap(initParams);
932     }
933     
934     /* ------------------------------------------------------------ */
935     /**
936      * @param servletContextName The servletContextName to set.
937      */
938     public void setDisplayName(String servletContextName)
939     {
940         _displayName = servletContextName;
941         if (_classLoader!=null && _classLoader instanceof WebAppClassLoader)
942             ((WebAppClassLoader)_classLoader).setName(servletContextName);
943     }
944     
945     /* ------------------------------------------------------------ */
946     /**
947      * @return Returns the resourceBase.
948      */
949     public Resource getBaseResource()
950     {
951         if (_baseResource==null)
952             return null;
953         return _baseResource;
954     }
955 
956     /* ------------------------------------------------------------ */
957     /**
958      * @return Returns the base resource as a string.
959      */
960     public String getResourceBase()
961     {
962         if (_baseResource==null)
963             return null;
964         return _baseResource.toString();
965     }
966     
967     /* ------------------------------------------------------------ */
968     /**
969      * @param base The resourceBase to set.
970      */
971     public void setBaseResource(Resource base) 
972     {
973         _baseResource=base;
974     }
975 
976     /* ------------------------------------------------------------ */
977     /**
978      * @param resourceBase The base resource as a string.
979      */
980     public void setResourceBase(String resourceBase) 
981     {
982         try
983         {
984             setBaseResource(Resource.newResource(resourceBase));
985         }
986         catch (Exception e)
987         {
988             Log.warn(e);
989             throw new IllegalArgumentException(resourceBase);
990         }
991     }
992 
993     /* ------------------------------------------------------------ */
994     /**
995      * @return Returns the mimeTypes.
996      */
997     public MimeTypes getMimeTypes()
998     {
999         return _mimeTypes;
1000     }
1001     
1002     /* ------------------------------------------------------------ */
1003     /**
1004      * @param mimeTypes The mimeTypes to set.
1005      */
1006     public void setMimeTypes(MimeTypes mimeTypes)
1007     {
1008         _mimeTypes = mimeTypes;
1009     }
1010 
1011     /* ------------------------------------------------------------ */
1012     /**
1013      */
1014     public void setWelcomeFiles(String[] files) 
1015     {
1016         _welcomeFiles=files;
1017     }
1018 
1019     /* ------------------------------------------------------------ */
1020     /**
1021      * @return The names of the files which the server should consider to be welcome files in this context.
1022      * @see <a href="http://jcp.org/aboutJava/communityprocess/final/jsr154/index.html">The Servlet Specification</a>
1023      * @see #setWelcomeFiles
1024      */
1025     public String[] getWelcomeFiles() 
1026     {
1027         return _welcomeFiles;
1028     }
1029 
1030     /* ------------------------------------------------------------ */
1031     /**
1032      * @return Returns the errorHandler.
1033      */
1034     public ErrorHandler getErrorHandler()
1035     {
1036         return _errorHandler;
1037     }
1038 
1039     /* ------------------------------------------------------------ */
1040     /**
1041      * @param errorHandler The errorHandler to set.
1042      */
1043     public void setErrorHandler(ErrorHandler errorHandler)
1044     {
1045         if (errorHandler!=null)
1046             errorHandler.setServer(getServer());
1047         if (getServer()!=null)
1048             getServer().getContainer().update(this, _errorHandler, errorHandler, "errorHandler",true);
1049         _errorHandler = errorHandler;
1050     }
1051     
1052     /* ------------------------------------------------------------ */
1053     public int getMaxFormContentSize()
1054     {
1055         return _maxFormContentSize;
1056     }
1057     
1058     /* ------------------------------------------------------------ */
1059     public void setMaxFormContentSize(int maxSize)
1060     {
1061         _maxFormContentSize=maxSize;
1062     }
1063 
1064 
1065     /* ------------------------------------------------------------ */
1066     /**
1067      * @return True if URLs are compacted to replace multiple '/'s with a single '/'
1068      */
1069     public boolean isCompactPath()
1070     {
1071         return _compactPath;
1072     }
1073 
1074     /* ------------------------------------------------------------ */
1075     /**
1076      * @param compactPath True if URLs are compacted to replace multiple '/'s with a single '/'
1077      */
1078     public void setCompactPath(boolean compactPath)
1079     {
1080         _compactPath=compactPath;
1081     }
1082 
1083     /* ------------------------------------------------------------ */
1084     public String toString()
1085     {
1086         
1087         return this.getClass().getName()+"@"+Integer.toHexString(hashCode())+"{"+getContextPath()+","+getBaseResource()+"}";
1088     }
1089 
1090     /* ------------------------------------------------------------ */
1091     public synchronized Class loadClass(String className)
1092         throws ClassNotFoundException
1093     {
1094         if (className==null)
1095             return null;
1096         
1097         if (_classLoader==null)
1098             return Loader.loadClass(this.getClass(), className);
1099 
1100         return _classLoader.loadClass(className);
1101     }
1102     
1103 
1104     /* ------------------------------------------------------------ */
1105     public void addLocaleEncoding(String locale,String encoding)
1106     {
1107         if (_localeEncodingMap==null)
1108             _localeEncodingMap=new HashMap();
1109         _localeEncodingMap.put(locale, encoding);
1110     }
1111     
1112     /* ------------------------------------------------------------ */
1113     /**
1114      * Get the character encoding for a locale. The full locale name is first
1115      * looked up in the map of encodings. If no encoding is found, then the
1116      * locale language is looked up. 
1117      *
1118      * @param locale a <code>Locale</code> value
1119      * @return a <code>String</code> representing the character encoding for
1120      * the locale or null if none found.
1121      */
1122     public String getLocaleEncoding(Locale locale)
1123     {
1124         if (_localeEncodingMap==null)
1125             return null;
1126         String encoding = (String)_localeEncodingMap.get(locale.toString());
1127         if (encoding==null)
1128             encoding = (String)_localeEncodingMap.get(locale.getLanguage());
1129         return encoding;
1130     }
1131     
1132     /* ------------------------------------------------------------ */
1133     /* 
1134      */
1135     public Resource getResource(String path) throws MalformedURLException
1136     {
1137         if (path==null || !path.startsWith(URIUtil.SLASH))
1138             throw new MalformedURLException(path);
1139         
1140         if (_baseResource==null)
1141             return null;
1142 
1143         try
1144         {
1145             path=URIUtil.canonicalPath(path);
1146             Resource resource=_baseResource.addPath(path);
1147             return resource;
1148         }
1149         catch(Exception e)
1150         {
1151             Log.ignore(e);
1152         }
1153                     
1154         return null;
1155     }
1156 
1157 
1158     /* ------------------------------------------------------------ */
1159     /* 
1160      */
1161     public Set getResourcePaths(String path)
1162     {           
1163         try
1164         {
1165             path=URIUtil.canonicalPath(path);
1166             Resource resource=getResource(path);
1167             
1168             if (resource!=null && resource.exists())
1169             {
1170                 if (!path.endsWith(URIUtil.SLASH))
1171                     path=path+URIUtil.SLASH;
1172                 
1173                 String[] l=resource.list();
1174                 if (l!=null)
1175                 {
1176                     HashSet set = new HashSet();
1177                     for(int i=0;i<l.length;i++)
1178                         set.add(path+l[i]);
1179                     return set;
1180                 }   
1181             }
1182         }
1183         catch(Exception e)
1184         {
1185             Log.ignore(e);
1186         }
1187         return Collections.EMPTY_SET;
1188     }
1189 
1190     
1191     /* ------------------------------------------------------------ */
1192     /** Context.
1193      * <p>
1194      * Implements {@link javax.servlet.ServletContext} from the {@link javax.servlet} package.   
1195      * </p>
1196      * @author gregw
1197      *
1198      */
1199     public class SContext implements ServletContext
1200     {
1201         /* ------------------------------------------------------------ */
1202         protected SContext()
1203         {
1204         }
1205 
1206         /* ------------------------------------------------------------ */
1207         public ContextHandler getContextHandler()
1208         {
1209             // TODO reduce visibility of this method
1210             return ContextHandler.this;
1211         }
1212 
1213         /* ------------------------------------------------------------ */
1214         /* 
1215          * @see javax.servlet.ServletContext#getContext(java.lang.String)
1216          */
1217         public ServletContext getContext(String uripath)
1218         {
1219             // TODO this is a very poor implementation!
1220             // TODO move this to Server
1221             ContextHandler context=null;
1222             Handler[] handlers = getServer().getChildHandlersByClass(ContextHandler.class);
1223             for (int i=0;i<handlers.length;i++)
1224             {
1225                 if (handlers[i]==null || !handlers[i].isStarted())
1226                     continue;
1227                 ContextHandler ch = (ContextHandler)handlers[i];
1228                 String context_path=ch.getContextPath();
1229                 if (uripath.equals(context_path) || (uripath.startsWith(context_path)&&uripath.charAt(context_path.length())=='/'))
1230                 {
1231                     if (context==null || context_path.length()>context.getContextPath().length())
1232                         context=ch;
1233                 }
1234             }
1235             
1236             if (context!=null)
1237                 return context._scontext;
1238             return null;
1239         }
1240 
1241         /* ------------------------------------------------------------ */
1242         /* 
1243          * @see javax.servlet.ServletContext#getMajorVersion()
1244          */
1245         public int getMajorVersion()
1246         {
1247             return 2;
1248         }
1249 
1250         /* ------------------------------------------------------------ */
1251         /* 
1252          * @see javax.servlet.ServletContext#getMimeType(java.lang.String)
1253          */
1254         public String getMimeType(String file)
1255         {
1256             if (_mimeTypes==null)
1257                 return null;
1258             Buffer mime = _mimeTypes.getMimeByExtension(file);
1259             if (mime!=null)
1260                 return mime.toString();
1261             return null;
1262         }
1263 
1264         /* ------------------------------------------------------------ */
1265         /* 
1266          * @see javax.servlet.ServletContext#getMinorVersion()
1267          */
1268         public int getMinorVersion()
1269         {
1270             return 5;
1271         }
1272 
1273         /* ------------------------------------------------------------ */
1274         /* 
1275          * @see javax.servlet.ServletContext#getNamedDispatcher(java.lang.String)
1276          */
1277         public RequestDispatcher getNamedDispatcher(String name)
1278         {
1279             return null;
1280         }
1281 
1282         /* ------------------------------------------------------------ */
1283         /* 
1284          * @see javax.servlet.ServletContext#getRealPath(java.lang.String)
1285          */
1286         public String getRealPath(String path)
1287         {
1288             if(path==null)
1289                 return null;
1290             if(path.length()==0)
1291                 path = URIUtil.SLASH;
1292             else if(path.charAt(0)!='/')
1293                 path = URIUtil.SLASH + path;
1294                 
1295             try
1296             {
1297                 Resource resource=ContextHandler.this.getResource(path);
1298                 if(resource!=null)
1299                 {
1300                     File file = resource.getFile();
1301                     if (file!=null)
1302                         return file.getCanonicalPath();
1303                 }
1304             }
1305             catch (Exception e)
1306             {
1307                 Log.ignore(e);
1308             }
1309             
1310             return null;
1311         }
1312 
1313         /* ------------------------------------------------------------ */
1314         /* 
1315          * @see javax.servlet.ServletContext#getRequestDispatcher(java.lang.String)
1316          */
1317         public RequestDispatcher getRequestDispatcher(String uriInContext)
1318         {
1319             return null;
1320         }
1321 
1322         /* ------------------------------------------------------------ */
1323         /* 
1324          */
1325         public URL getResource(String path) throws MalformedURLException
1326         {
1327             Resource resource=ContextHandler.this.getResource(path);
1328             if (resource!=null && resource.exists())
1329                 return resource.getURL();
1330             return null;
1331         }
1332         
1333         /* ------------------------------------------------------------ */
1334         /* 
1335          * @see javax.servlet.ServletContext#getResourceAsStream(java.lang.String)
1336          */
1337         public InputStream getResourceAsStream(String path)
1338         {
1339             try
1340             {
1341                 URL url=getResource(path);
1342                 if (url==null)
1343                     return null;
1344                 return url.openStream();
1345             }
1346             catch(Exception e)
1347             {
1348                 Log.ignore(e);
1349                 return null;
1350             }
1351         }
1352 
1353         /* ------------------------------------------------------------ */
1354         /* 
1355          * @see javax.servlet.ServletContext#getResourcePaths(java.lang.String)
1356          */
1357         public Set getResourcePaths(String path)
1358         {            
1359             return ContextHandler.this.getResourcePaths(path);
1360         }
1361 
1362         /* ------------------------------------------------------------ */
1363         /* 
1364          * @see javax.servlet.ServletContext#getServerInfo()
1365          */
1366         public String getServerInfo()
1367         {
1368             return "jetty/"+Server.getVersion();
1369         }
1370 
1371         /* ------------------------------------------------------------ */
1372         /* 
1373          * @see javax.servlet.ServletContext#getServlet(java.lang.String)
1374          */
1375         public Servlet getServlet(String name) throws ServletException
1376         {
1377             return null;
1378         }
1379 
1380         /* ------------------------------------------------------------ */
1381         /* 
1382          * @see javax.servlet.ServletContext#getServletNames()
1383          */
1384         public Enumeration getServletNames()
1385         {
1386             return Collections.enumeration(Collections.EMPTY_LIST);
1387         }
1388 
1389         /* ------------------------------------------------------------ */
1390         /* 
1391          * @see javax.servlet.ServletContext#getServlets()
1392          */
1393         public Enumeration getServlets()
1394         {
1395             return Collections.enumeration(Collections.EMPTY_LIST);
1396         }
1397 
1398         /* ------------------------------------------------------------ */
1399         /* 
1400          * @see javax.servlet.ServletContext#log(java.lang.Exception, java.lang.String)
1401          */
1402         public void log(Exception exception, String msg)
1403         {
1404             _logger.warn(msg,exception);
1405         }
1406 
1407         /* ------------------------------------------------------------ */
1408         /* 
1409          * @see javax.servlet.ServletContext#log(java.lang.String)
1410          */
1411         public void log(String msg)
1412         {
1413             _logger.info(msg, null, null);
1414         }
1415 
1416         /* ------------------------------------------------------------ */
1417         /* 
1418          * @see javax.servlet.ServletContext#log(java.lang.String, java.lang.Throwable)
1419          */
1420         public void log(String message, Throwable throwable)
1421         {
1422             _logger.warn(message,throwable);
1423         }
1424 
1425         /* ------------------------------------------------------------ */
1426         /* 
1427          * @see javax.servlet.ServletContext#getInitParameter(java.lang.String)
1428          */
1429         public String getInitParameter(String name)
1430         {
1431             return ContextHandler.this.getInitParameter(name);
1432         }
1433 
1434         /* ------------------------------------------------------------ */
1435         /* 
1436          * @see javax.servlet.ServletContext#getInitParameterNames()
1437          */
1438         public Enumeration getInitParameterNames()
1439         {
1440             return ContextHandler.this.getInitParameterNames();
1441         }
1442 
1443         /* ------------------------------------------------------------ */
1444         /* 
1445          * @see javax.servlet.ServletContext#getAttribute(java.lang.String)
1446          */
1447         public synchronized Object getAttribute(String name)
1448         {
1449             Object o = ContextHandler.this.getAttribute(name);
1450             if (o==null && _contextAttributes!=null)
1451                 o=_contextAttributes.getAttribute(name);
1452             return o;
1453         }
1454 
1455         /* ------------------------------------------------------------ */
1456         /* 
1457          * @see javax.servlet.ServletContext#getAttributeNames()
1458          */
1459         public synchronized Enumeration getAttributeNames()
1460         {
1461             HashSet set = new HashSet();
1462             if (_contextAttributes!=null)
1463             {
1464             	Enumeration e = _contextAttributes.getAttributeNames();
1465             	while(e.hasMoreElements())
1466             		set.add(e.nextElement());
1467             }
1468             Enumeration e = _attributes.getAttributeNames();
1469             while(e.hasMoreElements())
1470                 set.add(e.nextElement());
1471             
1472             return Collections.enumeration(set);
1473         }
1474 
1475         /* ------------------------------------------------------------ */
1476         /* 
1477          * @see javax.servlet.ServletContext#setAttribute(java.lang.String, java.lang.Object)
1478          */
1479         public synchronized void setAttribute(String name, Object value)
1480         {
1481             
1482             if (_contextAttributes==null)
1483             {
1484             	// Set it on the handler
1485             	ContextHandler.this.setAttribute(name, value);
1486                 return;
1487             }
1488 
1489             setManagedAttribute(name,value);
1490             Object old_value=_contextAttributes==null?null:_contextAttributes.getAttribute(name);
1491             
1492             if (value==null)
1493                 _contextAttributes.removeAttribute(name);
1494             else
1495                 _contextAttributes.setAttribute(name,value);
1496             
1497             if (_contextAttributeListeners!=null)
1498             {
1499                 ServletContextAttributeEvent event =
1500                     new ServletContextAttributeEvent(_scontext,name, old_value==null?value:old_value);
1501 
1502                 for(int i=0;i<LazyList.size(_contextAttributeListeners);i++)
1503                 {
1504                     ServletContextAttributeListener l = (ServletContextAttributeListener)LazyList.get(_contextAttributeListeners,i);
1505                     
1506                     if (old_value==null)
1507                         l.attributeAdded(event);
1508                     else if (value==null)
1509                         l.attributeRemoved(event);
1510                     else
1511                         l.attributeReplaced(event);
1512                 }
1513             }
1514         }
1515 
1516         /* ------------------------------------------------------------ */
1517         /* 
1518          * @see javax.servlet.ServletContext#removeAttribute(java.lang.String)
1519          */
1520         public synchronized void removeAttribute(String name)
1521         {
1522             setManagedAttribute(name,null);
1523             
1524             if (_contextAttributes==null)
1525             {
1526             	// Set it on the handler
1527             	_attributes.removeAttribute(name);
1528                 return;
1529             }
1530             
1531             Object old_value=_contextAttributes.getAttribute(name);
1532             _contextAttributes.removeAttribute(name);
1533             if (old_value!=null)
1534             {
1535                 if (_contextAttributeListeners!=null)
1536                 {
1537                     ServletContextAttributeEvent event =
1538                         new ServletContextAttributeEvent(_scontext,name, old_value);
1539 
1540                     for(int i=0;i<LazyList.size(_contextAttributeListeners);i++)
1541                         ((ServletContextAttributeListener)LazyList.get(_contextAttributeListeners,i)).attributeRemoved(event);
1542                 }
1543             }
1544         }
1545 
1546         /* ------------------------------------------------------------ */
1547         /* 
1548          * @see javax.servlet.ServletContext#getServletContextName()
1549          */
1550         public String getServletContextName()
1551         {
1552             String name = ContextHandler.this.getDisplayName();
1553             if (name==null)
1554                 name=ContextHandler.this.getContextPath();
1555             return name;
1556         }
1557 
1558         /* ------------------------------------------------------------ */
1559         /**
1560          * @return Returns the _contextPath.
1561          */
1562         public String getContextPath()
1563         {
1564             if ((_contextPath != null) && _contextPath.equals(URIUtil.SLASH))
1565                 return "";
1566             
1567             return _contextPath;
1568         }
1569 
1570         /* ------------------------------------------------------------ */
1571         public String toString()
1572         {
1573             return "ServletContext@"+Integer.toHexString(hashCode())+"{"+(getContextPath().equals("")?URIUtil.SLASH:getContextPath())+","+getBaseResource()+"}";
1574         }
1575     }
1576 
1577     /* ------------------------------------------------------------ */
1578     private String normalizeHostname( String host )
1579     {
1580         if ( host == null )
1581             return null;
1582         
1583         if ( host.endsWith( "." ) )
1584             return host.substring( 0, host.length() -1);
1585       
1586             return host;
1587     }
1588 
1589 }