Home | History | Annotate | Download | only in handler
      1 //
      2 //  ========================================================================
      3 //  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
      4 //  ------------------------------------------------------------------------
      5 //  All rights reserved. This program and the accompanying materials
      6 //  are made available under the terms of the Eclipse Public License v1.0
      7 //  and Apache License v2.0 which accompanies this distribution.
      8 //
      9 //      The Eclipse Public License is available at
     10 //      http://www.eclipse.org/legal/epl-v10.html
     11 //
     12 //      The Apache License v2.0 is available at
     13 //      http://www.opensource.org/licenses/apache2.0.php
     14 //
     15 //  You may elect to redistribute this code under either of these licenses.
     16 //  ========================================================================
     17 //
     18 
     19 package org.eclipse.jetty.server.handler;
     20 
     21 import java.io.File;
     22 import java.io.IOException;
     23 import java.io.InputStream;
     24 import java.net.MalformedURLException;
     25 import java.net.URL;
     26 import java.net.URLClassLoader;
     27 import java.security.AccessController;
     28 import java.util.ArrayList;
     29 import java.util.Arrays;
     30 import java.util.Collections;
     31 import java.util.Enumeration;
     32 import java.util.EventListener;
     33 import java.util.HashMap;
     34 import java.util.HashSet;
     35 import java.util.Iterator;
     36 import java.util.List;
     37 import java.util.Locale;
     38 import java.util.Map;
     39 import java.util.Set;
     40 import java.util.concurrent.CopyOnWriteArrayList;
     41 
     42 import javax.servlet.DispatcherType;
     43 import javax.servlet.RequestDispatcher;
     44 import javax.servlet.Servlet;
     45 import javax.servlet.ServletContext;
     46 import javax.servlet.ServletContextAttributeEvent;
     47 import javax.servlet.ServletContextAttributeListener;
     48 import javax.servlet.ServletContextEvent;
     49 import javax.servlet.ServletContextListener;
     50 import javax.servlet.ServletException;
     51 import javax.servlet.ServletRegistration;
     52 import javax.servlet.ServletRequestAttributeListener;
     53 import javax.servlet.ServletRequestEvent;
     54 import javax.servlet.ServletRequestListener;
     55 import javax.servlet.SessionCookieConfig;
     56 import javax.servlet.SessionTrackingMode;
     57 import javax.servlet.Filter;
     58 import javax.servlet.FilterRegistration;
     59 import javax.servlet.FilterRegistration.Dynamic;
     60 import javax.servlet.descriptor.JspConfigDescriptor;
     61 import javax.servlet.http.HttpServletRequest;
     62 import javax.servlet.http.HttpServletResponse;
     63 
     64 import org.eclipse.jetty.http.HttpException;
     65 import org.eclipse.jetty.http.MimeTypes;
     66 import org.eclipse.jetty.io.Buffer;
     67 import org.eclipse.jetty.server.AbstractHttpConnection;
     68 import org.eclipse.jetty.server.Dispatcher;
     69 import org.eclipse.jetty.server.Handler;
     70 import org.eclipse.jetty.server.HandlerContainer;
     71 import org.eclipse.jetty.server.Request;
     72 import org.eclipse.jetty.server.Server;
     73 import org.eclipse.jetty.util.Attributes;
     74 import org.eclipse.jetty.util.AttributesMap;
     75 import org.eclipse.jetty.util.LazyList;
     76 import org.eclipse.jetty.util.Loader;
     77 import org.eclipse.jetty.util.StringUtil;
     78 import org.eclipse.jetty.util.TypeUtil;
     79 import org.eclipse.jetty.util.URIUtil;
     80 import org.eclipse.jetty.util.component.AggregateLifeCycle;
     81 import org.eclipse.jetty.util.component.Dumpable;
     82 import org.eclipse.jetty.util.log.Log;
     83 import org.eclipse.jetty.util.log.Logger;
     84 import org.eclipse.jetty.util.resource.Resource;
     85 
     86 /* ------------------------------------------------------------ */
     87 /**
     88  * ContextHandler.
     89  *
     90  * This handler wraps a call to handle by setting the context and servlet path, plus setting the context classloader.
     91  *
     92  * <p>
     93  * If the context init parameter "org.eclipse.jetty.server.context.ManagedAttributes" is set to a comma separated list of names, then they are treated as
     94  * context attribute names, which if set as attributes are passed to the servers Container so that they may be managed with JMX.
     95  * <p>
     96  * The maximum size of a form that can be processed by this context is controlled by the system properties org.eclipse.jetty.server.Request.maxFormKeys
     97  * and org.eclipse.jetty.server.Request.maxFormContentSize.  These can also be configured with {@link #setMaxFormContentSize(int)} and {@link #setMaxFormKeys(int)}
     98  *
     99  * @org.apache.xbean.XBean description="Creates a basic HTTP context"
    100  */
    101 public class ContextHandler extends ScopedHandler implements Attributes, Server.Graceful
    102 {
    103     private static final Logger LOG = Log.getLogger(ContextHandler.class);
    104 
    105     private static final ThreadLocal<Context> __context = new ThreadLocal<Context>();
    106 
    107     /**
    108      * If a context attribute with this name is set, it is interpreted as a comma separated list of attribute name. Any other context attributes that are set
    109      * with a name from this list will result in a call to {@link #setManagedAttribute(String, Object)}, which typically initiates the creation of a JMX MBean
    110      * for the attribute value.
    111      */
    112     public static final String MANAGED_ATTRIBUTES = "org.eclipse.jetty.server.context.ManagedAttributes";
    113 
    114     /* ------------------------------------------------------------ */
    115     /**
    116      * Get the current ServletContext implementation.
    117      *
    118      * @return ServletContext implementation
    119      */
    120     public static Context getCurrentContext()
    121     {
    122         return __context.get();
    123     }
    124 
    125     protected Context _scontext;
    126 
    127     private final AttributesMap _attributes;
    128     private final AttributesMap _contextAttributes;
    129     private final Map<String, String> _initParams;
    130     private ClassLoader _classLoader;
    131     private String _contextPath = "/";
    132     private String _displayName;
    133     private Resource _baseResource;
    134     private MimeTypes _mimeTypes;
    135     private Map<String, String> _localeEncodingMap;
    136     private String[] _welcomeFiles;
    137     private ErrorHandler _errorHandler;
    138     private String[] _vhosts;
    139     private Set<String> _connectors;
    140     private EventListener[] _eventListeners;
    141     private Logger _logger;
    142     private boolean _allowNullPathInfo;
    143     private int _maxFormKeys = Integer.getInteger("org.eclipse.jetty.server.Request.maxFormKeys",-1).intValue();
    144     private int _maxFormContentSize = Integer.getInteger("org.eclipse.jetty.server.Request.maxFormContentSize",-1).intValue();
    145     private boolean _compactPath = false;
    146     private boolean _aliasesAllowed = false;
    147 
    148     private Object _contextListeners;
    149     private Object _contextAttributeListeners;
    150     private Object _requestListeners;
    151     private Object _requestAttributeListeners;
    152     private Object _durableListeners;
    153     private Map<String, Object> _managedAttributes;
    154     private String[] _protectedTargets;
    155     private final CopyOnWriteArrayList<AliasCheck> _aliasChecks = new CopyOnWriteArrayList<ContextHandler.AliasCheck>();
    156 
    157     private boolean _shutdown = false;
    158     private boolean _available = true;
    159     private volatile int _availability; // 0=STOPPED, 1=AVAILABLE, 2=SHUTDOWN, 3=UNAVAILABLE
    160 
    161     private final static int __STOPPED = 0, __AVAILABLE = 1, __SHUTDOWN = 2, __UNAVAILABLE = 3;
    162 
    163     /* ------------------------------------------------------------ */
    164     /**
    165      *
    166      */
    167     public ContextHandler()
    168     {
    169         super();
    170         _scontext = new Context();
    171         _attributes = new AttributesMap();
    172         _contextAttributes = new AttributesMap();
    173         _initParams = new HashMap<String, String>();
    174         addAliasCheck(new ApproveNonExistentDirectoryAliases());
    175     }
    176 
    177     /* ------------------------------------------------------------ */
    178     /**
    179      *
    180      */
    181     protected ContextHandler(Context context)
    182     {
    183         super();
    184         _scontext = context;
    185         _attributes = new AttributesMap();
    186         _contextAttributes = new AttributesMap();
    187         _initParams = new HashMap<String, String>();
    188         addAliasCheck(new ApproveNonExistentDirectoryAliases());
    189     }
    190 
    191     /* ------------------------------------------------------------ */
    192     /**
    193      *
    194      */
    195     public ContextHandler(String contextPath)
    196     {
    197         this();
    198         setContextPath(contextPath);
    199     }
    200 
    201     /* ------------------------------------------------------------ */
    202     /**
    203      *
    204      */
    205     public ContextHandler(HandlerContainer parent, String contextPath)
    206     {
    207         this();
    208         setContextPath(contextPath);
    209         if (parent instanceof HandlerWrapper)
    210             ((HandlerWrapper)parent).setHandler(this);
    211         else if (parent instanceof HandlerCollection)
    212             ((HandlerCollection)parent).addHandler(this);
    213     }
    214 
    215     /* ------------------------------------------------------------ */
    216     @Override
    217     public void dump(Appendable out, String indent) throws IOException
    218     {
    219         dumpThis(out);
    220         dump(out,indent,Collections.singletonList(new CLDump(getClassLoader())),TypeUtil.asList(getHandlers()),getBeans(),_initParams.entrySet(),
    221                 _attributes.getAttributeEntrySet(),_contextAttributes.getAttributeEntrySet());
    222     }
    223 
    224     /* ------------------------------------------------------------ */
    225     public Context getServletContext()
    226     {
    227         return _scontext;
    228     }
    229 
    230     /* ------------------------------------------------------------ */
    231     /**
    232      * @return the allowNullPathInfo true if /context is not redirected to /context/
    233      */
    234     public boolean getAllowNullPathInfo()
    235     {
    236         return _allowNullPathInfo;
    237     }
    238 
    239     /* ------------------------------------------------------------ */
    240     /**
    241      * @param allowNullPathInfo
    242      *            true if /context is not redirected to /context/
    243      */
    244     public void setAllowNullPathInfo(boolean allowNullPathInfo)
    245     {
    246         _allowNullPathInfo = allowNullPathInfo;
    247     }
    248 
    249     /* ------------------------------------------------------------ */
    250     @Override
    251     public void setServer(Server server)
    252     {
    253         if (_errorHandler != null)
    254         {
    255             Server old_server = getServer();
    256             if (old_server != null && old_server != server)
    257                 old_server.getContainer().update(this,_errorHandler,null,"error",true);
    258             super.setServer(server);
    259             if (server != null && server != old_server)
    260                 server.getContainer().update(this,null,_errorHandler,"error",true);
    261             _errorHandler.setServer(server);
    262         }
    263         else
    264             super.setServer(server);
    265     }
    266 
    267     /* ------------------------------------------------------------ */
    268     /**
    269      * Set the virtual hosts for the context. Only requests that have a matching host header or fully qualified URL will be passed to that context with a
    270      * virtual host name. A context with no virtual host names or a null virtual host name is available to all requests that are not served by a context with a
    271      * matching virtual host name.
    272      *
    273      * @param vhosts
    274      *            Array of virtual hosts that this context responds to. A null host name or null/empty array means any hostname is acceptable. Host names may be
    275      *            String representation of IP addresses. Host names may start with '*.' to wildcard one level of names.
    276      */
    277     public void setVirtualHosts(String[] vhosts)
    278     {
    279         if (vhosts == null)
    280         {
    281             _vhosts = vhosts;
    282         }
    283         else
    284         {
    285             _vhosts = new String[vhosts.length];
    286             for (int i = 0; i < vhosts.length; i++)
    287                 _vhosts[i] = normalizeHostname(vhosts[i]);
    288         }
    289     }
    290 
    291     /* ------------------------------------------------------------ */
    292     /** Either set virtual hosts or add to an existing set of virtual hosts.
    293      *
    294      * @param virtualHosts
    295      *            Array of virtual hosts that this context responds to. A null host name or null/empty array means any hostname is acceptable. Host names may be
    296      *            String representation of IP addresses. Host names may start with '*.' to wildcard one level of names.
    297      */
    298     public void addVirtualHosts(String[] virtualHosts)
    299     {
    300         if (virtualHosts == null)  // since this is add, we don't null the old ones
    301         {
    302             return;
    303         }
    304         else
    305         {
    306             List<String> currentVirtualHosts = null;
    307             if (_vhosts != null)
    308             {
    309                 currentVirtualHosts = new ArrayList<String>(Arrays.asList(_vhosts));
    310             }
    311             else
    312             {
    313                 currentVirtualHosts = new ArrayList<String>();
    314             }
    315 
    316             for (int i = 0; i < virtualHosts.length; i++)
    317             {
    318                 String normVhost = normalizeHostname(virtualHosts[i]);
    319                 if (!currentVirtualHosts.contains(normVhost))
    320                 {
    321                     currentVirtualHosts.add(normVhost);
    322                 }
    323             }
    324             _vhosts = currentVirtualHosts.toArray(new String[0]);
    325         }
    326     }
    327 
    328     /* ------------------------------------------------------------ */
    329     /**
    330      * Removes an array of virtual host entries, if this removes all entries the _vhosts will be set to null
    331      *
    332      *  @param virtualHosts
    333      *            Array of virtual hosts that this context responds to. A null host name or null/empty array means any hostname is acceptable. Host names may be
    334      *            String representation of IP addresses. Host names may start with '*.' to wildcard one level of names.
    335      */
    336     public void removeVirtualHosts(String[] virtualHosts)
    337     {
    338         if (virtualHosts == null)
    339         {
    340             return; // do nothing
    341         }
    342         else if ( _vhosts == null || _vhosts.length == 0)
    343         {
    344             return; // do nothing
    345         }
    346         else
    347         {
    348             List<String> existingVirtualHosts = new ArrayList<String>(Arrays.asList(_vhosts));
    349 
    350             for (int i = 0; i < virtualHosts.length; i++)
    351             {
    352                 String toRemoveVirtualHost = normalizeHostname(virtualHosts[i]);
    353                 if (existingVirtualHosts.contains(toRemoveVirtualHost))
    354                 {
    355                     existingVirtualHosts.remove(toRemoveVirtualHost);
    356                 }
    357             }
    358 
    359             if (existingVirtualHosts.isEmpty())
    360             {
    361                 _vhosts = null; // if we ended up removing them all, just null out _vhosts
    362             }
    363             else
    364             {
    365                 _vhosts = existingVirtualHosts.toArray(new String[0]);
    366             }
    367         }
    368     }
    369 
    370     /* ------------------------------------------------------------ */
    371     /**
    372      * Get the virtual hosts for the context. Only requests that have a matching host header or fully qualified URL will be passed to that context with a
    373      * virtual host name. A context with no virtual host names or a null virtual host name is available to all requests that are not served by a context with a
    374      * matching virtual host name.
    375      *
    376      * @return Array of virtual hosts that this context responds to. A null host name or empty array means any hostname is acceptable. Host names may be String
    377      *         representation of IP addresses. Host names may start with '*.' to wildcard one level of names.
    378      */
    379     public String[] getVirtualHosts()
    380     {
    381         return _vhosts;
    382     }
    383 
    384     /* ------------------------------------------------------------ */
    385     /**
    386      * @return an array of connector names that this context will accept a request from.
    387      */
    388     public String[] getConnectorNames()
    389     {
    390         if (_connectors == null || _connectors.size() == 0)
    391             return null;
    392 
    393         return _connectors.toArray(new String[_connectors.size()]);
    394     }
    395 
    396     /* ------------------------------------------------------------ */
    397     /**
    398      * Set the names of accepted connectors.
    399      *
    400      * Names are either "host:port" or a specific configured name for a connector.
    401      *
    402      * @param connectors
    403      *            If non null, an array of connector names that this context will accept a request from.
    404      */
    405     public void setConnectorNames(String[] connectors)
    406     {
    407         if (connectors == null || connectors.length == 0)
    408             _connectors = null;
    409         else
    410             _connectors = new HashSet<String>(Arrays.asList(connectors));
    411     }
    412 
    413     /* ------------------------------------------------------------ */
    414     /*
    415      * @see javax.servlet.ServletContext#getAttribute(java.lang.String)
    416      */
    417     public Object getAttribute(String name)
    418     {
    419         return _attributes.getAttribute(name);
    420     }
    421 
    422     /* ------------------------------------------------------------ */
    423     /*
    424      * @see javax.servlet.ServletContext#getAttributeNames()
    425      */
    426     @SuppressWarnings("unchecked")
    427     public Enumeration getAttributeNames()
    428     {
    429         return AttributesMap.getAttributeNamesCopy(_attributes);
    430     }
    431 
    432     /* ------------------------------------------------------------ */
    433     /**
    434      * @return Returns the attributes.
    435      */
    436     public Attributes getAttributes()
    437     {
    438         return _attributes;
    439     }
    440 
    441     /* ------------------------------------------------------------ */
    442     /**
    443      * @return Returns the classLoader.
    444      */
    445     public ClassLoader getClassLoader()
    446     {
    447         return _classLoader;
    448     }
    449 
    450     /* ------------------------------------------------------------ */
    451     /**
    452      * Make best effort to extract a file classpath from the context classloader
    453      *
    454      * @return Returns the classLoader.
    455      */
    456     public String getClassPath()
    457     {
    458         if (_classLoader == null || !(_classLoader instanceof URLClassLoader))
    459             return null;
    460         URLClassLoader loader = (URLClassLoader)_classLoader;
    461         URL[] urls = loader.getURLs();
    462         StringBuilder classpath = new StringBuilder();
    463         for (int i = 0; i < urls.length; i++)
    464         {
    465             try
    466             {
    467                 Resource resource = newResource(urls[i]);
    468                 File file = resource.getFile();
    469                 if (file != null && file.exists())
    470                 {
    471                     if (classpath.length() > 0)
    472                         classpath.append(File.pathSeparatorChar);
    473                     classpath.append(file.getAbsolutePath());
    474                 }
    475             }
    476             catch (IOException e)
    477             {
    478                 LOG.debug(e);
    479             }
    480         }
    481         if (classpath.length() == 0)
    482             return null;
    483         return classpath.toString();
    484     }
    485 
    486     /* ------------------------------------------------------------ */
    487     /**
    488      * @return Returns the _contextPath.
    489      */
    490     public String getContextPath()
    491     {
    492         return _contextPath;
    493     }
    494 
    495     /* ------------------------------------------------------------ */
    496     /*
    497      * @see javax.servlet.ServletContext#getInitParameter(java.lang.String)
    498      */
    499     public String getInitParameter(String name)
    500     {
    501         return _initParams.get(name);
    502     }
    503 
    504     /* ------------------------------------------------------------ */
    505     /*
    506      */
    507     public String setInitParameter(String name, String value)
    508     {
    509         return _initParams.put(name,value);
    510     }
    511 
    512     /* ------------------------------------------------------------ */
    513     /*
    514      * @see javax.servlet.ServletContext#getInitParameterNames()
    515      */
    516     @SuppressWarnings("rawtypes")
    517     public Enumeration getInitParameterNames()
    518     {
    519         return Collections.enumeration(_initParams.keySet());
    520     }
    521 
    522     /* ------------------------------------------------------------ */
    523     /**
    524      * @return Returns the initParams.
    525      */
    526     public Map<String, String> getInitParams()
    527     {
    528         return _initParams;
    529     }
    530 
    531     /* ------------------------------------------------------------ */
    532     /*
    533      * @see javax.servlet.ServletContext#getServletContextName()
    534      */
    535     public String getDisplayName()
    536     {
    537         return _displayName;
    538     }
    539 
    540     /* ------------------------------------------------------------ */
    541     public EventListener[] getEventListeners()
    542     {
    543         return _eventListeners;
    544     }
    545 
    546     /* ------------------------------------------------------------ */
    547     /**
    548      * Set the context event listeners.
    549      *
    550      * @param eventListeners
    551      *            the event listeners
    552      * @see ServletContextListener
    553      * @see ServletContextAttributeListener
    554      * @see ServletRequestListener
    555      * @see ServletRequestAttributeListener
    556      */
    557     public void setEventListeners(EventListener[] eventListeners)
    558     {
    559         _contextListeners = null;
    560         _contextAttributeListeners = null;
    561         _requestListeners = null;
    562         _requestAttributeListeners = null;
    563 
    564         _eventListeners = eventListeners;
    565 
    566         for (int i = 0; eventListeners != null && i < eventListeners.length; i++)
    567         {
    568             EventListener listener = _eventListeners[i];
    569 
    570             if (listener instanceof ServletContextListener)
    571                 _contextListeners = LazyList.add(_contextListeners,listener);
    572 
    573             if (listener instanceof ServletContextAttributeListener)
    574                 _contextAttributeListeners = LazyList.add(_contextAttributeListeners,listener);
    575 
    576             if (listener instanceof ServletRequestListener)
    577                 _requestListeners = LazyList.add(_requestListeners,listener);
    578 
    579             if (listener instanceof ServletRequestAttributeListener)
    580                 _requestAttributeListeners = LazyList.add(_requestAttributeListeners,listener);
    581         }
    582     }
    583 
    584     /* ------------------------------------------------------------ */
    585     /**
    586      * Add a context event listeners.
    587      *
    588      * @see ServletContextListener
    589      * @see ServletContextAttributeListener
    590      * @see ServletRequestListener
    591      * @see ServletRequestAttributeListener
    592      */
    593     public void addEventListener(EventListener listener)
    594     {
    595         //Only listeners added before the context starts last through a stop/start cycle
    596         if (!(isStarted() || isStarting()))
    597             _durableListeners = LazyList.add(_durableListeners, listener);
    598 
    599         setEventListeners((EventListener[])LazyList.addToArray(getEventListeners(),listener,EventListener.class));
    600     }
    601 
    602 
    603     /**
    604      * Apply any necessary restrictions on a programmatically added
    605      * listener.
    606      *
    607      * Superclasses should implement.
    608      *
    609      * @param listener
    610      */
    611     public void restrictEventListener (EventListener listener)
    612     {
    613     }
    614 
    615 
    616 
    617     /* ------------------------------------------------------------ */
    618     /**
    619      * @return true if this context is accepting new requests
    620      */
    621     public boolean isShutdown()
    622     {
    623         synchronized (this)
    624         {
    625             return !_shutdown;
    626         }
    627     }
    628 
    629     /* ------------------------------------------------------------ */
    630     /**
    631      * Set shutdown status. This field allows for graceful shutdown of a context. A started context may be put into non accepting state so that existing
    632      * requests can complete, but no new requests are accepted.
    633      *
    634      * @param shutdown
    635      *            true if this context is (not?) accepting new requests
    636      */
    637     public void setShutdown(boolean shutdown)
    638     {
    639         synchronized (this)
    640         {
    641             _shutdown = shutdown;
    642             _availability = isRunning()?(_shutdown?__SHUTDOWN:_available?__AVAILABLE:__UNAVAILABLE):__STOPPED;
    643         }
    644     }
    645 
    646     /* ------------------------------------------------------------ */
    647     /**
    648      * @return false if this context is unavailable (sends 503)
    649      */
    650     public boolean isAvailable()
    651     {
    652         synchronized (this)
    653         {
    654             return _available;
    655         }
    656     }
    657 
    658     /* ------------------------------------------------------------ */
    659     /**
    660      * Set Available status.
    661      */
    662     public void setAvailable(boolean available)
    663     {
    664         synchronized (this)
    665         {
    666             _available = available;
    667             _availability = isRunning()?(_shutdown?__SHUTDOWN:_available?__AVAILABLE:__UNAVAILABLE):__STOPPED;
    668         }
    669     }
    670 
    671     /* ------------------------------------------------------------ */
    672     public Logger getLogger()
    673     {
    674         return _logger;
    675     }
    676 
    677     /* ------------------------------------------------------------ */
    678     public void setLogger(Logger logger)
    679     {
    680         _logger = logger;
    681     }
    682 
    683     /* ------------------------------------------------------------ */
    684     /*
    685      * @see org.eclipse.thread.AbstractLifeCycle#doStart()
    686      */
    687     @Override
    688     protected void doStart() throws Exception
    689     {
    690         _availability = __STOPPED;
    691 
    692         if (_contextPath == null)
    693             throw new IllegalStateException("Null contextPath");
    694 
    695         _logger = Log.getLogger(getDisplayName() == null?getContextPath():getDisplayName());
    696         ClassLoader old_classloader = null;
    697         Thread current_thread = null;
    698         Context old_context = null;
    699 
    700         try
    701         {
    702             // Set the classloader
    703             if (_classLoader != null)
    704             {
    705                 current_thread = Thread.currentThread();
    706                 old_classloader = current_thread.getContextClassLoader();
    707                 current_thread.setContextClassLoader(_classLoader);
    708             }
    709 
    710             if (_mimeTypes == null)
    711                 _mimeTypes = new MimeTypes();
    712 
    713             old_context = __context.get();
    714             __context.set(_scontext);
    715 
    716             // defers the calling of super.doStart()
    717             startContext();
    718 
    719             synchronized(this)
    720             {
    721                 _availability = _shutdown?__SHUTDOWN:_available?__AVAILABLE:__UNAVAILABLE;
    722             }
    723         }
    724         finally
    725         {
    726             __context.set(old_context);
    727 
    728             // reset the classloader
    729             if (_classLoader != null)
    730             {
    731                 current_thread.setContextClassLoader(old_classloader);
    732             }
    733 
    734         }
    735     }
    736 
    737     /* ------------------------------------------------------------ */
    738     /**
    739      * Extensible startContext. this method is called from {@link ContextHandler#doStart()} instead of a call to super.doStart(). This allows derived classes to
    740      * insert additional handling (Eg configuration) before the call to super.doStart by this method will start contained handlers.
    741      *
    742      * @see org.eclipse.jetty.server.handler.ContextHandler.Context
    743      */
    744     protected void startContext() throws Exception
    745     {
    746         String managedAttributes = _initParams.get(MANAGED_ATTRIBUTES);
    747         if (managedAttributes != null)
    748         {
    749             _managedAttributes = new HashMap<String, Object>();
    750             String[] attributes = managedAttributes.split(",");
    751             for (String attribute : attributes)
    752                 _managedAttributes.put(attribute,null);
    753 
    754             Enumeration e = _scontext.getAttributeNames();
    755             while (e.hasMoreElements())
    756             {
    757                 String name = (String)e.nextElement();
    758                 Object value = _scontext.getAttribute(name);
    759                 checkManagedAttribute(name,value);
    760             }
    761         }
    762 
    763         super.doStart();
    764 
    765         if (_errorHandler != null)
    766             _errorHandler.start();
    767 
    768         // Context listeners
    769         if (_contextListeners != null)
    770         {
    771             ServletContextEvent event = new ServletContextEvent(_scontext);
    772             for (int i = 0; i < LazyList.size(_contextListeners); i++)
    773             {
    774                 callContextInitialized(((ServletContextListener)LazyList.get(_contextListeners, i)), event);
    775             }
    776         }
    777     }
    778 
    779     /* ------------------------------------------------------------ */
    780     public void callContextInitialized (ServletContextListener l, ServletContextEvent e)
    781     {
    782         l.contextInitialized(e);
    783     }
    784 
    785     /* ------------------------------------------------------------ */
    786     public void callContextDestroyed (ServletContextListener l, ServletContextEvent e)
    787     {
    788         l.contextDestroyed(e);
    789     }
    790 
    791     /* ------------------------------------------------------------ */
    792     /*
    793      * @see org.eclipse.thread.AbstractLifeCycle#doStop()
    794      */
    795     @Override
    796     protected void doStop() throws Exception
    797     {
    798         _availability = __STOPPED;
    799 
    800         ClassLoader old_classloader = null;
    801         Thread current_thread = null;
    802 
    803         Context old_context = __context.get();
    804         __context.set(_scontext);
    805         try
    806         {
    807             // Set the classloader
    808             if (_classLoader != null)
    809             {
    810                 current_thread = Thread.currentThread();
    811                 old_classloader = current_thread.getContextClassLoader();
    812                 current_thread.setContextClassLoader(_classLoader);
    813             }
    814 
    815             super.doStop();
    816 
    817             // Context listeners
    818             if (_contextListeners != null)
    819             {
    820                 ServletContextEvent event = new ServletContextEvent(_scontext);
    821                 for (int i = LazyList.size(_contextListeners); i-- > 0;)
    822                 {
    823                     ((ServletContextListener)LazyList.get(_contextListeners,i)).contextDestroyed(event);
    824                 }
    825             }
    826 
    827             //remove all non-durable listeners
    828             setEventListeners((EventListener[])LazyList.toArray(_durableListeners, EventListener.class));
    829             _durableListeners = null;
    830 
    831             if (_errorHandler != null)
    832                 _errorHandler.stop();
    833 
    834             Enumeration e = _scontext.getAttributeNames();
    835             while (e.hasMoreElements())
    836             {
    837                 String name = (String)e.nextElement();
    838                 checkManagedAttribute(name,null);
    839             }
    840         }
    841         finally
    842         {
    843             LOG.info("stopped {}",this);
    844             __context.set(old_context);
    845             // reset the classloader
    846             if (_classLoader != null)
    847                 current_thread.setContextClassLoader(old_classloader);
    848         }
    849 
    850         _contextAttributes.clearAttributes();
    851     }
    852 
    853     /* ------------------------------------------------------------ */
    854     /*
    855      * @see org.eclipse.jetty.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
    856      */
    857     public boolean checkContext(final String target, final Request baseRequest, final HttpServletResponse response) throws IOException, ServletException
    858     {
    859         DispatcherType dispatch = baseRequest.getDispatcherType();
    860 
    861         switch (_availability)
    862         {
    863             case __STOPPED:
    864             case __SHUTDOWN:
    865                 return false;
    866             case __UNAVAILABLE:
    867                 baseRequest.setHandled(true);
    868                 response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
    869                 return false;
    870             default:
    871                 if ((DispatcherType.REQUEST.equals(dispatch) && baseRequest.isHandled()))
    872                     return false;
    873         }
    874 
    875         // Check the vhosts
    876         if (_vhosts != null && _vhosts.length > 0)
    877         {
    878             String vhost = normalizeHostname(baseRequest.getServerName());
    879 
    880             boolean match = false;
    881 
    882             // TODO non-linear lookup
    883             for (int i = 0; !match && i < _vhosts.length; i++)
    884             {
    885                 String contextVhost = _vhosts[i];
    886                 if (contextVhost == null)
    887                     continue;
    888                 if (contextVhost.startsWith("*."))
    889                 {
    890                     // wildcard only at the beginning, and only for one additional subdomain level
    891                     match = contextVhost.regionMatches(true,2,vhost,vhost.indexOf(".") + 1,contextVhost.length() - 2);
    892                 }
    893                 else
    894                     match = contextVhost.equalsIgnoreCase(vhost);
    895             }
    896             if (!match)
    897                 return false;
    898         }
    899 
    900         // Check the connector
    901         if (_connectors != null && _connectors.size() > 0)
    902         {
    903             String connector = AbstractHttpConnection.getCurrentConnection().getConnector().getName();
    904             if (connector == null || !_connectors.contains(connector))
    905                 return false;
    906         }
    907 
    908         // Are we not the root context?
    909         if (_contextPath.length() > 1)
    910         {
    911             // reject requests that are not for us
    912             if (!target.startsWith(_contextPath))
    913                 return false;
    914             if (target.length() > _contextPath.length() && target.charAt(_contextPath.length()) != '/')
    915                 return false;
    916 
    917             // redirect null path infos
    918             if (!_allowNullPathInfo && _contextPath.length() == target.length())
    919             {
    920                 // context request must end with /
    921                 baseRequest.setHandled(true);
    922                 if (baseRequest.getQueryString() != null)
    923                     response.sendRedirect(URIUtil.addPaths(baseRequest.getRequestURI(),URIUtil.SLASH) + "?" + baseRequest.getQueryString());
    924                 else
    925                     response.sendRedirect(URIUtil.addPaths(baseRequest.getRequestURI(),URIUtil.SLASH));
    926                 return false;
    927             }
    928         }
    929 
    930         return true;
    931     }
    932 
    933     /* ------------------------------------------------------------ */
    934     /**
    935      * @see org.eclipse.jetty.server.handler.ScopedHandler#doScope(java.lang.String, org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest,
    936      *      javax.servlet.http.HttpServletResponse)
    937      */
    938     @Override
    939     public void doScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
    940     {
    941         if (LOG.isDebugEnabled())
    942             LOG.debug("scope {}|{}|{} @ {}",baseRequest.getContextPath(),baseRequest.getServletPath(),baseRequest.getPathInfo(),this);
    943 
    944         Context old_context = null;
    945         String old_context_path = null;
    946         String old_servlet_path = null;
    947         String old_path_info = null;
    948         ClassLoader old_classloader = null;
    949         Thread current_thread = null;
    950         String pathInfo = target;
    951 
    952         DispatcherType dispatch = baseRequest.getDispatcherType();
    953 
    954         old_context = baseRequest.getContext();
    955 
    956         // Are we already in this context?
    957         if (old_context != _scontext)
    958         {
    959             // check the target.
    960             if (DispatcherType.REQUEST.equals(dispatch) || DispatcherType.ASYNC.equals(dispatch) || (DispatcherType.ERROR.equals(dispatch) && baseRequest.getAsyncContinuation().isExpired()))
    961             {
    962                 if (_compactPath)
    963                     target = URIUtil.compactPath(target);
    964                 if (!checkContext(target,baseRequest,response))
    965                     return;
    966 
    967                 if (target.length() > _contextPath.length())
    968                 {
    969                     if (_contextPath.length() > 1)
    970                         target = target.substring(_contextPath.length());
    971                     pathInfo = target;
    972                 }
    973                 else if (_contextPath.length() == 1)
    974                 {
    975                     target = URIUtil.SLASH;
    976                     pathInfo = URIUtil.SLASH;
    977                 }
    978                 else
    979                 {
    980                     target = URIUtil.SLASH;
    981                     pathInfo = null;
    982                 }
    983             }
    984 
    985             // Set the classloader
    986             if (_classLoader != null)
    987             {
    988                 current_thread = Thread.currentThread();
    989                 old_classloader = current_thread.getContextClassLoader();
    990                 current_thread.setContextClassLoader(_classLoader);
    991             }
    992         }
    993 
    994         try
    995         {
    996             old_context_path = baseRequest.getContextPath();
    997             old_servlet_path = baseRequest.getServletPath();
    998             old_path_info = baseRequest.getPathInfo();
    999 
   1000             // Update the paths
   1001             baseRequest.setContext(_scontext);
   1002             __context.set(_scontext);
   1003             if (!DispatcherType.INCLUDE.equals(dispatch) && target.startsWith("/"))
   1004             {
   1005                 if (_contextPath.length() == 1)
   1006                     baseRequest.setContextPath("");
   1007                 else
   1008                     baseRequest.setContextPath(_contextPath);
   1009                 baseRequest.setServletPath(null);
   1010                 baseRequest.setPathInfo(pathInfo);
   1011             }
   1012 
   1013             if (LOG.isDebugEnabled())
   1014                 LOG.debug("context={}|{}|{} @ {}",baseRequest.getContextPath(),baseRequest.getServletPath(), baseRequest.getPathInfo(),this);
   1015 
   1016             // start manual inline of nextScope(target,baseRequest,request,response);
   1017             if (never())
   1018                 nextScope(target,baseRequest,request,response);
   1019             else if (_nextScope != null)
   1020                 _nextScope.doScope(target,baseRequest,request,response);
   1021             else if (_outerScope != null)
   1022                 _outerScope.doHandle(target,baseRequest,request,response);
   1023             else
   1024                 doHandle(target,baseRequest,request,response);
   1025             // end manual inline (pathentic attempt to reduce stack depth)
   1026         }
   1027         finally
   1028         {
   1029             if (old_context != _scontext)
   1030             {
   1031                 // reset the classloader
   1032                 if (_classLoader != null)
   1033                 {
   1034                     current_thread.setContextClassLoader(old_classloader);
   1035                 }
   1036 
   1037                 // reset the context and servlet path.
   1038                 baseRequest.setContext(old_context);
   1039                 __context.set(old_context);
   1040                 baseRequest.setContextPath(old_context_path);
   1041                 baseRequest.setServletPath(old_servlet_path);
   1042                 baseRequest.setPathInfo(old_path_info);
   1043             }
   1044         }
   1045     }
   1046 
   1047     /* ------------------------------------------------------------ */
   1048     /**
   1049      * @see org.eclipse.jetty.server.handler.ScopedHandler#doHandle(java.lang.String, org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest,
   1050      *      javax.servlet.http.HttpServletResponse)
   1051      */
   1052     @Override
   1053     public void doHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
   1054     {
   1055         final DispatcherType dispatch = baseRequest.getDispatcherType();
   1056         final boolean new_context = baseRequest.takeNewContext();
   1057         try
   1058         {
   1059             if (new_context)
   1060             {
   1061                 // Handle the REALLY SILLY request events!
   1062                 if (_requestAttributeListeners != null)
   1063                 {
   1064                     final int s = LazyList.size(_requestAttributeListeners);
   1065                     for (int i = 0; i < s; i++)
   1066                         baseRequest.addEventListener(((EventListener)LazyList.get(_requestAttributeListeners,i)));
   1067                 }
   1068 
   1069                 if (_requestListeners != null)
   1070                 {
   1071                     final int s = LazyList.size(_requestListeners);
   1072                     final ServletRequestEvent sre = new ServletRequestEvent(_scontext,request);
   1073                     for (int i = 0; i < s; i++)
   1074                         ((ServletRequestListener)LazyList.get(_requestListeners,i)).requestInitialized(sre);
   1075                 }
   1076             }
   1077 
   1078             if (DispatcherType.REQUEST.equals(dispatch) && isProtectedTarget(target))
   1079                 throw new HttpException(HttpServletResponse.SC_NOT_FOUND);
   1080 
   1081             // start manual inline of nextHandle(target,baseRequest,request,response);
   1082             // noinspection ConstantIfStatement
   1083             if (never())
   1084                 nextHandle(target,baseRequest,request,response);
   1085             else if (_nextScope != null && _nextScope == _handler)
   1086                 _nextScope.doHandle(target,baseRequest,request,response);
   1087             else if (_handler != null)
   1088                 _handler.handle(target,baseRequest,request,response);
   1089             // end manual inline
   1090         }
   1091         catch (HttpException e)
   1092         {
   1093             LOG.debug(e);
   1094             baseRequest.setHandled(true);
   1095             response.sendError(e.getStatus(),e.getReason());
   1096         }
   1097         finally
   1098         {
   1099             // Handle more REALLY SILLY request events!
   1100             if (new_context)
   1101             {
   1102                 if (_requestListeners != null)
   1103                 {
   1104                     final ServletRequestEvent sre = new ServletRequestEvent(_scontext,request);
   1105                     for (int i = LazyList.size(_requestListeners); i-- > 0;)
   1106                         ((ServletRequestListener)LazyList.get(_requestListeners,i)).requestDestroyed(sre);
   1107                 }
   1108 
   1109                 if (_requestAttributeListeners != null)
   1110                 {
   1111                     for (int i = LazyList.size(_requestAttributeListeners); i-- > 0;)
   1112                         baseRequest.removeEventListener(((EventListener)LazyList.get(_requestAttributeListeners,i)));
   1113                 }
   1114             }
   1115         }
   1116     }
   1117 
   1118     /* ------------------------------------------------------------ */
   1119     /*
   1120      * Handle a runnable in this context
   1121      */
   1122     public void handle(Runnable runnable)
   1123     {
   1124         ClassLoader old_classloader = null;
   1125         Thread current_thread = null;
   1126         Context old_context = null;
   1127         try
   1128         {
   1129             old_context = __context.get();
   1130             __context.set(_scontext);
   1131 
   1132             // Set the classloader
   1133             if (_classLoader != null)
   1134             {
   1135                 current_thread = Thread.currentThread();
   1136                 old_classloader = current_thread.getContextClassLoader();
   1137                 current_thread.setContextClassLoader(_classLoader);
   1138             }
   1139 
   1140             runnable.run();
   1141         }
   1142         finally
   1143         {
   1144             __context.set(old_context);
   1145             if (old_classloader != null)
   1146             {
   1147                 current_thread.setContextClassLoader(old_classloader);
   1148             }
   1149         }
   1150     }
   1151 
   1152     /* ------------------------------------------------------------ */
   1153     /**
   1154      * Check the target. Called by {@link #handle(String, Request, HttpServletRequest, HttpServletResponse)} when a target within a context is determined. If
   1155      * the target is protected, 404 is returned.
   1156      */
   1157     /* ------------------------------------------------------------ */
   1158     public boolean isProtectedTarget(String target)
   1159     {
   1160         if (target == null || _protectedTargets == null)
   1161             return false;
   1162 
   1163         while (target.startsWith("//"))
   1164             target=URIUtil.compactPath(target);
   1165 
   1166         boolean isProtected = false;
   1167         int i=0;
   1168         while (!isProtected && i<_protectedTargets.length)
   1169         {
   1170             isProtected = StringUtil.startsWithIgnoreCase(target, _protectedTargets[i++]);
   1171         }
   1172         return isProtected;
   1173     }
   1174 
   1175 
   1176     public void setProtectedTargets (String[] targets)
   1177     {
   1178         if (targets == null)
   1179         {
   1180             _protectedTargets = null;
   1181             return;
   1182         }
   1183 
   1184         _protectedTargets = new String[targets.length];
   1185         System.arraycopy(targets, 0, _protectedTargets, 0, targets.length);
   1186     }
   1187 
   1188     public String[] getProtectedTargets ()
   1189     {
   1190         if (_protectedTargets == null)
   1191             return null;
   1192 
   1193         String[] tmp = new String[_protectedTargets.length];
   1194         System.arraycopy(_protectedTargets, 0, tmp, 0, _protectedTargets.length);
   1195         return tmp;
   1196     }
   1197 
   1198 
   1199     /* ------------------------------------------------------------ */
   1200     /*
   1201      * @see javax.servlet.ServletContext#removeAttribute(java.lang.String)
   1202      */
   1203     public void removeAttribute(String name)
   1204     {
   1205         checkManagedAttribute(name,null);
   1206         _attributes.removeAttribute(name);
   1207     }
   1208 
   1209     /* ------------------------------------------------------------ */
   1210     /*
   1211      * Set a context attribute. Attributes set via this API cannot be overriden by the ServletContext.setAttribute API. Their lifecycle spans the stop/start of
   1212      * a context. No attribute listener events are triggered by this API.
   1213      *
   1214      * @see javax.servlet.ServletContext#setAttribute(java.lang.String, java.lang.Object)
   1215      */
   1216     public void setAttribute(String name, Object value)
   1217     {
   1218         checkManagedAttribute(name,value);
   1219         _attributes.setAttribute(name,value);
   1220     }
   1221 
   1222     /* ------------------------------------------------------------ */
   1223     /**
   1224      * @param attributes
   1225      *            The attributes to set.
   1226      */
   1227     public void setAttributes(Attributes attributes)
   1228     {
   1229         _attributes.clearAttributes();
   1230         _attributes.addAll(attributes);
   1231         Enumeration e = _attributes.getAttributeNames();
   1232         while (e.hasMoreElements())
   1233         {
   1234             String name = (String)e.nextElement();
   1235             checkManagedAttribute(name,attributes.getAttribute(name));
   1236         }
   1237     }
   1238 
   1239     /* ------------------------------------------------------------ */
   1240     public void clearAttributes()
   1241     {
   1242         Enumeration e = _attributes.getAttributeNames();
   1243         while (e.hasMoreElements())
   1244         {
   1245             String name = (String)e.nextElement();
   1246             checkManagedAttribute(name,null);
   1247         }
   1248         _attributes.clearAttributes();
   1249     }
   1250 
   1251     /* ------------------------------------------------------------ */
   1252     public void checkManagedAttribute(String name, Object value)
   1253     {
   1254         if (_managedAttributes != null && _managedAttributes.containsKey(name))
   1255         {
   1256             setManagedAttribute(name,value);
   1257         }
   1258     }
   1259 
   1260     /* ------------------------------------------------------------ */
   1261     public void setManagedAttribute(String name, Object value)
   1262     {
   1263         Object old = _managedAttributes.put(name,value);
   1264         getServer().getContainer().update(this,old,value,name,true);
   1265     }
   1266 
   1267     /* ------------------------------------------------------------ */
   1268     /**
   1269      * @param classLoader
   1270      *            The classLoader to set.
   1271      */
   1272     public void setClassLoader(ClassLoader classLoader)
   1273     {
   1274         _classLoader = classLoader;
   1275     }
   1276 
   1277     /* ------------------------------------------------------------ */
   1278     /**
   1279      * @param contextPath
   1280      *            The _contextPath to set.
   1281      */
   1282     public void setContextPath(String contextPath)
   1283     {
   1284         if (contextPath != null && contextPath.length() > 1 && contextPath.endsWith("/"))
   1285             throw new IllegalArgumentException("ends with /");
   1286         _contextPath = contextPath;
   1287 
   1288         if (getServer() != null && (getServer().isStarting() || getServer().isStarted()))
   1289         {
   1290             Handler[] contextCollections = getServer().getChildHandlersByClass(ContextHandlerCollection.class);
   1291             for (int h = 0; contextCollections != null && h < contextCollections.length; h++)
   1292                 ((ContextHandlerCollection)contextCollections[h]).mapContexts();
   1293         }
   1294     }
   1295 
   1296     /* ------------------------------------------------------------ */
   1297     /**
   1298      * @param servletContextName
   1299      *            The servletContextName to set.
   1300      */
   1301     public void setDisplayName(String servletContextName)
   1302     {
   1303         _displayName = servletContextName;
   1304     }
   1305 
   1306     /* ------------------------------------------------------------ */
   1307     /**
   1308      * @return Returns the resourceBase.
   1309      */
   1310     public Resource getBaseResource()
   1311     {
   1312         if (_baseResource == null)
   1313             return null;
   1314         return _baseResource;
   1315     }
   1316 
   1317     /* ------------------------------------------------------------ */
   1318     /**
   1319      * @return Returns the base resource as a string.
   1320      */
   1321     public String getResourceBase()
   1322     {
   1323         if (_baseResource == null)
   1324             return null;
   1325         return _baseResource.toString();
   1326     }
   1327 
   1328     /* ------------------------------------------------------------ */
   1329     /**
   1330      * @param base
   1331      *            The resourceBase to set.
   1332      */
   1333     public void setBaseResource(Resource base)
   1334     {
   1335         _baseResource = base;
   1336     }
   1337 
   1338     /* ------------------------------------------------------------ */
   1339     /**
   1340      * @param resourceBase
   1341      *            The base resource as a string.
   1342      */
   1343     public void setResourceBase(String resourceBase)
   1344     {
   1345         try
   1346         {
   1347             setBaseResource(newResource(resourceBase));
   1348         }
   1349         catch (Exception e)
   1350         {
   1351             LOG.warn(e.toString());
   1352             LOG.debug(e);
   1353             throw new IllegalArgumentException(resourceBase);
   1354         }
   1355     }
   1356 
   1357     /* ------------------------------------------------------------ */
   1358     /**
   1359      * @return True if aliases are allowed
   1360      */
   1361     public boolean isAliases()
   1362     {
   1363         return _aliasesAllowed;
   1364     }
   1365 
   1366     /* ------------------------------------------------------------ */
   1367     /**
   1368      * @param aliases
   1369      *            aliases are allowed
   1370      */
   1371     public void setAliases(boolean aliases)
   1372     {
   1373         _aliasesAllowed = aliases;
   1374     }
   1375 
   1376     /* ------------------------------------------------------------ */
   1377     /**
   1378      * @return Returns the mimeTypes.
   1379      */
   1380     public MimeTypes getMimeTypes()
   1381     {
   1382         if (_mimeTypes == null)
   1383             _mimeTypes = new MimeTypes();
   1384         return _mimeTypes;
   1385     }
   1386 
   1387     /* ------------------------------------------------------------ */
   1388     /**
   1389      * @param mimeTypes
   1390      *            The mimeTypes to set.
   1391      */
   1392     public void setMimeTypes(MimeTypes mimeTypes)
   1393     {
   1394         _mimeTypes = mimeTypes;
   1395     }
   1396 
   1397     /* ------------------------------------------------------------ */
   1398     /**
   1399      */
   1400     public void setWelcomeFiles(String[] files)
   1401     {
   1402         _welcomeFiles = files;
   1403     }
   1404 
   1405     /* ------------------------------------------------------------ */
   1406     /**
   1407      * @return The names of the files which the server should consider to be welcome files in this context.
   1408      * @see <a href="http://jcp.org/aboutJava/communityprocess/final/jsr154/index.html">The Servlet Specification</a>
   1409      * @see #setWelcomeFiles
   1410      */
   1411     public String[] getWelcomeFiles()
   1412     {
   1413         return _welcomeFiles;
   1414     }
   1415 
   1416     /* ------------------------------------------------------------ */
   1417     /**
   1418      * @return Returns the errorHandler.
   1419      */
   1420     public ErrorHandler getErrorHandler()
   1421     {
   1422         return _errorHandler;
   1423     }
   1424 
   1425     /* ------------------------------------------------------------ */
   1426     /**
   1427      * @param errorHandler
   1428      *            The errorHandler to set.
   1429      */
   1430     public void setErrorHandler(ErrorHandler errorHandler)
   1431     {
   1432         if (errorHandler != null)
   1433             errorHandler.setServer(getServer());
   1434         if (getServer() != null)
   1435             getServer().getContainer().update(this,_errorHandler,errorHandler,"errorHandler",true);
   1436         _errorHandler = errorHandler;
   1437     }
   1438 
   1439     /* ------------------------------------------------------------ */
   1440     public int getMaxFormContentSize()
   1441     {
   1442         return _maxFormContentSize;
   1443     }
   1444 
   1445     /* ------------------------------------------------------------ */
   1446     /**
   1447      * Set the maximum size of a form post, to protect against DOS attacks from large forms.
   1448      * @param maxSize
   1449      */
   1450     public void setMaxFormContentSize(int maxSize)
   1451     {
   1452         _maxFormContentSize = maxSize;
   1453     }
   1454 
   1455     /* ------------------------------------------------------------ */
   1456     public int getMaxFormKeys()
   1457     {
   1458         return _maxFormKeys;
   1459     }
   1460 
   1461     /* ------------------------------------------------------------ */
   1462     /**
   1463      * Set the maximum number of form Keys to protect against DOS attack from crafted hash keys.
   1464      * @param max
   1465      */
   1466     public void setMaxFormKeys(int max)
   1467     {
   1468         _maxFormKeys = max;
   1469     }
   1470 
   1471     /* ------------------------------------------------------------ */
   1472     /**
   1473      * @return True if URLs are compacted to replace multiple '/'s with a single '/'
   1474      */
   1475     public boolean isCompactPath()
   1476     {
   1477         return _compactPath;
   1478     }
   1479 
   1480     /* ------------------------------------------------------------ */
   1481     /**
   1482      * @param compactPath
   1483      *            True if URLs are compacted to replace multiple '/'s with a single '/'
   1484      */
   1485     public void setCompactPath(boolean compactPath)
   1486     {
   1487         _compactPath = compactPath;
   1488     }
   1489 
   1490     /* ------------------------------------------------------------ */
   1491     @Override
   1492     public String toString()
   1493     {
   1494         String[] vhosts = getVirtualHosts();
   1495 
   1496         StringBuilder b = new StringBuilder();
   1497 
   1498         Package pkg = getClass().getPackage();
   1499         if (pkg != null)
   1500         {
   1501             String p = pkg.getName();
   1502             if (p != null && p.length() > 0)
   1503             {
   1504                 String[] ss = p.split("\\.");
   1505                 for (String s : ss)
   1506                     b.append(s.charAt(0)).append('.');
   1507             }
   1508         }
   1509         b.append(getClass().getSimpleName());
   1510         b.append('{').append(getContextPath()).append(',').append(getBaseResource());
   1511 
   1512         if (vhosts != null && vhosts.length > 0)
   1513             b.append(',').append(vhosts[0]);
   1514         b.append('}');
   1515 
   1516         return b.toString();
   1517     }
   1518 
   1519     /* ------------------------------------------------------------ */
   1520     public synchronized Class<?> loadClass(String className) throws ClassNotFoundException
   1521     {
   1522         if (className == null)
   1523             return null;
   1524 
   1525         if (_classLoader == null)
   1526             return Loader.loadClass(this.getClass(),className);
   1527 
   1528         return _classLoader.loadClass(className);
   1529     }
   1530 
   1531     /* ------------------------------------------------------------ */
   1532     public void addLocaleEncoding(String locale, String encoding)
   1533     {
   1534         if (_localeEncodingMap == null)
   1535             _localeEncodingMap = new HashMap<String, String>();
   1536         _localeEncodingMap.put(locale,encoding);
   1537     }
   1538 
   1539     /* ------------------------------------------------------------ */
   1540     public String getLocaleEncoding(String locale)
   1541     {
   1542         if (_localeEncodingMap == null)
   1543             return null;
   1544         String encoding = _localeEncodingMap.get(locale);
   1545         return encoding;
   1546     }
   1547 
   1548     /* ------------------------------------------------------------ */
   1549     /**
   1550      * Get the character encoding for a locale. The full locale name is first looked up in the map of encodings. If no encoding is found, then the locale
   1551      * language is looked up.
   1552      *
   1553      * @param locale
   1554      *            a <code>Locale</code> value
   1555      * @return a <code>String</code> representing the character encoding for the locale or null if none found.
   1556      */
   1557     public String getLocaleEncoding(Locale locale)
   1558     {
   1559         if (_localeEncodingMap == null)
   1560             return null;
   1561         String encoding = _localeEncodingMap.get(locale.toString());
   1562         if (encoding == null)
   1563             encoding = _localeEncodingMap.get(locale.getLanguage());
   1564         return encoding;
   1565     }
   1566 
   1567     /* ------------------------------------------------------------ */
   1568     /*
   1569      */
   1570     public Resource getResource(String path) throws MalformedURLException
   1571     {
   1572         if (path == null || !path.startsWith(URIUtil.SLASH))
   1573             throw new MalformedURLException(path);
   1574 
   1575         if (_baseResource == null)
   1576             return null;
   1577 
   1578         try
   1579         {
   1580             path = URIUtil.canonicalPath(path);
   1581             Resource resource = _baseResource.addPath(path);
   1582 
   1583             if (checkAlias(path,resource))
   1584                 return resource;
   1585             return null;
   1586         }
   1587         catch (Exception e)
   1588         {
   1589             LOG.ignore(e);
   1590         }
   1591 
   1592         return null;
   1593     }
   1594 
   1595     /* ------------------------------------------------------------ */
   1596     public boolean checkAlias(String path, Resource resource)
   1597     {
   1598         // Is the resource aliased?
   1599         if (!_aliasesAllowed && resource.getAlias() != null)
   1600         {
   1601             if (LOG.isDebugEnabled())
   1602                 LOG.debug("Aliased resource: " + resource + "~=" + resource.getAlias());
   1603 
   1604             // alias checks
   1605             for (Iterator<AliasCheck> i=_aliasChecks.iterator();i.hasNext();)
   1606             {
   1607                 AliasCheck check = i.next();
   1608                 if (check.check(path,resource))
   1609                 {
   1610                     if (LOG.isDebugEnabled())
   1611                         LOG.debug("Aliased resource: " + resource + " approved by " + check);
   1612                     return true;
   1613                 }
   1614             }
   1615             return false;
   1616         }
   1617         return true;
   1618     }
   1619 
   1620     /* ------------------------------------------------------------ */
   1621     /**
   1622      * Convert URL to Resource wrapper for {@link Resource#newResource(URL)} enables extensions to provide alternate resource implementations.
   1623      */
   1624     public Resource newResource(URL url) throws IOException
   1625     {
   1626         return Resource.newResource(url);
   1627     }
   1628 
   1629     /* ------------------------------------------------------------ */
   1630     /**
   1631      * Convert a URL or path to a Resource. The default implementation is a wrapper for {@link Resource#newResource(String)}.
   1632      *
   1633      * @param urlOrPath
   1634      *            The URL or path to convert
   1635      * @return The Resource for the URL/path
   1636      * @throws IOException
   1637      *             The Resource could not be created.
   1638      */
   1639     public Resource newResource(String urlOrPath) throws IOException
   1640     {
   1641         return Resource.newResource(urlOrPath);
   1642     }
   1643 
   1644     /* ------------------------------------------------------------ */
   1645     /*
   1646      */
   1647     public Set<String> getResourcePaths(String path)
   1648     {
   1649         try
   1650         {
   1651             path = URIUtil.canonicalPath(path);
   1652             Resource resource = getResource(path);
   1653 
   1654             if (resource != null && resource.exists())
   1655             {
   1656                 if (!path.endsWith(URIUtil.SLASH))
   1657                     path = path + URIUtil.SLASH;
   1658 
   1659                 String[] l = resource.list();
   1660                 if (l != null)
   1661                 {
   1662                     HashSet<String> set = new HashSet<String>();
   1663                     for (int i = 0; i < l.length; i++)
   1664                         set.add(path + l[i]);
   1665                     return set;
   1666                 }
   1667             }
   1668         }
   1669         catch (Exception e)
   1670         {
   1671             LOG.ignore(e);
   1672         }
   1673         return Collections.emptySet();
   1674     }
   1675 
   1676     /* ------------------------------------------------------------ */
   1677     private String normalizeHostname(String host)
   1678     {
   1679         if (host == null)
   1680             return null;
   1681 
   1682         if (host.endsWith("."))
   1683             return host.substring(0,host.length() - 1);
   1684 
   1685         return host;
   1686     }
   1687 
   1688     /* ------------------------------------------------------------ */
   1689     /**
   1690      * Add an AliasCheck instance to possibly permit aliased resources
   1691      * @param check The alias checker
   1692      */
   1693     public void addAliasCheck(AliasCheck check)
   1694     {
   1695         _aliasChecks.add(check);
   1696     }
   1697 
   1698     /* ------------------------------------------------------------ */
   1699     /**
   1700      * @return Mutable list of Alias checks
   1701      */
   1702     public List<AliasCheck> getAliasChecks()
   1703     {
   1704         return _aliasChecks;
   1705     }
   1706 
   1707     /* ------------------------------------------------------------ */
   1708     /**
   1709      * Context.
   1710      * <p>
   1711      * A partial implementation of {@link javax.servlet.ServletContext}. A complete implementation is provided by the derived {@link ContextHandler}.
   1712      * </p>
   1713      *
   1714      *
   1715      */
   1716     public class Context implements ServletContext
   1717     {
   1718         protected int _majorVersion = 3;
   1719         protected int _minorVersion = 0;
   1720         protected boolean _enabled = true; //whether or not the dynamic API is enabled for callers
   1721 
   1722         /* ------------------------------------------------------------ */
   1723         protected Context()
   1724         {
   1725         }
   1726 
   1727         /* ------------------------------------------------------------ */
   1728         public ContextHandler getContextHandler()
   1729         {
   1730             // TODO reduce visibility of this method
   1731             return ContextHandler.this;
   1732         }
   1733 
   1734         /* ------------------------------------------------------------ */
   1735         /*
   1736          * @see javax.servlet.ServletContext#getContext(java.lang.String)
   1737          */
   1738         @Override
   1739         public ServletContext getContext(String uripath)
   1740         {
   1741             List<ContextHandler> contexts = new ArrayList<ContextHandler>();
   1742             Handler[] handlers = getServer().getChildHandlersByClass(ContextHandler.class);
   1743             String matched_path = null;
   1744 
   1745             for (Handler handler : handlers)
   1746             {
   1747                 if (handler == null)
   1748                     continue;
   1749                 ContextHandler ch = (ContextHandler)handler;
   1750                 String context_path = ch.getContextPath();
   1751 
   1752                 if (uripath.equals(context_path) || (uripath.startsWith(context_path) && uripath.charAt(context_path.length()) == '/')
   1753                         || "/".equals(context_path))
   1754                 {
   1755                     // look first for vhost matching context only
   1756                     if (getVirtualHosts() != null && getVirtualHosts().length > 0)
   1757                     {
   1758                         if (ch.getVirtualHosts() != null && ch.getVirtualHosts().length > 0)
   1759                         {
   1760                             for (String h1 : getVirtualHosts())
   1761                                 for (String h2 : ch.getVirtualHosts())
   1762                                     if (h1.equals(h2))
   1763                                     {
   1764                                         if (matched_path == null || context_path.length() > matched_path.length())
   1765                                         {
   1766                                             contexts.clear();
   1767                                             matched_path = context_path;
   1768                                         }
   1769 
   1770                                         if (matched_path.equals(context_path))
   1771                                             contexts.add(ch);
   1772                                     }
   1773                         }
   1774                     }
   1775                     else
   1776                     {
   1777                         if (matched_path == null || context_path.length() > matched_path.length())
   1778                         {
   1779                             contexts.clear();
   1780                             matched_path = context_path;
   1781                         }
   1782 
   1783                         if (matched_path.equals(context_path))
   1784                             contexts.add(ch);
   1785                     }
   1786                 }
   1787             }
   1788 
   1789             if (contexts.size() > 0)
   1790                 return contexts.get(0)._scontext;
   1791 
   1792             // try again ignoring virtual hosts
   1793             matched_path = null;
   1794             for (Handler handler : handlers)
   1795             {
   1796                 if (handler == null)
   1797                     continue;
   1798                 ContextHandler ch = (ContextHandler)handler;
   1799                 String context_path = ch.getContextPath();
   1800 
   1801                 if (uripath.equals(context_path) || (uripath.startsWith(context_path) && uripath.charAt(context_path.length()) == '/')
   1802                         || "/".equals(context_path))
   1803                 {
   1804                     if (matched_path == null || context_path.length() > matched_path.length())
   1805                     {
   1806                         contexts.clear();
   1807                         matched_path = context_path;
   1808                     }
   1809 
   1810                     if (matched_path.equals(context_path))
   1811                         contexts.add(ch);
   1812                 }
   1813             }
   1814 
   1815             if (contexts.size() > 0)
   1816                 return contexts.get(0)._scontext;
   1817             return null;
   1818         }
   1819 
   1820         /* ------------------------------------------------------------ */
   1821         /*
   1822          * @see javax.servlet.ServletContext#getMajorVersion()
   1823          */
   1824         @Override
   1825         public int getMajorVersion()
   1826         {
   1827             return 3;
   1828         }
   1829 
   1830 
   1831         /* ------------------------------------------------------------ */
   1832         /*
   1833          * @see javax.servlet.ServletContext#getMimeType(java.lang.String)
   1834          */
   1835         @Override
   1836         public String getMimeType(String file)
   1837         {
   1838             if (_mimeTypes == null)
   1839                 return null;
   1840             Buffer mime = _mimeTypes.getMimeByExtension(file);
   1841             if (mime != null)
   1842                 return mime.toString();
   1843             return null;
   1844         }
   1845 
   1846         /* ------------------------------------------------------------ */
   1847         /*
   1848          * @see javax.servlet.ServletContext#getMinorVersion()
   1849          */
   1850         @Override
   1851         public int getMinorVersion()
   1852         {
   1853             return 0;
   1854         }
   1855 
   1856         /* ------------------------------------------------------------ */
   1857         /*
   1858          * @see javax.servlet.ServletContext#getNamedDispatcher(java.lang.String)
   1859          */
   1860         @Override
   1861         public RequestDispatcher getNamedDispatcher(String name)
   1862         {
   1863             return null;
   1864         }
   1865 
   1866         /* ------------------------------------------------------------ */
   1867         /*
   1868          * @see javax.servlet.ServletContext#getRequestDispatcher(java.lang.String)
   1869          */
   1870         @Override
   1871         public RequestDispatcher getRequestDispatcher(String uriInContext)
   1872         {
   1873             if (uriInContext == null)
   1874                 return null;
   1875 
   1876             if (!uriInContext.startsWith("/"))
   1877                 return null;
   1878 
   1879             try
   1880             {
   1881                 String query = null;
   1882                 int q = 0;
   1883                 if ((q = uriInContext.indexOf('?')) > 0)
   1884                 {
   1885                     query = uriInContext.substring(q + 1);
   1886                     uriInContext = uriInContext.substring(0,q);
   1887                 }
   1888 
   1889                 String pathInContext = URIUtil.canonicalPath(URIUtil.decodePath(uriInContext));
   1890                 if (pathInContext!=null)
   1891                 {
   1892                     String uri = URIUtil.addPaths(getContextPath(),uriInContext);
   1893                     ContextHandler context = ContextHandler.this;
   1894                     return new Dispatcher(context,uri,pathInContext,query);
   1895                 }
   1896             }
   1897             catch (Exception e)
   1898             {
   1899                 LOG.ignore(e);
   1900             }
   1901             return null;
   1902         }
   1903 
   1904         /* ------------------------------------------------------------ */
   1905         /*
   1906          * @see javax.servlet.ServletContext#getRealPath(java.lang.String)
   1907          */
   1908         @Override
   1909         public String getRealPath(String path)
   1910         {
   1911             if (path == null)
   1912                 return null;
   1913             if (path.length() == 0)
   1914                 path = URIUtil.SLASH;
   1915             else if (path.charAt(0) != '/')
   1916                 path = URIUtil.SLASH + path;
   1917 
   1918             try
   1919             {
   1920                 Resource resource = ContextHandler.this.getResource(path);
   1921                 if (resource != null)
   1922                 {
   1923                     File file = resource.getFile();
   1924                     if (file != null)
   1925                         return file.getCanonicalPath();
   1926                 }
   1927             }
   1928             catch (Exception e)
   1929             {
   1930                 LOG.ignore(e);
   1931             }
   1932 
   1933             return null;
   1934         }
   1935 
   1936         /* ------------------------------------------------------------ */
   1937         @Override
   1938         public URL getResource(String path) throws MalformedURLException
   1939         {
   1940             Resource resource = ContextHandler.this.getResource(path);
   1941             if (resource != null && resource.exists())
   1942                 return resource.getURL();
   1943             return null;
   1944         }
   1945 
   1946         /* ------------------------------------------------------------ */
   1947         /*
   1948          * @see javax.servlet.ServletContext#getResourceAsStream(java.lang.String)
   1949          */
   1950         @Override
   1951         public InputStream getResourceAsStream(String path)
   1952         {
   1953             try
   1954             {
   1955                 URL url = getResource(path);
   1956                 if (url == null)
   1957                     return null;
   1958                 Resource r = Resource.newResource(url);
   1959                 return r.getInputStream();
   1960             }
   1961             catch (Exception e)
   1962             {
   1963                 LOG.ignore(e);
   1964                 return null;
   1965             }
   1966         }
   1967 
   1968         /* ------------------------------------------------------------ */
   1969         /*
   1970          * @see javax.servlet.ServletContext#getResourcePaths(java.lang.String)
   1971          */
   1972         @Override
   1973         public Set getResourcePaths(String path)
   1974         {
   1975             return ContextHandler.this.getResourcePaths(path);
   1976         }
   1977 
   1978         /* ------------------------------------------------------------ */
   1979         /*
   1980          * @see javax.servlet.ServletContext#getServerInfo()
   1981          */
   1982         @Override
   1983         public String getServerInfo()
   1984         {
   1985             return "jetty/" + Server.getVersion();
   1986         }
   1987 
   1988         /* ------------------------------------------------------------ */
   1989         /*
   1990          * @see javax.servlet.ServletContext#getServlet(java.lang.String)
   1991          */
   1992         @Override
   1993         @Deprecated
   1994         public Servlet getServlet(String name) throws ServletException
   1995         {
   1996             return null;
   1997         }
   1998 
   1999         /* ------------------------------------------------------------ */
   2000         /*
   2001          * @see javax.servlet.ServletContext#getServletNames()
   2002          */
   2003         @SuppressWarnings("unchecked")
   2004         @Override
   2005         @Deprecated
   2006         public Enumeration getServletNames()
   2007         {
   2008             return Collections.enumeration(Collections.EMPTY_LIST);
   2009         }
   2010 
   2011         /* ------------------------------------------------------------ */
   2012         /*
   2013          * @see javax.servlet.ServletContext#getServlets()
   2014          */
   2015         @SuppressWarnings("unchecked")
   2016         @Override
   2017         @Deprecated
   2018         public Enumeration getServlets()
   2019         {
   2020             return Collections.enumeration(Collections.EMPTY_LIST);
   2021         }
   2022 
   2023         /* ------------------------------------------------------------ */
   2024         /*
   2025          * @see javax.servlet.ServletContext#log(java.lang.Exception, java.lang.String)
   2026          */
   2027         @Override
   2028         public void log(Exception exception, String msg)
   2029         {
   2030             _logger.warn(msg,exception);
   2031         }
   2032 
   2033         /* ------------------------------------------------------------ */
   2034         /*
   2035          * @see javax.servlet.ServletContext#log(java.lang.String)
   2036          */
   2037         @Override
   2038         public void log(String msg)
   2039         {
   2040             _logger.info(msg);
   2041         }
   2042 
   2043         /* ------------------------------------------------------------ */
   2044         /*
   2045          * @see javax.servlet.ServletContext#log(java.lang.String, java.lang.Throwable)
   2046          */
   2047         @Override
   2048         public void log(String message, Throwable throwable)
   2049         {
   2050             _logger.warn(message,throwable);
   2051         }
   2052 
   2053         /* ------------------------------------------------------------ */
   2054         /*
   2055          * @see javax.servlet.ServletContext#getInitParameter(java.lang.String)
   2056          */
   2057         @Override
   2058         public String getInitParameter(String name)
   2059         {
   2060             return ContextHandler.this.getInitParameter(name);
   2061         }
   2062 
   2063         /* ------------------------------------------------------------ */
   2064         /*
   2065          * @see javax.servlet.ServletContext#getInitParameterNames()
   2066          */
   2067         @SuppressWarnings("unchecked")
   2068         @Override
   2069         public Enumeration getInitParameterNames()
   2070         {
   2071             return ContextHandler.this.getInitParameterNames();
   2072         }
   2073 
   2074         /* ------------------------------------------------------------ */
   2075         /*
   2076          * @see javax.servlet.ServletContext#getAttribute(java.lang.String)
   2077          */
   2078         @Override
   2079         public synchronized Object getAttribute(String name)
   2080         {
   2081             Object o = ContextHandler.this.getAttribute(name);
   2082             if (o == null && _contextAttributes != null)
   2083                 o = _contextAttributes.getAttribute(name);
   2084             return o;
   2085         }
   2086 
   2087         /* ------------------------------------------------------------ */
   2088         /*
   2089          * @see javax.servlet.ServletContext#getAttributeNames()
   2090          */
   2091         @SuppressWarnings("unchecked")
   2092         @Override
   2093         public synchronized Enumeration getAttributeNames()
   2094         {
   2095             HashSet<String> set = new HashSet<String>();
   2096             if (_contextAttributes != null)
   2097             {
   2098                 Enumeration<String> e = _contextAttributes.getAttributeNames();
   2099                 while (e.hasMoreElements())
   2100                     set.add(e.nextElement());
   2101             }
   2102             Enumeration<String> e = _attributes.getAttributeNames();
   2103             while (e.hasMoreElements())
   2104                 set.add(e.nextElement());
   2105 
   2106             return Collections.enumeration(set);
   2107         }
   2108 
   2109         /* ------------------------------------------------------------ */
   2110         /*
   2111          * @see javax.servlet.ServletContext#setAttribute(java.lang.String, java.lang.Object)
   2112          */
   2113         @Override
   2114         public synchronized void setAttribute(String name, Object value)
   2115         {
   2116             checkManagedAttribute(name,value);
   2117             Object old_value = _contextAttributes.getAttribute(name);
   2118 
   2119             if (value == null)
   2120                 _contextAttributes.removeAttribute(name);
   2121             else
   2122                 _contextAttributes.setAttribute(name,value);
   2123 
   2124             if (_contextAttributeListeners != null)
   2125             {
   2126                 ServletContextAttributeEvent event = new ServletContextAttributeEvent(_scontext,name,old_value == null?value:old_value);
   2127 
   2128                 for (int i = 0; i < LazyList.size(_contextAttributeListeners); i++)
   2129                 {
   2130                     ServletContextAttributeListener l = (ServletContextAttributeListener)LazyList.get(_contextAttributeListeners,i);
   2131 
   2132                     if (old_value == null)
   2133                         l.attributeAdded(event);
   2134                     else if (value == null)
   2135                         l.attributeRemoved(event);
   2136                     else
   2137                         l.attributeReplaced(event);
   2138                 }
   2139             }
   2140         }
   2141 
   2142         /* ------------------------------------------------------------ */
   2143         /*
   2144          * @see javax.servlet.ServletContext#removeAttribute(java.lang.String)
   2145          */
   2146         @Override
   2147         public synchronized void removeAttribute(String name)
   2148         {
   2149             checkManagedAttribute(name,null);
   2150 
   2151             if (_contextAttributes == null)
   2152             {
   2153                 // Set it on the handler
   2154                 _attributes.removeAttribute(name);
   2155                 return;
   2156             }
   2157 
   2158             Object old_value = _contextAttributes.getAttribute(name);
   2159             _contextAttributes.removeAttribute(name);
   2160             if (old_value != null)
   2161             {
   2162                 if (_contextAttributeListeners != null)
   2163                 {
   2164                     ServletContextAttributeEvent event = new ServletContextAttributeEvent(_scontext,name,old_value);
   2165 
   2166                     for (int i = 0; i < LazyList.size(_contextAttributeListeners); i++)
   2167                         ((ServletContextAttributeListener)LazyList.get(_contextAttributeListeners,i)).attributeRemoved(event);
   2168                 }
   2169             }
   2170         }
   2171 
   2172         /* ------------------------------------------------------------ */
   2173         /*
   2174          * @see javax.servlet.ServletContext#getServletContextName()
   2175          */
   2176         @Override
   2177         public String getServletContextName()
   2178         {
   2179             String name = ContextHandler.this.getDisplayName();
   2180             if (name == null)
   2181                 name = ContextHandler.this.getContextPath();
   2182             return name;
   2183         }
   2184 
   2185         /* ------------------------------------------------------------ */
   2186         @Override
   2187         public String getContextPath()
   2188         {
   2189             if ((_contextPath != null) && _contextPath.equals(URIUtil.SLASH))
   2190                 return "";
   2191 
   2192             return _contextPath;
   2193         }
   2194 
   2195         /* ------------------------------------------------------------ */
   2196         @Override
   2197         public String toString()
   2198         {
   2199             return "ServletContext@" + ContextHandler.this.toString();
   2200         }
   2201 
   2202         /* ------------------------------------------------------------ */
   2203         @Override
   2204         public boolean setInitParameter(String name, String value)
   2205         {
   2206             if (ContextHandler.this.getInitParameter(name) != null)
   2207                 return false;
   2208             ContextHandler.this.getInitParams().put(name,value);
   2209             return true;
   2210         }
   2211 
   2212         /* ------------------------------------------------------------ */
   2213         final private static String __unimplmented="Unimplemented - use org.eclipse.jetty.servlet.ServletContextHandler";
   2214 
   2215         @Override
   2216         public Dynamic addFilter(String filterName, Class<? extends Filter> filterClass)
   2217         {
   2218             LOG.warn(__unimplmented);
   2219             return null;
   2220         }
   2221 
   2222         @Override
   2223         public Dynamic addFilter(String filterName, Filter filter)
   2224         {
   2225             LOG.warn(__unimplmented);
   2226             return null;
   2227         }
   2228 
   2229         @Override
   2230         public Dynamic addFilter(String filterName, String className)
   2231         {
   2232             LOG.warn(__unimplmented);
   2233             return null;
   2234         }
   2235 
   2236         @Override
   2237         public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, Class<? extends Servlet> servletClass)
   2238         {
   2239             LOG.warn(__unimplmented);
   2240             return null;
   2241         }
   2242 
   2243         @Override
   2244         public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet)
   2245         {
   2246             LOG.warn(__unimplmented);
   2247             return null;
   2248         }
   2249 
   2250         @Override
   2251         public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, String className)
   2252         {
   2253             LOG.warn(__unimplmented);
   2254             return null;
   2255         }
   2256 
   2257         @Override
   2258         public <T extends Filter> T createFilter(Class<T> c) throws ServletException
   2259         {
   2260             LOG.warn(__unimplmented);
   2261             return null;
   2262         }
   2263 
   2264         @Override
   2265         public <T extends Servlet> T createServlet(Class<T> c) throws ServletException
   2266         {
   2267             LOG.warn(__unimplmented);
   2268             return null;
   2269         }
   2270 
   2271         @Override
   2272         public Set<SessionTrackingMode> getDefaultSessionTrackingModes()
   2273         {
   2274             LOG.warn(__unimplmented);
   2275             return null;
   2276         }
   2277 
   2278         @Override
   2279         public Set<SessionTrackingMode> getEffectiveSessionTrackingModes()
   2280         {
   2281             LOG.warn(__unimplmented);
   2282             return null;
   2283         }
   2284 
   2285         @Override
   2286         public FilterRegistration getFilterRegistration(String filterName)
   2287         {
   2288             LOG.warn(__unimplmented);
   2289             return null;
   2290         }
   2291 
   2292         @Override
   2293         public Map<String, ? extends FilterRegistration> getFilterRegistrations()
   2294         {
   2295             LOG.warn(__unimplmented);
   2296             return null;
   2297         }
   2298 
   2299         @Override
   2300         public ServletRegistration getServletRegistration(String servletName)
   2301         {
   2302             LOG.warn(__unimplmented);
   2303             return null;
   2304         }
   2305 
   2306         @Override
   2307         public Map<String, ? extends ServletRegistration> getServletRegistrations()
   2308         {
   2309             LOG.warn(__unimplmented);
   2310             return null;
   2311         }
   2312 
   2313         @Override
   2314         public SessionCookieConfig getSessionCookieConfig()
   2315         {
   2316             LOG.warn(__unimplmented);
   2317             return null;
   2318         }
   2319 
   2320         @Override
   2321         public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes)
   2322         {
   2323             LOG.warn(__unimplmented);
   2324         }
   2325 
   2326         @Override
   2327         public void addListener(String className)
   2328         {
   2329             if (!_enabled)
   2330                 throw new UnsupportedOperationException();
   2331 
   2332             try
   2333             {
   2334                 Class<? extends EventListener> clazz = _classLoader==null?Loader.loadClass(ContextHandler.class,className):_classLoader.loadClass(className);
   2335                 addListener(clazz);
   2336             }
   2337             catch (ClassNotFoundException e)
   2338             {
   2339                 throw new IllegalArgumentException(e);
   2340             }
   2341         }
   2342 
   2343         @Override
   2344         public <T extends EventListener> void addListener(T t)
   2345         {
   2346             if (!_enabled)
   2347                 throw new UnsupportedOperationException();
   2348             ContextHandler.this.addEventListener(t);
   2349             ContextHandler.this.restrictEventListener(t);
   2350         }
   2351 
   2352         @Override
   2353         public void addListener(Class<? extends EventListener> listenerClass)
   2354         {
   2355             if (!_enabled)
   2356                 throw new UnsupportedOperationException();
   2357 
   2358             try
   2359             {
   2360                 EventListener e = createListener(listenerClass);
   2361                 ContextHandler.this.addEventListener(e);
   2362                 ContextHandler.this.restrictEventListener(e);
   2363             }
   2364             catch (ServletException e)
   2365             {
   2366                 throw new IllegalArgumentException(e);
   2367             }
   2368         }
   2369 
   2370         @Override
   2371         public <T extends EventListener> T createListener(Class<T> clazz) throws ServletException
   2372         {
   2373             try
   2374             {
   2375                 return clazz.newInstance();
   2376             }
   2377             catch (InstantiationException e)
   2378             {
   2379                 throw new ServletException(e);
   2380             }
   2381             catch (IllegalAccessException e)
   2382             {
   2383                 throw new ServletException(e);
   2384             }
   2385         }
   2386 
   2387         @Override
   2388         public ClassLoader getClassLoader()
   2389         {
   2390             AccessController.checkPermission(new RuntimePermission("getClassLoader"));
   2391             return _classLoader;
   2392         }
   2393 
   2394         @Override
   2395         public int getEffectiveMajorVersion()
   2396         {
   2397             return _majorVersion;
   2398         }
   2399 
   2400         @Override
   2401         public int getEffectiveMinorVersion()
   2402         {
   2403             return _minorVersion;
   2404         }
   2405 
   2406         public void setEffectiveMajorVersion (int v)
   2407         {
   2408             _majorVersion = v;
   2409         }
   2410 
   2411         public void setEffectiveMinorVersion (int v)
   2412         {
   2413             _minorVersion = v;
   2414         }
   2415 
   2416         @Override
   2417         public JspConfigDescriptor getJspConfigDescriptor()
   2418         {
   2419             LOG.warn(__unimplmented);
   2420             return null;
   2421         }
   2422 
   2423         public void setJspConfigDescriptor(JspConfigDescriptor d)
   2424         {
   2425 
   2426         }
   2427 
   2428         @Override
   2429         public void declareRoles(String... roleNames)
   2430         {
   2431             if (!isStarting())
   2432                 throw new IllegalStateException ();
   2433             if (!_enabled)
   2434                 throw new UnsupportedOperationException();
   2435 
   2436             // TODO Auto-generated method stub
   2437 
   2438         }
   2439 
   2440         public void setEnabled(boolean enabled)
   2441         {
   2442             _enabled = enabled;
   2443         }
   2444 
   2445         public boolean isEnabled()
   2446         {
   2447             return _enabled;
   2448         }
   2449     }
   2450 
   2451     private static class CLDump implements Dumpable
   2452     {
   2453         final ClassLoader _loader;
   2454 
   2455         CLDump(ClassLoader loader)
   2456         {
   2457             _loader = loader;
   2458         }
   2459 
   2460         public String dump()
   2461         {
   2462             return AggregateLifeCycle.dump(this);
   2463         }
   2464 
   2465         public void dump(Appendable out, String indent) throws IOException
   2466         {
   2467             out.append(String.valueOf(_loader)).append("\n");
   2468 
   2469             if (_loader != null)
   2470             {
   2471                 Object parent = _loader.getParent();
   2472                 if (parent != null)
   2473                 {
   2474                     if (!(parent instanceof Dumpable))
   2475                         parent = new CLDump((ClassLoader)parent);
   2476 
   2477                     if (_loader instanceof URLClassLoader)
   2478                         AggregateLifeCycle.dump(out,indent,TypeUtil.asList(((URLClassLoader)_loader).getURLs()),Collections.singleton(parent));
   2479                     else
   2480                         AggregateLifeCycle.dump(out,indent,Collections.singleton(parent));
   2481                 }
   2482             }
   2483         }
   2484     }
   2485 
   2486 
   2487     /* ------------------------------------------------------------ */
   2488     /** Interface to check aliases
   2489      */
   2490     public interface AliasCheck
   2491     {
   2492         /* ------------------------------------------------------------ */
   2493         /** Check an alias
   2494          * @param path The path the aliased resource was created for
   2495          * @param resource The aliased resourced
   2496          * @return True if the resource is OK to be served.
   2497          */
   2498         boolean check(String path, Resource resource);
   2499     }
   2500 
   2501 
   2502     /* ------------------------------------------------------------ */
   2503     /** Approve Aliases with same suffix.
   2504      * Eg. a symbolic link from /foobar.html to /somewhere/wibble.html would be
   2505      * approved because both the resource and alias end with ".html".
   2506      */
   2507     @Deprecated
   2508     public static class ApproveSameSuffixAliases implements AliasCheck
   2509     {
   2510         {
   2511             LOG.warn("ApproveSameSuffixAlias is not safe for production");
   2512         }
   2513 
   2514         public boolean check(String path, Resource resource)
   2515         {
   2516             int dot = path.lastIndexOf('.');
   2517             if (dot<0)
   2518                 return false;
   2519             String suffix=path.substring(dot);
   2520             return resource.toString().endsWith(suffix);
   2521         }
   2522     }
   2523 
   2524 
   2525     /* ------------------------------------------------------------ */
   2526     /** Approve Aliases with a path prefix.
   2527      * Eg. a symbolic link from /dirA/foobar.html to /dirB/foobar.html would be
   2528      * approved because both the resource and alias end with "/foobar.html".
   2529      */
   2530     @Deprecated
   2531     public static class ApprovePathPrefixAliases implements AliasCheck
   2532     {
   2533         {
   2534             LOG.warn("ApprovePathPrefixAliases is not safe for production");
   2535         }
   2536 
   2537         public boolean check(String path, Resource resource)
   2538         {
   2539             int slash = path.lastIndexOf('/');
   2540             if (slash<0 || slash==path.length()-1)
   2541                 return false;
   2542             String suffix=path.substring(slash);
   2543             return resource.toString().endsWith(suffix);
   2544         }
   2545     }
   2546 
   2547     /* ------------------------------------------------------------ */
   2548     /** Approve Aliases of a non existent directory.
   2549      * If a directory "/foobar/" does not exist, then the resource is
   2550      * aliased to "/foobar".  Accept such aliases.
   2551      */
   2552     public static class ApproveNonExistentDirectoryAliases implements AliasCheck
   2553     {
   2554         public boolean check(String path, Resource resource)
   2555         {
   2556             if (resource.exists())
   2557                 return false;
   2558 
   2559             String a=resource.getAlias().toString();
   2560             String r=resource.getURL().toString();
   2561 
   2562             if (a.length()>r.length())
   2563                 return a.startsWith(r) && a.length()==r.length()+1 && a.endsWith("/");
   2564             else
   2565                 return r.startsWith(a) && r.length()==a.length()+1 && r.endsWith("/");
   2566         }
   2567     }
   2568 
   2569 }
   2570