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