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