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                 base_request.setRequestListeners(_requestListeners);
749                 if (_requestAttributeListeners!=null)
750                 {
751                     final int s=LazyList.size(_requestAttributeListeners);
752                     for(int i=0;i<s;i++)
753                         base_request.addEventListener(((EventListener)LazyList.get(_requestAttributeListeners,i)));
754                 }
755             }
756             
757             // Handle the request
758             try
759             {
760                 if (dispatch==REQUEST && isProtectedTarget(target))
761                     throw new HttpException(HttpServletResponse.SC_NOT_FOUND);
762                 
763                 Handler handler = getHandler();
764                 if (handler!=null)
765                     handler.handle(target, request, response, dispatch);
766             }
767             catch(HttpException e)
768             {
769                 Log.debug(e);
770                 response.sendError(e.getStatus(), e.getReason());
771             }
772             finally
773             {
774                 // Handle more REALLY SILLY request events!
775                 if (new_context)
776                 {
777                     base_request.takeRequestListeners();
778                     if (_requestAttributeListeners!=null)
779                     {
780                         for(int i=LazyList.size(_requestAttributeListeners);i-->0;)
781                             base_request.removeEventListener(((EventListener)LazyList.get(_requestAttributeListeners,i)));
782                     }
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.toString());
989             Log.debug(e);
990             throw new IllegalArgumentException(resourceBase);
991         }
992     }
993 
994     /* ------------------------------------------------------------ */
995     /**
996      * @return Returns the mimeTypes.
997      */
998     public MimeTypes getMimeTypes()
999     {
1000         return _mimeTypes;
1001     }
1002     
1003     /* ------------------------------------------------------------ */
1004     /**
1005      * @param mimeTypes The mimeTypes to set.
1006      */
1007     public void setMimeTypes(MimeTypes mimeTypes)
1008     {
1009         _mimeTypes = mimeTypes;
1010     }
1011 
1012     /* ------------------------------------------------------------ */
1013     /**
1014      */
1015     public void setWelcomeFiles(String[] files) 
1016     {
1017         _welcomeFiles=files;
1018     }
1019 
1020     /* ------------------------------------------------------------ */
1021     /**
1022      * @return The names of the files which the server should consider to be welcome files in this context.
1023      * @see <a href="http://jcp.org/aboutJava/communityprocess/final/jsr154/index.html">The Servlet Specification</a>
1024      * @see #setWelcomeFiles
1025      */
1026     public String[] getWelcomeFiles() 
1027     {
1028         return _welcomeFiles;
1029     }
1030 
1031     /* ------------------------------------------------------------ */
1032     /**
1033      * @return Returns the errorHandler.
1034      */
1035     public ErrorHandler getErrorHandler()
1036     {
1037         return _errorHandler;
1038     }
1039 
1040     /* ------------------------------------------------------------ */
1041     /**
1042      * @param errorHandler The errorHandler to set.
1043      */
1044     public void setErrorHandler(ErrorHandler errorHandler)
1045     {
1046         if (errorHandler!=null)
1047             errorHandler.setServer(getServer());
1048         if (getServer()!=null)
1049             getServer().getContainer().update(this, _errorHandler, errorHandler, "errorHandler",true);
1050         _errorHandler = errorHandler;
1051     }
1052     
1053     /* ------------------------------------------------------------ */
1054     public int getMaxFormContentSize()
1055     {
1056         return _maxFormContentSize;
1057     }
1058     
1059     /* ------------------------------------------------------------ */
1060     public void setMaxFormContentSize(int maxSize)
1061     {
1062         _maxFormContentSize=maxSize;
1063     }
1064 
1065 
1066     /* ------------------------------------------------------------ */
1067     /**
1068      * @return True if URLs are compacted to replace multiple '/'s with a single '/'
1069      */
1070     public boolean isCompactPath()
1071     {
1072         return _compactPath;
1073     }
1074 
1075     /* ------------------------------------------------------------ */
1076     /**
1077      * @param compactPath True if URLs are compacted to replace multiple '/'s with a single '/'
1078      */
1079     public void setCompactPath(boolean compactPath)
1080     {
1081         _compactPath=compactPath;
1082     }
1083 
1084     /* ------------------------------------------------------------ */
1085     public String toString()
1086     {
1087         
1088         return this.getClass().getName()+"@"+Integer.toHexString(hashCode())+"{"+getContextPath()+","+getBaseResource()+"}";
1089     }
1090 
1091     /* ------------------------------------------------------------ */
1092     public synchronized Class loadClass(String className)
1093         throws ClassNotFoundException
1094     {
1095         if (className==null)
1096             return null;
1097         
1098         if (_classLoader==null)
1099             return Loader.loadClass(this.getClass(), className);
1100 
1101         return _classLoader.loadClass(className);
1102     }
1103     
1104 
1105     /* ------------------------------------------------------------ */
1106     public void addLocaleEncoding(String locale,String encoding)
1107     {
1108         if (_localeEncodingMap==null)
1109             _localeEncodingMap=new HashMap();
1110         _localeEncodingMap.put(locale, encoding);
1111     }
1112     
1113     /* ------------------------------------------------------------ */
1114     /**
1115      * Get the character encoding for a locale. The full locale name is first
1116      * looked up in the map of encodings. If no encoding is found, then the
1117      * locale language is looked up. 
1118      *
1119      * @param locale a <code>Locale</code> value
1120      * @return a <code>String</code> representing the character encoding for
1121      * the locale or null if none found.
1122      */
1123     public String getLocaleEncoding(Locale locale)
1124     {
1125         if (_localeEncodingMap==null)
1126             return null;
1127         String encoding = (String)_localeEncodingMap.get(locale.toString());
1128         if (encoding==null)
1129             encoding = (String)_localeEncodingMap.get(locale.getLanguage());
1130         return encoding;
1131     }
1132     
1133     /* ------------------------------------------------------------ */
1134     /* 
1135      */
1136     public Resource getResource(String path) throws MalformedURLException
1137     {
1138         if (path==null || !path.startsWith(URIUtil.SLASH))
1139             throw new MalformedURLException(path);
1140         
1141         if (_baseResource==null)
1142             return null;
1143 
1144         try
1145         {
1146             path=URIUtil.canonicalPath(path);
1147             Resource resource=_baseResource.addPath(path);
1148             return resource;
1149         }
1150         catch(Exception e)
1151         {
1152             Log.ignore(e);
1153         }
1154                     
1155         return null;
1156     }
1157 
1158 
1159     /* ------------------------------------------------------------ */
1160     /* 
1161      */
1162     public Set getResourcePaths(String path)
1163     {           
1164         try
1165         {
1166             path=URIUtil.canonicalPath(path);
1167             Resource resource=getResource(path);
1168             
1169             if (resource!=null && resource.exists())
1170             {
1171                 if (!path.endsWith(URIUtil.SLASH))
1172                     path=path+URIUtil.SLASH;
1173                 
1174                 String[] l=resource.list();
1175                 if (l!=null)
1176                 {
1177                     HashSet set = new HashSet();
1178                     for(int i=0;i<l.length;i++)
1179                         set.add(path+l[i]);
1180                     return set;
1181                 }   
1182             }
1183         }
1184         catch(Exception e)
1185         {
1186             Log.ignore(e);
1187         }
1188         return Collections.EMPTY_SET;
1189     }
1190 
1191     
1192     /* ------------------------------------------------------------ */
1193     /** Context.
1194      * <p>
1195      * Implements {@link javax.servlet.ServletContext} from the {@link javax.servlet} package.   
1196      * </p>
1197      * @author gregw
1198      *
1199      */
1200     public class SContext implements ServletContext
1201     {
1202         /* ------------------------------------------------------------ */
1203         protected SContext()
1204         {
1205         }
1206 
1207         /* ------------------------------------------------------------ */
1208         public ContextHandler getContextHandler()
1209         {
1210             // TODO reduce visibility of this method
1211             return ContextHandler.this;
1212         }
1213 
1214         /* ------------------------------------------------------------ */
1215         /* 
1216          * @see javax.servlet.ServletContext#getContext(java.lang.String)
1217          */
1218         public ServletContext getContext(String uripath)
1219         {
1220             // TODO this is a very poor implementation!
1221             // TODO move this to Server
1222             ContextHandler context=null;
1223             Handler[] handlers = getServer().getChildHandlersByClass(ContextHandler.class);
1224             for (int i=0;i<handlers.length;i++)
1225             {
1226                 if (handlers[i]==null || !handlers[i].isStarted())
1227                     continue;
1228                 ContextHandler ch = (ContextHandler)handlers[i];
1229                 String context_path=ch.getContextPath();
1230                 if (uripath.equals(context_path) || (uripath.startsWith(context_path)&&uripath.charAt(context_path.length())=='/'))
1231                 {
1232                     if (context==null || context_path.length()>context.getContextPath().length())
1233                         context=ch;
1234                 }
1235             }
1236             
1237             if (context!=null)
1238                 return context._scontext;
1239             return null;
1240         }
1241 
1242         /* ------------------------------------------------------------ */
1243         /* 
1244          * @see javax.servlet.ServletContext#getMajorVersion()
1245          */
1246         public int getMajorVersion()
1247         {
1248             return 2;
1249         }
1250 
1251         /* ------------------------------------------------------------ */
1252         /* 
1253          * @see javax.servlet.ServletContext#getMimeType(java.lang.String)
1254          */
1255         public String getMimeType(String file)
1256         {
1257             if (_mimeTypes==null)
1258                 return null;
1259             Buffer mime = _mimeTypes.getMimeByExtension(file);
1260             if (mime!=null)
1261                 return mime.toString();
1262             return null;
1263         }
1264 
1265         /* ------------------------------------------------------------ */
1266         /* 
1267          * @see javax.servlet.ServletContext#getMinorVersion()
1268          */
1269         public int getMinorVersion()
1270         {
1271             return 5;
1272         }
1273 
1274         /* ------------------------------------------------------------ */
1275         /* 
1276          * @see javax.servlet.ServletContext#getNamedDispatcher(java.lang.String)
1277          */
1278         public RequestDispatcher getNamedDispatcher(String name)
1279         {
1280             return null;
1281         }
1282 
1283         /* ------------------------------------------------------------ */
1284         /* 
1285          * @see javax.servlet.ServletContext#getRealPath(java.lang.String)
1286          */
1287         public String getRealPath(String path)
1288         {
1289             if(path==null)
1290                 return null;
1291             if(path.length()==0)
1292                 path = URIUtil.SLASH;
1293             else if(path.charAt(0)!='/')
1294                 path = URIUtil.SLASH + path;
1295                 
1296             try
1297             {
1298                 Resource resource=ContextHandler.this.getResource(path);
1299                 if(resource!=null)
1300                 {
1301                     File file = resource.getFile();
1302                     if (file!=null)
1303                         return file.getCanonicalPath();
1304                 }
1305             }
1306             catch (Exception e)
1307             {
1308                 Log.ignore(e);
1309             }
1310             
1311             return null;
1312         }
1313 
1314         /* ------------------------------------------------------------ */
1315         /* 
1316          * @see javax.servlet.ServletContext#getRequestDispatcher(java.lang.String)
1317          */
1318         public RequestDispatcher getRequestDispatcher(String uriInContext)
1319         {
1320             return null;
1321         }
1322 
1323         /* ------------------------------------------------------------ */
1324         /* 
1325          */
1326         public URL getResource(String path) throws MalformedURLException
1327         {
1328             Resource resource=ContextHandler.this.getResource(path);
1329             if (resource!=null && resource.exists())
1330                 return resource.getURL();
1331             return null;
1332         }
1333         
1334         /* ------------------------------------------------------------ */
1335         /* 
1336          * @see javax.servlet.ServletContext#getResourceAsStream(java.lang.String)
1337          */
1338         public InputStream getResourceAsStream(String path)
1339         {
1340             try
1341             {
1342                 URL url=getResource(path);
1343                 if (url==null)
1344                     return null;
1345                 return url.openStream();
1346             }
1347             catch(Exception e)
1348             {
1349                 Log.ignore(e);
1350                 return null;
1351             }
1352         }
1353 
1354         /* ------------------------------------------------------------ */
1355         /* 
1356          * @see javax.servlet.ServletContext#getResourcePaths(java.lang.String)
1357          */
1358         public Set getResourcePaths(String path)
1359         {            
1360             return ContextHandler.this.getResourcePaths(path);
1361         }
1362 
1363         /* ------------------------------------------------------------ */
1364         /* 
1365          * @see javax.servlet.ServletContext#getServerInfo()
1366          */
1367         public String getServerInfo()
1368         {
1369             return "jetty/"+Server.getVersion();
1370         }
1371 
1372         /* ------------------------------------------------------------ */
1373         /* 
1374          * @see javax.servlet.ServletContext#getServlet(java.lang.String)
1375          */
1376         public Servlet getServlet(String name) throws ServletException
1377         {
1378             return null;
1379         }
1380 
1381         /* ------------------------------------------------------------ */
1382         /* 
1383          * @see javax.servlet.ServletContext#getServletNames()
1384          */
1385         public Enumeration getServletNames()
1386         {
1387             return Collections.enumeration(Collections.EMPTY_LIST);
1388         }
1389 
1390         /* ------------------------------------------------------------ */
1391         /* 
1392          * @see javax.servlet.ServletContext#getServlets()
1393          */
1394         public Enumeration getServlets()
1395         {
1396             return Collections.enumeration(Collections.EMPTY_LIST);
1397         }
1398 
1399         /* ------------------------------------------------------------ */
1400         /* 
1401          * @see javax.servlet.ServletContext#log(java.lang.Exception, java.lang.String)
1402          */
1403         public void log(Exception exception, String msg)
1404         {
1405             _logger.warn(msg,exception);
1406         }
1407 
1408         /* ------------------------------------------------------------ */
1409         /* 
1410          * @see javax.servlet.ServletContext#log(java.lang.String)
1411          */
1412         public void log(String msg)
1413         {
1414             _logger.info(msg, null, null);
1415         }
1416 
1417         /* ------------------------------------------------------------ */
1418         /* 
1419          * @see javax.servlet.ServletContext#log(java.lang.String, java.lang.Throwable)
1420          */
1421         public void log(String message, Throwable throwable)
1422         {
1423             _logger.warn(message,throwable);
1424         }
1425 
1426         /* ------------------------------------------------------------ */
1427         /* 
1428          * @see javax.servlet.ServletContext#getInitParameter(java.lang.String)
1429          */
1430         public String getInitParameter(String name)
1431         {
1432             return ContextHandler.this.getInitParameter(name);
1433         }
1434 
1435         /* ------------------------------------------------------------ */
1436         /* 
1437          * @see javax.servlet.ServletContext#getInitParameterNames()
1438          */
1439         public Enumeration getInitParameterNames()
1440         {
1441             return ContextHandler.this.getInitParameterNames();
1442         }
1443 
1444         /* ------------------------------------------------------------ */
1445         /* 
1446          * @see javax.servlet.ServletContext#getAttribute(java.lang.String)
1447          */
1448         public synchronized Object getAttribute(String name)
1449         {
1450             Object o = ContextHandler.this.getAttribute(name);
1451             if (o==null && _contextAttributes!=null)
1452                 o=_contextAttributes.getAttribute(name);
1453             return o;
1454         }
1455 
1456         /* ------------------------------------------------------------ */
1457         /* 
1458          * @see javax.servlet.ServletContext#getAttributeNames()
1459          */
1460         public synchronized Enumeration getAttributeNames()
1461         {
1462             HashSet set = new HashSet();
1463             if (_contextAttributes!=null)
1464             {
1465             	Enumeration e = _contextAttributes.getAttributeNames();
1466             	while(e.hasMoreElements())
1467             		set.add(e.nextElement());
1468             }
1469             Enumeration e = _attributes.getAttributeNames();
1470             while(e.hasMoreElements())
1471                 set.add(e.nextElement());
1472             
1473             return Collections.enumeration(set);
1474         }
1475 
1476         /* ------------------------------------------------------------ */
1477         /* 
1478          * @see javax.servlet.ServletContext#setAttribute(java.lang.String, java.lang.Object)
1479          */
1480         public synchronized void setAttribute(String name, Object value)
1481         {
1482             
1483             if (_contextAttributes==null)
1484             {
1485             	// Set it on the handler
1486             	ContextHandler.this.setAttribute(name, value);
1487                 return;
1488             }
1489 
1490             setManagedAttribute(name,value);
1491             Object old_value=_contextAttributes==null?null:_contextAttributes.getAttribute(name);
1492             
1493             if (value==null)
1494                 _contextAttributes.removeAttribute(name);
1495             else
1496                 _contextAttributes.setAttribute(name,value);
1497             
1498             if (_contextAttributeListeners!=null)
1499             {
1500                 ServletContextAttributeEvent event =
1501                     new ServletContextAttributeEvent(_scontext,name, old_value==null?value:old_value);
1502 
1503                 for(int i=0;i<LazyList.size(_contextAttributeListeners);i++)
1504                 {
1505                     ServletContextAttributeListener l = (ServletContextAttributeListener)LazyList.get(_contextAttributeListeners,i);
1506                     
1507                     if (old_value==null)
1508                         l.attributeAdded(event);
1509                     else if (value==null)
1510                         l.attributeRemoved(event);
1511                     else
1512                         l.attributeReplaced(event);
1513                 }
1514             }
1515         }
1516 
1517         /* ------------------------------------------------------------ */
1518         /* 
1519          * @see javax.servlet.ServletContext#removeAttribute(java.lang.String)
1520          */
1521         public synchronized void removeAttribute(String name)
1522         {
1523             setManagedAttribute(name,null);
1524             
1525             if (_contextAttributes==null)
1526             {
1527             	// Set it on the handler
1528             	_attributes.removeAttribute(name);
1529                 return;
1530             }
1531             
1532             Object old_value=_contextAttributes.getAttribute(name);
1533             _contextAttributes.removeAttribute(name);
1534             if (old_value!=null)
1535             {
1536                 if (_contextAttributeListeners!=null)
1537                 {
1538                     ServletContextAttributeEvent event =
1539                         new ServletContextAttributeEvent(_scontext,name, old_value);
1540 
1541                     for(int i=0;i<LazyList.size(_contextAttributeListeners);i++)
1542                         ((ServletContextAttributeListener)LazyList.get(_contextAttributeListeners,i)).attributeRemoved(event);
1543                 }
1544             }
1545         }
1546 
1547         /* ------------------------------------------------------------ */
1548         /* 
1549          * @see javax.servlet.ServletContext#getServletContextName()
1550          */
1551         public String getServletContextName()
1552         {
1553             String name = ContextHandler.this.getDisplayName();
1554             if (name==null)
1555                 name=ContextHandler.this.getContextPath();
1556             return name;
1557         }
1558 
1559         /* ------------------------------------------------------------ */
1560         /**
1561          * @return Returns the _contextPath.
1562          */
1563         public String getContextPath()
1564         {
1565             if ((_contextPath != null) && _contextPath.equals(URIUtil.SLASH))
1566                 return "";
1567             
1568             return _contextPath;
1569         }
1570 
1571         /* ------------------------------------------------------------ */
1572         public String toString()
1573         {
1574             return "ServletContext@"+Integer.toHexString(hashCode())+"{"+(getContextPath().equals("")?URIUtil.SLASH:getContextPath())+","+getBaseResource()+"}";
1575         }
1576     }
1577 
1578     /* ------------------------------------------------------------ */
1579     private String normalizeHostname( String host )
1580     {
1581         if ( host == null )
1582             return null;
1583         
1584         if ( host.endsWith( "." ) )
1585             return host.substring( 0, host.length() -1);
1586       
1587             return host;
1588     }
1589 
1590 }