View Javadoc

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