+//
+// ========================================================================
+// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.server.handler;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.AccessController;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.EventListener;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Future;
+
+import javax.servlet.DispatcherType;
+import javax.servlet.Filter;
+import javax.servlet.FilterRegistration;
+import javax.servlet.FilterRegistration.Dynamic;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextAttributeEvent;
+import javax.servlet.ServletContextAttributeListener;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRegistration;
+import javax.servlet.ServletRequestAttributeListener;
+import javax.servlet.ServletRequestEvent;
+import javax.servlet.ServletRequestListener;
+import javax.servlet.SessionCookieConfig;
+import javax.servlet.SessionTrackingMode;
+import javax.servlet.descriptor.JspConfigDescriptor;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.server.ClassLoaderDump;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.Dispatcher;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.HandlerContainer;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.util.Attributes;
+import org.eclipse.jetty.util.AttributesMap;
+import org.eclipse.jetty.util.FutureCallback;
+import org.eclipse.jetty.util.Loader;
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.URIUtil;
+import org.eclipse.jetty.util.annotation.ManagedAttribute;
+import org.eclipse.jetty.util.annotation.ManagedObject;
+import org.eclipse.jetty.util.component.Graceful;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.resource.Resource;
+
+/* ------------------------------------------------------------ */
+/**
+ * ContextHandler.
+ *
+ * This handler wraps a call to handle by setting the context and servlet path, plus setting the context classloader.
+ *
+ * <p>
+ * 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
+ * context attribute names, which if set as attributes are passed to the servers Container so that they may be managed with JMX.
+ * <p>
+ * 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
+ * and org.eclipse.jetty.server.Request.maxFormContentSize. These can also be configured with {@link #setMaxFormContentSize(int)} and {@link #setMaxFormKeys(int)}
+ * <p>
+ * This servers executore is made available via a context attributed "org.eclipse.jetty.server.Executor".
+ *
+ * @org.apache.xbean.XBean description="Creates a basic HTTP context"
+ */
+@ManagedObject("URI Context")
+public class ContextHandler extends ScopedHandler implements Attributes, Graceful
+{
+ public final static int SERVLET_MAJOR_VERSION=3;
+ public final static int SERVLET_MINOR_VERSION=0;
+ public static final Class[] SERVLET_LISTENER_TYPES = new Class[] {ServletContextListener.class,
+ ServletContextAttributeListener.class,
+ ServletRequestListener.class,
+ ServletRequestAttributeListener.class};
+
+ public static final int DEFAULT_LISTENER_TYPE_INDEX = 1;
+ public static final int EXTENDED_LISTENER_TYPE_INDEX = 0;
+
+
+ final private static String __unimplmented="Unimplemented - use org.eclipse.jetty.servlet.ServletContextHandler";
+
+ private static final Logger LOG = Log.getLogger(ContextHandler.class);
+
+ private static final ThreadLocal<Context> __context = new ThreadLocal<Context>();
+
+ /**
+ * 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
+ * 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
+ * for the attribute value.
+ */
+ public static final String MANAGED_ATTRIBUTES = "org.eclipse.jetty.server.context.ManagedAttributes";
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Get the current ServletContext implementation.
+ *
+ * @return ServletContext implementation
+ */
+ public static Context getCurrentContext()
+ {
+ return __context.get();
+ }
+
+ /* ------------------------------------------------------------ */
+ public static ContextHandler getContextHandler(ServletContext context)
+ {
+ if(context instanceof ContextHandler.Context)
+ return ((ContextHandler.Context)context).getContextHandler();
+ Context c= getCurrentContext();
+ if (c!=null)
+ return c.getContextHandler();
+ return null;
+ }
+
+
+ protected Context _scontext;
+ private final AttributesMap _attributes;
+ private final Map<String, String> _initParams;
+ private ClassLoader _classLoader;
+ private String _contextPath = "/";
+
+ private String _displayName;
+
+ private Resource _baseResource;
+ private MimeTypes _mimeTypes;
+ private Map<String, String> _localeEncodingMap;
+ private String[] _welcomeFiles;
+ private ErrorHandler _errorHandler;
+ private String[] _vhosts;
+
+ private Logger _logger;
+ private boolean _allowNullPathInfo;
+ private int _maxFormKeys = Integer.getInteger("org.eclipse.jetty.server.Request.maxFormKeys",-1).intValue();
+ private int _maxFormContentSize = Integer.getInteger("org.eclipse.jetty.server.Request.maxFormContentSize",-1).intValue();
+ private boolean _compactPath = false;
+
+ private final List<EventListener> _eventListeners=new CopyOnWriteArrayList<>();
+ private final List<EventListener> _programmaticListeners=new CopyOnWriteArrayList<>();
+ private final List<ServletContextListener> _contextListeners=new CopyOnWriteArrayList<>();
+ private final List<ServletContextAttributeListener> _contextAttributeListeners=new CopyOnWriteArrayList<>();
+ private final List<ServletRequestListener> _requestListeners=new CopyOnWriteArrayList<>();
+ private final List<ServletRequestAttributeListener> _requestAttributeListeners=new CopyOnWriteArrayList<>();
+ private final List<EventListener> _durableListeners = new CopyOnWriteArrayList<>();
+ private Map<String, Object> _managedAttributes;
+ private String[] _protectedTargets;
+ private final CopyOnWriteArrayList<AliasCheck> _aliasChecks = new CopyOnWriteArrayList<ContextHandler.AliasCheck>();
+
+ public enum Availability { UNAVAILABLE,STARTING,AVAILABLE,SHUTDOWN,};
+ private volatile Availability _availability;
+
+ /* ------------------------------------------------------------ */
+ /**
+ *
+ */
+ public ContextHandler()
+ {
+ super();
+ _scontext = new Context();
+ _attributes = new AttributesMap();
+ _initParams = new HashMap<String, String>();
+ addAliasCheck(new ApproveNonExistentDirectoryAliases());
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ *
+ */
+ protected ContextHandler(Context context)
+ {
+ super();
+ _scontext = context;
+ _attributes = new AttributesMap();
+ _initParams = new HashMap<String, String>();
+ addAliasCheck(new ApproveNonExistentDirectoryAliases());
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ *
+ */
+ public ContextHandler(String contextPath)
+ {
+ this();
+ setContextPath(contextPath);
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ *
+ */
+ public ContextHandler(HandlerContainer parent, String contextPath)
+ {
+ this();
+ setContextPath(contextPath);
+ if (parent instanceof HandlerWrapper)
+ ((HandlerWrapper)parent).setHandler(this);
+ else if (parent instanceof HandlerCollection)
+ ((HandlerCollection)parent).addHandler(this);
+ }
+
+ /* ------------------------------------------------------------ */
+ @Override
+ public void dump(Appendable out, String indent) throws IOException
+ {
+ dumpBeans(out,indent,
+ Collections.singletonList(new ClassLoaderDump(getClassLoader())),
+ _initParams.entrySet(),
+ _attributes.getAttributeEntrySet(),
+ _scontext.getAttributeEntrySet());
+ }
+
+ /* ------------------------------------------------------------ */
+ public Context getServletContext()
+ {
+ return _scontext;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @return the allowNullPathInfo true if /context is not redirected to /context/
+ */
+ @ManagedAttribute("Checks if the /context is not redirected to /context/")
+ public boolean getAllowNullPathInfo()
+ {
+ return _allowNullPathInfo;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param allowNullPathInfo
+ * true if /context is not redirected to /context/
+ */
+ public void setAllowNullPathInfo(boolean allowNullPathInfo)
+ {
+ _allowNullPathInfo = allowNullPathInfo;
+ }
+
+ /* ------------------------------------------------------------ */
+ @Override
+ public void setServer(Server server)
+ {
+ super.setServer(server);
+ if (_errorHandler != null)
+ _errorHandler.setServer(server);
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * 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
+ * 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
+ * matching virtual host name.
+ *
+ * @param vhosts
+ * 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
+ * String representation of IP addresses. Host names may start with '*.' to wildcard one level of names. Hosts may start with '@', in which case they
+ * will match the {@link Connector#getName()} for the request.
+ */
+ public void setVirtualHosts(String[] vhosts)
+ {
+ if (vhosts == null)
+ {
+ _vhosts = vhosts;
+ }
+ else
+ {
+ _vhosts = new String[vhosts.length];
+ for (int i = 0; i < vhosts.length; i++)
+ _vhosts[i] = normalizeHostname(vhosts[i]);
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ /** Either set virtual hosts or add to an existing set of virtual hosts.
+ *
+ * @param virtualHosts
+ * 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
+ * String representation of IP addresses. Host names may start with '*.' to wildcard one level of names. Host names may start with '@', in which case they
+ * will match the {@link Connector#getName()} for the request.
+ */
+ public void addVirtualHosts(String[] virtualHosts)
+ {
+ if (virtualHosts == null) // since this is add, we don't null the old ones
+ {
+ return;
+ }
+ else
+ {
+ List<String> currentVirtualHosts = null;
+ if (_vhosts != null)
+ {
+ currentVirtualHosts = new ArrayList<String>(Arrays.asList(_vhosts));
+ }
+ else
+ {
+ currentVirtualHosts = new ArrayList<String>();
+ }
+
+ for (int i = 0; i < virtualHosts.length; i++)
+ {
+ String normVhost = normalizeHostname(virtualHosts[i]);
+ if (!currentVirtualHosts.contains(normVhost))
+ {
+ currentVirtualHosts.add(normVhost);
+ }
+ }
+ _vhosts = currentVirtualHosts.toArray(new String[0]);
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Removes an array of virtual host entries, if this removes all entries the _vhosts will be set to null
+ *
+ * @param virtualHosts
+ * 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
+ * String representation of IP addresses. Host names may start with '*.' to wildcard one level of names.
+ */
+ public void removeVirtualHosts(String[] virtualHosts)
+ {
+ if (virtualHosts == null)
+ {
+ return; // do nothing
+ }
+ else if ( _vhosts == null || _vhosts.length == 0)
+ {
+ return; // do nothing
+ }
+ else
+ {
+ List<String> existingVirtualHosts = new ArrayList<String>(Arrays.asList(_vhosts));
+
+ for (int i = 0; i < virtualHosts.length; i++)
+ {
+ String toRemoveVirtualHost = normalizeHostname(virtualHosts[i]);
+ if (existingVirtualHosts.contains(toRemoveVirtualHost))
+ {
+ existingVirtualHosts.remove(toRemoveVirtualHost);
+ }
+ }
+
+ if (existingVirtualHosts.isEmpty())
+ {
+ _vhosts = null; // if we ended up removing them all, just null out _vhosts
+ }
+ else
+ {
+ _vhosts = existingVirtualHosts.toArray(new String[0]);
+ }
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * 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
+ * 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
+ * matching virtual host name.
+ *
+ * @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
+ * representation of IP addresses. Host names may start with '*.' to wildcard one level of names.
+ */
+ @ManagedAttribute(value="Virtual hosts accepted by the context", readonly=true)
+ public String[] getVirtualHosts()
+ {
+ return _vhosts;
+ }
+
+ /* ------------------------------------------------------------ */
+ /*
+ * @see javax.servlet.ServletContext#getAttribute(java.lang.String)
+ */
+ @Override
+ public Object getAttribute(String name)
+ {
+ return _attributes.getAttribute(name);
+ }
+
+ /* ------------------------------------------------------------ */
+ /*
+ * @see javax.servlet.ServletContext#getAttributeNames()
+ */
+ @Override
+ public Enumeration<String> getAttributeNames()
+ {
+ return AttributesMap.getAttributeNamesCopy(_attributes);
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @return Returns the attributes.
+ */
+ public Attributes getAttributes()
+ {
+ return _attributes;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @return Returns the classLoader.
+ */
+ public ClassLoader getClassLoader()
+ {
+ return _classLoader;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Make best effort to extract a file classpath from the context classloader
+ *
+ * @return Returns the classLoader.
+ */
+ @ManagedAttribute("The file classpath")
+ public String getClassPath()
+ {
+ if (_classLoader == null || !(_classLoader instanceof URLClassLoader))
+ return null;
+ URLClassLoader loader = (URLClassLoader)_classLoader;
+ URL[] urls = loader.getURLs();
+ StringBuilder classpath = new StringBuilder();
+ for (int i = 0; i < urls.length; i++)
+ {
+ try
+ {
+ Resource resource = newResource(urls[i]);
+ File file = resource.getFile();
+ if (file != null && file.exists())
+ {
+ if (classpath.length() > 0)
+ classpath.append(File.pathSeparatorChar);
+ classpath.append(file.getAbsolutePath());
+ }
+ }
+ catch (IOException e)
+ {
+ LOG.debug(e);
+ }
+ }
+ if (classpath.length() == 0)
+ return null;
+ return classpath.toString();
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @return Returns the _contextPath.
+ */
+ @ManagedAttribute("True if URLs are compacted to replace the multiple '/'s with a single '/'")
+ public String getContextPath()
+ {
+ return _contextPath;
+ }
+
+ /* ------------------------------------------------------------ */
+ /*
+ * @see javax.servlet.ServletContext#getInitParameter(java.lang.String)
+ */
+ public String getInitParameter(String name)
+ {
+ return _initParams.get(name);
+ }
+
+ /* ------------------------------------------------------------ */
+ /*
+ */
+ public String setInitParameter(String name, String value)
+ {
+ return _initParams.put(name,value);
+ }
+
+ /* ------------------------------------------------------------ */
+ /*
+ * @see javax.servlet.ServletContext#getInitParameterNames()
+ */
+ @SuppressWarnings("rawtypes")
+ public Enumeration getInitParameterNames()
+ {
+ return Collections.enumeration(_initParams.keySet());
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @return Returns the initParams.
+ */
+ @ManagedAttribute("Initial Parameter map for the context")
+ public Map<String, String> getInitParams()
+ {
+ return _initParams;
+ }
+
+ /* ------------------------------------------------------------ */
+ /*
+ * @see javax.servlet.ServletContext#getServletContextName()
+ */
+ @ManagedAttribute(value="Display name of the Context", readonly=true)
+ public String getDisplayName()
+ {
+ return _displayName;
+ }
+
+ /* ------------------------------------------------------------ */
+ public EventListener[] getEventListeners()
+ {
+ return _eventListeners.toArray(new EventListener[_eventListeners.size()]);
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Set the context event listeners.
+ *
+ * @param eventListeners
+ * the event listeners
+ * @see ServletContextListener
+ * @see ServletContextAttributeListener
+ * @see ServletRequestListener
+ * @see ServletRequestAttributeListener
+ */
+ public void setEventListeners(EventListener[] eventListeners)
+ {
+ _contextListeners.clear();
+ _contextAttributeListeners.clear();
+ _requestListeners.clear();
+ _requestAttributeListeners.clear();
+ _eventListeners.clear();
+
+ if (eventListeners!=null)
+ for (EventListener listener : eventListeners)
+ addEventListener(listener);
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Add a context event listeners.
+ *
+ * @see ServletContextListener
+ * @see ServletContextAttributeListener
+ * @see ServletRequestListener
+ * @see ServletRequestAttributeListener
+ */
+ public void addEventListener(EventListener listener)
+ {
+ _eventListeners.add(listener);
+
+ if (!(isStarted() || isStarting()))
+ _durableListeners.add(listener);
+
+ if (listener instanceof ServletContextListener)
+ _contextListeners.add((ServletContextListener)listener);
+
+ if (listener instanceof ServletContextAttributeListener)
+ _contextAttributeListeners.add((ServletContextAttributeListener)listener);
+
+ if (listener instanceof ServletRequestListener)
+ _requestListeners.add((ServletRequestListener)listener);
+
+ if (listener instanceof ServletRequestAttributeListener)
+ _requestAttributeListeners.add((ServletRequestAttributeListener)listener);
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Remove a context event listeners.
+ *
+ * @see ServletContextListener
+ * @see ServletContextAttributeListener
+ * @see ServletRequestListener
+ * @see ServletRequestAttributeListener
+ */
+ public void removeEventListener(EventListener listener)
+ {
+ _eventListeners.remove(listener);
+
+ if (listener instanceof ServletContextListener)
+ _contextListeners.remove(listener);
+
+ if (listener instanceof ServletContextAttributeListener)
+ _contextAttributeListeners.remove(listener);
+
+ if (listener instanceof ServletRequestListener)
+ _requestListeners.remove(listener);
+
+ if (listener instanceof ServletRequestAttributeListener)
+ _requestAttributeListeners.remove(listener);
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Apply any necessary restrictions on a programmatic added listener.
+ *
+ * @param listener
+ */
+ protected void addProgrammaticListener (EventListener listener)
+ {
+ _programmaticListeners.add(listener);
+ }
+
+ /* ------------------------------------------------------------ */
+ protected boolean isProgrammaticListener(EventListener listener)
+ {
+ return _programmaticListeners.contains(listener);
+ }
+
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @return true if this context is accepting new requests
+ */
+ @ManagedAttribute("true for graceful shutdown, which allows existing requests to complete")
+ public boolean isShutdown()
+ {
+ switch(_availability)
+ {
+ case SHUTDOWN:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * 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
+ * requests can complete, but no new requests are accepted.
+ *
+ */
+ @Override
+ public Future<Void> shutdown()
+ {
+ _availability = isRunning() ? Availability.SHUTDOWN : Availability.UNAVAILABLE;
+ return new FutureCallback(true);
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @return false if this context is unavailable (sends 503)
+ */
+ public boolean isAvailable()
+ {
+ return _availability==Availability.AVAILABLE;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Set Available status.
+ */
+ public void setAvailable(boolean available)
+ {
+ synchronized (this)
+ {
+ if (available && isRunning())
+ _availability = Availability.AVAILABLE;
+ else if (!available || !isRunning())
+ _availability = Availability.UNAVAILABLE;
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ public Logger getLogger()
+ {
+ return _logger;
+ }
+
+ /* ------------------------------------------------------------ */
+ public void setLogger(Logger logger)
+ {
+ _logger = logger;
+ }
+
+ /* ------------------------------------------------------------ */
+ /*
+ * @see org.eclipse.thread.AbstractLifeCycle#doStart()
+ */
+ @Override
+ protected void doStart() throws Exception
+ {
+ _availability = Availability.STARTING;
+
+ if (_contextPath == null)
+ throw new IllegalStateException("Null contextPath");
+
+ _logger = Log.getLogger(getDisplayName() == null?getContextPath():getDisplayName());
+ ClassLoader old_classloader = null;
+ Thread current_thread = null;
+ Context old_context = null;
+
+ _attributes.setAttribute("org.eclipse.jetty.server.Executor",getServer().getThreadPool());
+
+ try
+ {
+ // Set the classloader
+ if (_classLoader != null)
+ {
+ current_thread = Thread.currentThread();
+ old_classloader = current_thread.getContextClassLoader();
+ current_thread.setContextClassLoader(_classLoader);
+ }
+
+ if (_mimeTypes == null)
+ _mimeTypes = new MimeTypes();
+
+ old_context = __context.get();
+ __context.set(_scontext);
+
+ // defers the calling of super.doStart()
+ startContext();
+
+ _availability = Availability.AVAILABLE;
+ LOG.info("Started {}", this);
+ }
+ finally
+ {
+ __context.set(old_context);
+
+ // reset the classloader
+ if (_classLoader != null && current_thread!=null)
+ current_thread.setContextClassLoader(old_classloader);
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Extensible startContext. this method is called from {@link ContextHandler#doStart()} instead of a call to super.doStart(). This allows derived classes to
+ * insert additional handling (Eg configuration) before the call to super.doStart by this method will start contained handlers.
+ *
+ * @see org.eclipse.jetty.server.handler.ContextHandler.Context
+ */
+ protected void startContext() throws Exception
+ {
+ String managedAttributes = _initParams.get(MANAGED_ATTRIBUTES);
+ if (managedAttributes != null)
+ {
+ _managedAttributes = new HashMap<String, Object>();
+ String[] attributes = managedAttributes.split(",");
+ for (String attribute : attributes)
+ _managedAttributes.put(attribute,null);
+
+ Enumeration<String> e = _scontext.getAttributeNames();
+ while (e.hasMoreElements())
+ {
+ String name = e.nextElement();
+ Object value = _scontext.getAttribute(name);
+ checkManagedAttribute(name,value);
+ }
+ }
+
+ super.doStart();
+
+ // Call context listeners
+ if (!_contextListeners.isEmpty())
+ {
+ ServletContextEvent event = new ServletContextEvent(_scontext);
+ for (ServletContextListener listener:_contextListeners)
+ callContextInitialized(listener, event);
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ protected void callContextInitialized (ServletContextListener l, ServletContextEvent e)
+ {
+ LOG.debug("contextInitialized: {}->{}",e,l);
+ l.contextInitialized(e);
+ }
+
+ /* ------------------------------------------------------------ */
+ protected void callContextDestroyed (ServletContextListener l, ServletContextEvent e)
+ {
+ LOG.debug("contextDestroyed: {}->{}",e,l);
+ l.contextDestroyed(e);
+ }
+
+ /* ------------------------------------------------------------ */
+ /*
+ * @see org.eclipse.thread.AbstractLifeCycle#doStop()
+ */
+ @Override
+ protected void doStop() throws Exception
+ {
+ _availability = Availability.UNAVAILABLE;
+
+ ClassLoader old_classloader = null;
+ Thread current_thread = null;
+
+ Context old_context = __context.get();
+ __context.set(_scontext);
+ try
+ {
+ // Set the classloader
+ if (_classLoader != null)
+ {
+ current_thread = Thread.currentThread();
+ old_classloader = current_thread.getContextClassLoader();
+ current_thread.setContextClassLoader(_classLoader);
+ }
+
+ super.doStop();
+
+ // Context listeners
+ if (!_contextListeners.isEmpty())
+ {
+ ServletContextEvent event = new ServletContextEvent(_scontext);
+ for (int i = _contextListeners.size(); i-->0;)
+ callContextDestroyed(_contextListeners.get(i),event);
+ }
+
+ //retain only durable listeners
+ setEventListeners(_durableListeners.toArray(new EventListener[_durableListeners.size()]));
+ _durableListeners.clear();
+
+ if (_errorHandler != null)
+ _errorHandler.stop();
+
+ Enumeration<String> e = _scontext.getAttributeNames();
+ while (e.hasMoreElements())
+ {
+ String name = e.nextElement();
+ checkManagedAttribute(name,null);
+ }
+
+ for (EventListener l : _programmaticListeners)
+ removeEventListener(l);
+ _programmaticListeners.clear();
+ }
+ finally
+ {
+ LOG.info("Stopped {}", this);
+ __context.set(old_context);
+ // reset the classloader
+ if (_classLoader != null && current_thread!=null)
+ current_thread.setContextClassLoader(old_classloader);
+ }
+
+ _scontext.clearAttributes();
+ }
+
+ /* ------------------------------------------------------------ */
+ /*
+ * @see org.eclipse.jetty.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+ */
+ public boolean checkContext(final String target, final Request baseRequest, final HttpServletResponse response) throws IOException
+ {
+ DispatcherType dispatch = baseRequest.getDispatcherType();
+
+ switch (_availability)
+ {
+ case SHUTDOWN:
+ case UNAVAILABLE:
+ baseRequest.setHandled(true);
+ response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
+ return false;
+ default:
+ if ((DispatcherType.REQUEST.equals(dispatch) && baseRequest.isHandled()))
+ return false;
+ }
+
+ // Check the vhosts
+ if (_vhosts != null && _vhosts.length > 0)
+ {
+ String vhost = normalizeHostname(baseRequest.getServerName());
+
+ boolean match = false;
+ boolean connectorName = false;
+ boolean connectorMatch = false;
+
+ for (String contextVhost:_vhosts)
+ {
+ if (contextVhost == null || contextVhost.length()==0)
+ continue;
+ char c=contextVhost.charAt(0);
+ switch (c)
+ {
+ case '*':
+ if (contextVhost.startsWith("*."))
+ // wildcard only at the beginning, and only for one additional subdomain level
+ match = match || contextVhost.regionMatches(true,2,vhost,vhost.indexOf(".") + 1,contextVhost.length() - 2);
+ break;
+ case '@':
+ connectorName=true;
+ String name=baseRequest.getHttpChannel().getConnector().getName();
+ boolean m=name!=null && contextVhost.length()==name.length()+1 && contextVhost.endsWith(name);
+ match = match || m;
+ connectorMatch = connectorMatch || m;
+ break;
+ default:
+ match = match || contextVhost.equalsIgnoreCase(vhost);
+ }
+
+ }
+ if (!match || connectorName && !connectorMatch)
+ return false;
+ }
+
+ // Are we not the root context?
+ if (_contextPath.length() > 1)
+ {
+ // reject requests that are not for us
+ if (!target.startsWith(_contextPath))
+ return false;
+ if (target.length() > _contextPath.length() && target.charAt(_contextPath.length()) != '/')
+ return false;
+
+ // redirect null path infos
+ if (!_allowNullPathInfo && _contextPath.length() == target.length())
+ {
+ // context request must end with /
+ baseRequest.setHandled(true);
+ if (baseRequest.getQueryString() != null)
+ response.sendRedirect(URIUtil.addPaths(baseRequest.getRequestURI(),URIUtil.SLASH) + "?" + baseRequest.getQueryString());
+ else
+ response.sendRedirect(URIUtil.addPaths(baseRequest.getRequestURI(),URIUtil.SLASH));
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.server.handler.ScopedHandler#doScope(java.lang.String, org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest,
+ * javax.servlet.http.HttpServletResponse)
+ */
+ @Override
+ public void doScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("scope {}|{}|{} @ {}",baseRequest.getContextPath(),baseRequest.getServletPath(),baseRequest.getPathInfo(),this);
+
+ Context old_context = null;
+ String old_context_path = null;
+ String old_servlet_path = null;
+ String old_path_info = null;
+ ClassLoader old_classloader = null;
+ Thread current_thread = null;
+ String pathInfo = target;
+
+ DispatcherType dispatch = baseRequest.getDispatcherType();
+
+ old_context = baseRequest.getContext();
+
+ // Are we already in this context?
+ if (old_context != _scontext)
+ {
+ // check the target.
+ if (DispatcherType.REQUEST.equals(dispatch) ||
+ DispatcherType.ASYNC.equals(dispatch) ||
+ DispatcherType.ERROR.equals(dispatch) && baseRequest.getHttpChannelState().isAsync())
+ {
+ if (_compactPath)
+ target = URIUtil.compactPath(target);
+ if (!checkContext(target,baseRequest,response))
+ return;
+
+ if (target.length() > _contextPath.length())
+ {
+ if (_contextPath.length() > 1)
+ target = target.substring(_contextPath.length());
+ pathInfo = target;
+ }
+ else if (_contextPath.length() == 1)
+ {
+ target = URIUtil.SLASH;
+ pathInfo = URIUtil.SLASH;
+ }
+ else
+ {
+ target = URIUtil.SLASH;
+ pathInfo = null;
+ }
+ }
+
+ // Set the classloader
+ if (_classLoader != null)
+ {
+ current_thread = Thread.currentThread();
+ old_classloader = current_thread.getContextClassLoader();
+ current_thread.setContextClassLoader(_classLoader);
+ }
+ }
+
+ try
+ {
+ old_context_path = baseRequest.getContextPath();
+ old_servlet_path = baseRequest.getServletPath();
+ old_path_info = baseRequest.getPathInfo();
+
+ // Update the paths
+ baseRequest.setContext(_scontext);
+ __context.set(_scontext);
+ if (!DispatcherType.INCLUDE.equals(dispatch) && target.startsWith("/"))
+ {
+ if (_contextPath.length() == 1)
+ baseRequest.setContextPath("");
+ else
+ baseRequest.setContextPath(_contextPath);
+ baseRequest.setServletPath(null);
+ baseRequest.setPathInfo(pathInfo);
+ }
+
+ if (LOG.isDebugEnabled())
+ LOG.debug("context={}|{}|{} @ {}",baseRequest.getContextPath(),baseRequest.getServletPath(), baseRequest.getPathInfo(),this);
+
+ // start manual inline of nextScope(target,baseRequest,request,response);
+ if (never())
+ nextScope(target,baseRequest,request,response);
+ else if (_nextScope != null)
+ _nextScope.doScope(target,baseRequest,request,response);
+ else if (_outerScope != null)
+ _outerScope.doHandle(target,baseRequest,request,response);
+ else
+ doHandle(target,baseRequest,request,response);
+ // end manual inline (pathentic attempt to reduce stack depth)
+ }
+ finally
+ {
+ if (old_context != _scontext)
+ {
+ // reset the classloader
+ if (_classLoader != null && current_thread!=null)
+ {
+ current_thread.setContextClassLoader(old_classloader);
+ }
+
+ // reset the context and servlet path.
+ baseRequest.setContext(old_context);
+ __context.set(old_context);
+ baseRequest.setContextPath(old_context_path);
+ baseRequest.setServletPath(old_servlet_path);
+ baseRequest.setPathInfo(old_path_info);
+ }
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.server.handler.ScopedHandler#doHandle(java.lang.String, org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest,
+ * javax.servlet.http.HttpServletResponse)
+ */
+ @Override
+ public void doHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ final DispatcherType dispatch = baseRequest.getDispatcherType();
+ final boolean new_context = baseRequest.takeNewContext();
+ try
+ {
+ if (new_context)
+ {
+ // Handle the REALLY SILLY request events!
+ if (!_requestAttributeListeners.isEmpty())
+ for (ServletRequestAttributeListener l :_requestAttributeListeners)
+ baseRequest.addEventListener(l);
+
+ if (!_requestListeners.isEmpty())
+ {
+ final ServletRequestEvent sre = new ServletRequestEvent(_scontext,request);
+ for (ServletRequestListener l : _requestListeners)
+ l.requestInitialized(sre);
+ }
+ }
+
+ if (DispatcherType.REQUEST.equals(dispatch) && isProtectedTarget(target))
+ {
+ response.sendError(HttpServletResponse.SC_NOT_FOUND);
+ baseRequest.setHandled(true);
+ return;
+ }
+
+ // start manual inline of nextHandle(target,baseRequest,request,response);
+ // noinspection ConstantIfStatement
+ if (never())
+ nextHandle(target,baseRequest,request,response);
+ else if (_nextScope != null && _nextScope == _handler)
+ _nextScope.doHandle(target,baseRequest,request,response);
+ else if (_handler != null)
+ _handler.handle(target,baseRequest,request,response);
+ // end manual inline
+ }
+ finally
+ {
+ // Handle more REALLY SILLY request events!
+ if (new_context)
+ {
+ if (!_requestListeners.isEmpty())
+ {
+ final ServletRequestEvent sre = new ServletRequestEvent(_scontext,request);
+ for (int i=_requestListeners.size();i-->0;)
+ _requestListeners.get(i).requestDestroyed(sre);
+ }
+
+ if (!_requestAttributeListeners.isEmpty())
+ {
+ ListIterator<ServletRequestAttributeListener> iter = _requestAttributeListeners.listIterator(_requestAttributeListeners.size());
+ for (int i=_requestAttributeListeners.size();i-->0;)
+ baseRequest.removeEventListener(_requestAttributeListeners.get(i));
+ }
+ }
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ /*
+ * Handle a runnable in this context
+ */
+ public void handle(Runnable runnable)
+ {
+ ClassLoader old_classloader = null;
+ Thread current_thread = null;
+ Context old_context = null;
+ try
+ {
+ old_context = __context.get();
+ __context.set(_scontext);
+
+ // Set the classloader
+ if (_classLoader != null)
+ {
+ current_thread = Thread.currentThread();
+ old_classloader = current_thread.getContextClassLoader();
+ current_thread.setContextClassLoader(_classLoader);
+ }
+
+ runnable.run();
+ }
+ finally
+ {
+ __context.set(old_context);
+ if (old_classloader != null && current_thread!=null)
+ {
+ current_thread.setContextClassLoader(old_classloader);
+ }
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Check the target. Called by {@link #handle(String, Request, HttpServletRequest, HttpServletResponse)} when a target within a context is determined. If
+ * the target is protected, 404 is returned.
+ */
+ /* ------------------------------------------------------------ */
+ public boolean isProtectedTarget(String target)
+ {
+ if (target == null || _protectedTargets == null)
+ return false;
+
+ while (target.startsWith("//"))
+ target=URIUtil.compactPath(target);
+
+ for (int i=0; i<_protectedTargets.length; i++)
+ {
+ String t=_protectedTargets[i];
+ if (StringUtil.startsWithIgnoreCase(target,t))
+ {
+ if (target.length()==t.length())
+ return true;
+
+ // Check that the target prefix really is a path segment, thus
+ // it can end with /, a query, a target or a parameter
+ char c=target.charAt(t.length());
+ if (c=='/'||c=='?'||c=='#'||c==';')
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param targets Array of URL prefix. Each prefix is in the form /path and will match
+ * either /path exactly or /path/anything
+ */
+ public void setProtectedTargets (String[] targets)
+ {
+ if (targets == null)
+ {
+ _protectedTargets = null;
+ return;
+ }
+
+ _protectedTargets = Arrays.copyOf(targets, targets.length);
+ }
+
+ /* ------------------------------------------------------------ */
+ public String[] getProtectedTargets()
+ {
+ if (_protectedTargets == null)
+ return null;
+
+ return Arrays.copyOf(_protectedTargets, _protectedTargets.length);
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /*
+ * @see javax.servlet.ServletContext#removeAttribute(java.lang.String)
+ */
+ @Override
+ public void removeAttribute(String name)
+ {
+ checkManagedAttribute(name,null);
+ _attributes.removeAttribute(name);
+ }
+
+ /* ------------------------------------------------------------ */
+ /*
+ * Set a context attribute. Attributes set via this API cannot be overriden by the ServletContext.setAttribute API. Their lifecycle spans the stop/start of
+ * a context. No attribute listener events are triggered by this API.
+ *
+ * @see javax.servlet.ServletContext#setAttribute(java.lang.String, java.lang.Object)
+ */
+ @Override
+ public void setAttribute( String name, Object value)
+ {
+ checkManagedAttribute(name,value);
+ _attributes.setAttribute(name,value);
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param attributes
+ * The attributes to set.
+ */
+ public void setAttributes(Attributes attributes)
+ {
+ _attributes.clearAttributes();
+ _attributes.addAll(attributes);
+ Enumeration<String> e = _attributes.getAttributeNames();
+ while (e.hasMoreElements())
+ {
+ String name = e.nextElement();
+ checkManagedAttribute(name,attributes.getAttribute(name));
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ @Override
+ public void clearAttributes()
+ {
+ Enumeration<String> e = _attributes.getAttributeNames();
+ while (e.hasMoreElements())
+ {
+ String name = e.nextElement();
+ checkManagedAttribute(name,null);
+ }
+ _attributes.clearAttributes();
+ }
+
+ /* ------------------------------------------------------------ */
+ public void checkManagedAttribute(String name, Object value)
+ {
+ if (_managedAttributes != null && _managedAttributes.containsKey(name))
+ {
+ setManagedAttribute(name,value);
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ public void setManagedAttribute(String name, Object value)
+ {
+ Object old = _managedAttributes.put(name,value);
+ updateBean(old,value);
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param classLoader
+ * The classLoader to set.
+ */
+ public void setClassLoader(ClassLoader classLoader)
+ {
+ _classLoader = classLoader;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param contextPath
+ * The _contextPath to set.
+ */
+ public void setContextPath(String contextPath)
+ {
+ if (contextPath == null)
+ throw new IllegalArgumentException("null contextPath");
+
+ if (contextPath.endsWith("/*"))
+ {
+ LOG.warn(this+" contextPath ends with /*");
+ contextPath=contextPath.substring(0,contextPath.length()-2);
+ }
+ else if (contextPath.length()>1 && contextPath.endsWith("/"))
+ {
+ LOG.warn(this+" contextPath ends with /");
+ contextPath=contextPath.substring(0,contextPath.length()-1);
+ }
+
+ if (contextPath.length()==0)
+ {
+ LOG.warn("Empty contextPath");
+ contextPath="/";
+ }
+
+ _contextPath = contextPath;
+
+ if (getServer() != null && (getServer().isStarting() || getServer().isStarted()))
+ {
+ Handler[] contextCollections = getServer().getChildHandlersByClass(ContextHandlerCollection.class);
+ for (int h = 0; contextCollections != null && h < contextCollections.length; h++)
+ ((ContextHandlerCollection)contextCollections[h]).mapContexts();
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param servletContextName
+ * The servletContextName to set.
+ */
+ public void setDisplayName(String servletContextName)
+ {
+ _displayName = servletContextName;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @return Returns the resourceBase.
+ */
+ public Resource getBaseResource()
+ {
+ if (_baseResource == null)
+ return null;
+ return _baseResource;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @return Returns the base resource as a string.
+ */
+ @ManagedAttribute("document root for context")
+ public String getResourceBase()
+ {
+ if (_baseResource == null)
+ return null;
+ return _baseResource.toString();
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Set the base resource for this context.
+ * @param base The resource used as the base for all static content of this context.
+ * @see #setResourceBase(String)
+ */
+ public void setBaseResource(Resource base)
+ {
+ _baseResource = base;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Set the base resource for this context.
+ * @param resourceBase A string representing the base resource for the context. Any string accepted
+ * by {@link Resource#newResource(String)} may be passed and the call is equivalent to
+ * <code>setBaseResource(newResource(resourceBase));</code>
+ */
+ public void setResourceBase(String resourceBase)
+ {
+ try
+ {
+ setBaseResource(newResource(resourceBase));
+ }
+ catch (Exception e)
+ {
+ LOG.warn(e.toString());
+ LOG.debug(e);
+ throw new IllegalArgumentException(resourceBase);
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @return Returns the mimeTypes.
+ */
+ public MimeTypes getMimeTypes()
+ {
+ if (_mimeTypes == null)
+ _mimeTypes = new MimeTypes();
+ return _mimeTypes;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param mimeTypes
+ * The mimeTypes to set.
+ */
+ public void setMimeTypes(MimeTypes mimeTypes)
+ {
+ _mimeTypes = mimeTypes;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ */
+ public void setWelcomeFiles(String[] files)
+ {
+ _welcomeFiles = files;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @return The names of the files which the server should consider to be welcome files in this context.
+ * @see <a href="http://jcp.org/aboutJava/communityprocess/final/jsr154/index.html">The Servlet Specification</a>
+ * @see #setWelcomeFiles
+ */
+ @ManagedAttribute(value="Partial URIs of directory welcome files", readonly=true)
+ public String[] getWelcomeFiles()
+ {
+ return _welcomeFiles;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @return Returns the errorHandler.
+ */
+ @ManagedAttribute("The error handler to use for the context")
+ public ErrorHandler getErrorHandler()
+ {
+ return _errorHandler;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param errorHandler
+ * The errorHandler to set.
+ */
+ public void setErrorHandler(ErrorHandler errorHandler)
+ {
+ if (errorHandler != null)
+ errorHandler.setServer(getServer());
+ updateBean(_errorHandler,errorHandler);
+ _errorHandler = errorHandler;
+ }
+
+ /* ------------------------------------------------------------ */
+ @ManagedAttribute("The maximum content size")
+ public int getMaxFormContentSize()
+ {
+ return _maxFormContentSize;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Set the maximum size of a form post, to protect against DOS attacks from large forms.
+ * @param maxSize
+ */
+ public void setMaxFormContentSize(int maxSize)
+ {
+ _maxFormContentSize = maxSize;
+ }
+
+ /* ------------------------------------------------------------ */
+ public int getMaxFormKeys()
+ {
+ return _maxFormKeys;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Set the maximum number of form Keys to protect against DOS attack from crafted hash keys.
+ * @param max
+ */
+ public void setMaxFormKeys(int max)
+ {
+ _maxFormKeys = max;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @return True if URLs are compacted to replace multiple '/'s with a single '/'
+ */
+ public boolean isCompactPath()
+ {
+ return _compactPath;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param compactPath
+ * True if URLs are compacted to replace multiple '/'s with a single '/'
+ */
+ public void setCompactPath(boolean compactPath)
+ {
+ _compactPath = compactPath;
+ }
+
+ /* ------------------------------------------------------------ */
+ @Override
+ public String toString()
+ {
+ String[] vhosts = getVirtualHosts();
+
+ StringBuilder b = new StringBuilder();
+
+ Package pkg = getClass().getPackage();
+ if (pkg != null)
+ {
+ String p = pkg.getName();
+ if (p != null && p.length() > 0)
+ {
+ String[] ss = p.split("\\.");
+ for (String s : ss)
+ b.append(s.charAt(0)).append('.');
+ }
+ }
+ b.append(getClass().getSimpleName()).append('@').append(Integer.toString(hashCode(),16));
+ b.append('{').append(getContextPath()).append(',').append(getBaseResource()).append(',').append(_availability);
+
+ if (vhosts != null && vhosts.length > 0)
+ b.append(',').append(vhosts[0]);
+ b.append('}');
+
+ return b.toString();
+ }
+
+ /* ------------------------------------------------------------ */
+ public synchronized Class<?> loadClass(String className) throws ClassNotFoundException
+ {
+ if (className == null)
+ return null;
+
+ if (_classLoader == null)
+ return Loader.loadClass(this.getClass(),className);
+
+ return _classLoader.loadClass(className);
+ }
+
+ /* ------------------------------------------------------------ */
+ public void addLocaleEncoding(String locale, String encoding)
+ {
+ if (_localeEncodingMap == null)
+ _localeEncodingMap = new HashMap<String, String>();
+ _localeEncodingMap.put(locale,encoding);
+ }
+
+ /* ------------------------------------------------------------ */
+ public String getLocaleEncoding(String locale)
+ {
+ if (_localeEncodingMap == null)
+ return null;
+ String encoding = _localeEncodingMap.get(locale);
+ return encoding;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * 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
+ * language is looked up.
+ *
+ * @param locale
+ * a <code>Locale</code> value
+ * @return a <code>String</code> representing the character encoding for the locale or null if none found.
+ */
+ public String getLocaleEncoding(Locale locale)
+ {
+ if (_localeEncodingMap == null)
+ return null;
+ String encoding = _localeEncodingMap.get(locale.toString());
+ if (encoding == null)
+ encoding = _localeEncodingMap.get(locale.getLanguage());
+ return encoding;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Get all of the locale encodings
+ *
+ * @return a map of all the locale encodings: key is name of the locale and value is the char encoding
+ */
+ public Map<String,String> getLocaleEncodings()
+ {
+ if (_localeEncodingMap == null)
+ return null;
+ return Collections.unmodifiableMap(_localeEncodingMap);
+ }
+
+ /* ------------------------------------------------------------ */
+ /*
+ */
+ public Resource getResource(String path) throws MalformedURLException
+ {
+ if (path == null || !path.startsWith(URIUtil.SLASH))
+ throw new MalformedURLException(path);
+
+ if (_baseResource == null)
+ return null;
+
+ try
+ {
+ path = URIUtil.canonicalPath(path);
+ Resource resource = _baseResource.addPath(path);
+
+ if (checkAlias(path,resource))
+ return resource;
+ return null;
+ }
+ catch (Exception e)
+ {
+ LOG.ignore(e);
+ }
+
+ return null;
+ }
+
+ /* ------------------------------------------------------------ */
+ public boolean checkAlias(String path, Resource resource)
+ {
+ // Is the resource aliased?
+ if (resource.getAlias() != null)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Aliased resource: " + resource + "~=" + resource.getAlias());
+
+ // alias checks
+ for (Iterator<AliasCheck> i=_aliasChecks.iterator();i.hasNext();)
+ {
+ AliasCheck check = i.next();
+ if (check.check(path,resource))
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Aliased resource: " + resource + " approved by " + check);
+ return true;
+ }
+ }
+ return false;
+ }
+ return true;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Convert URL to Resource wrapper for {@link Resource#newResource(URL)} enables extensions to provide alternate resource implementations.
+ */
+ public Resource newResource(URL url) throws IOException
+ {
+ return Resource.newResource(url);
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Convert URL to Resource wrapper for {@link Resource#newResource(URL)} enables extensions to provide alternate resource implementations.
+ */
+ public Resource newResource(URI uri) throws IOException
+ {
+ return Resource.newResource(uri);
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Convert a URL or path to a Resource. The default implementation is a wrapper for {@link Resource#newResource(String)}.
+ *
+ * @param urlOrPath
+ * The URL or path to convert
+ * @return The Resource for the URL/path
+ * @throws IOException
+ * The Resource could not be created.
+ */
+ public Resource newResource(String urlOrPath) throws IOException
+ {
+ return Resource.newResource(urlOrPath);
+ }
+
+ /* ------------------------------------------------------------ */
+ /*
+ */
+ public Set<String> getResourcePaths(String path)
+ {
+ try
+ {
+ path = URIUtil.canonicalPath(path);
+ Resource resource = getResource(path);
+
+ if (resource != null && resource.exists())
+ {
+ if (!path.endsWith(URIUtil.SLASH))
+ path = path + URIUtil.SLASH;
+
+ String[] l = resource.list();
+ if (l != null)
+ {
+ HashSet<String> set = new HashSet<String>();
+ for (int i = 0; i < l.length; i++)
+ set.add(path + l[i]);
+ return set;
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ LOG.ignore(e);
+ }
+ return Collections.emptySet();
+ }
+
+ /* ------------------------------------------------------------ */
+ private String normalizeHostname(String host)
+ {
+ if (host == null)
+ return null;
+
+ if (host.endsWith("."))
+ return host.substring(0,host.length() - 1);
+
+ return host;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Add an AliasCheck instance to possibly permit aliased resources
+ * @param check The alias checker
+ */
+ public void addAliasCheck(AliasCheck check)
+ {
+ _aliasChecks.add(check);
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @return Mutable list of Alias checks
+ */
+ public List<AliasCheck> getAliasChecks()
+ {
+ return _aliasChecks;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param checks list of AliasCheck instances
+ */
+ public void setAliasChecks(List<AliasCheck> checks)
+ {
+ _aliasChecks.clear();
+ _aliasChecks.addAll(checks);
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Context.
+ * <p>
+ * A partial implementation of {@link javax.servlet.ServletContext}. A complete implementation is provided by the derived {@link ContextHandler}.
+ * </p>
+ *
+ *
+ */
+ public class Context extends NoContext
+ {
+ protected boolean _enabled = true; //whether or not the dynamic API is enabled for callers
+ protected boolean _extendedListenerTypes = false;
+
+
+ /* ------------------------------------------------------------ */
+ protected Context()
+ {
+ }
+
+ /* ------------------------------------------------------------ */
+ public ContextHandler getContextHandler()
+ {
+ return ContextHandler.this;
+ }
+
+ /* ------------------------------------------------------------ */
+ /*
+ * @see javax.servlet.ServletContext#getContext(java.lang.String)
+ */
+ @Override
+ public ServletContext getContext(String uripath)
+ {
+ List<ContextHandler> contexts = new ArrayList<ContextHandler>();
+ Handler[] handlers = getServer().getChildHandlersByClass(ContextHandler.class);
+ String matched_path = null;
+
+ for (Handler handler : handlers)
+ {
+ if (handler == null)
+ continue;
+ ContextHandler ch = (ContextHandler)handler;
+ String context_path = ch.getContextPath();
+
+ if (uripath.equals(context_path) || (uripath.startsWith(context_path) && uripath.charAt(context_path.length()) == '/')
+ || "/".equals(context_path))
+ {
+ // look first for vhost matching context only
+ if (getVirtualHosts() != null && getVirtualHosts().length > 0)
+ {
+ if (ch.getVirtualHosts() != null && ch.getVirtualHosts().length > 0)
+ {
+ for (String h1 : getVirtualHosts())
+ for (String h2 : ch.getVirtualHosts())
+ if (h1.equals(h2))
+ {
+ if (matched_path == null || context_path.length() > matched_path.length())
+ {
+ contexts.clear();
+ matched_path = context_path;
+ }
+
+ if (matched_path.equals(context_path))
+ contexts.add(ch);
+ }
+ }
+ }
+ else
+ {
+ if (matched_path == null || context_path.length() > matched_path.length())
+ {
+ contexts.clear();
+ matched_path = context_path;
+ }
+
+ if (matched_path.equals(context_path))
+ contexts.add(ch);
+ }
+ }
+ }
+
+ if (contexts.size() > 0)
+ return contexts.get(0)._scontext;
+
+ // try again ignoring virtual hosts
+ matched_path = null;
+ for (Handler handler : handlers)
+ {
+ if (handler == null)
+ continue;
+ ContextHandler ch = (ContextHandler)handler;
+ String context_path = ch.getContextPath();
+
+ if (uripath.equals(context_path) || (uripath.startsWith(context_path) && uripath.charAt(context_path.length()) == '/')
+ || "/".equals(context_path))
+ {
+ if (matched_path == null || context_path.length() > matched_path.length())
+ {
+ contexts.clear();
+ matched_path = context_path;
+ }
+
+ if (matched_path.equals(context_path))
+ contexts.add(ch);
+ }
+ }
+
+ if (contexts.size() > 0)
+ return contexts.get(0)._scontext;
+ return null;
+ }
+
+ /* ------------------------------------------------------------ */
+ /*
+ * @see javax.servlet.ServletContext#getMimeType(java.lang.String)
+ */
+ @Override
+ public String getMimeType(String file)
+ {
+ if (_mimeTypes == null)
+ return null;
+ return _mimeTypes.getMimeByExtension(file);
+ }
+
+ /* ------------------------------------------------------------ */
+ /*
+ * @see javax.servlet.ServletContext#getRequestDispatcher(java.lang.String)
+ */
+ @Override
+ public RequestDispatcher getRequestDispatcher(String uriInContext)
+ {
+ if (uriInContext == null)
+ return null;
+
+ if (!uriInContext.startsWith("/"))
+ return null;
+
+ try
+ {
+ String query = null;
+ int q = 0;
+ if ((q = uriInContext.indexOf('?')) > 0)
+ {
+ query = uriInContext.substring(q + 1);
+ uriInContext = uriInContext.substring(0,q);
+ }
+
+ String pathInContext = URIUtil.canonicalPath(URIUtil.decodePath(uriInContext));
+ if (pathInContext!=null)
+ {
+ String uri = URIUtil.addPaths(getContextPath(),uriInContext);
+ ContextHandler context = ContextHandler.this;
+ return new Dispatcher(context,uri,pathInContext,query);
+ }
+ }
+ catch (Exception e)
+ {
+ LOG.ignore(e);
+ }
+ return null;
+ }
+
+ /* ------------------------------------------------------------ */
+ /*
+ * @see javax.servlet.ServletContext#getRealPath(java.lang.String)
+ */
+ @Override
+ public String getRealPath(String path)
+ {
+ if (path == null)
+ return null;
+ if (path.length() == 0)
+ path = URIUtil.SLASH;
+ else if (path.charAt(0) != '/')
+ path = URIUtil.SLASH + path;
+
+ try
+ {
+ Resource resource = ContextHandler.this.getResource(path);
+ if (resource != null)
+ {
+ File file = resource.getFile();
+ if (file != null)
+ return file.getCanonicalPath();
+ }
+ }
+ catch (Exception e)
+ {
+ LOG.ignore(e);
+ }
+
+ return null;
+ }
+
+ /* ------------------------------------------------------------ */
+ @Override
+ public URL getResource(String path) throws MalformedURLException
+ {
+ Resource resource = ContextHandler.this.getResource(path);
+ if (resource != null && resource.exists())
+ return resource.getURL();
+ return null;
+ }
+
+ /* ------------------------------------------------------------ */
+ /*
+ * @see javax.servlet.ServletContext#getResourceAsStream(java.lang.String)
+ */
+ @Override
+ public InputStream getResourceAsStream(String path)
+ {
+ try
+ {
+ URL url = getResource(path);
+ if (url == null)
+ return null;
+ Resource r = Resource.newResource(url);
+ return r.getInputStream();
+ }
+ catch (Exception e)
+ {
+ LOG.ignore(e);
+ return null;
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ /*
+ * @see javax.servlet.ServletContext#getResourcePaths(java.lang.String)
+ */
+ @Override
+ public Set<String> getResourcePaths(String path)
+ {
+ return ContextHandler.this.getResourcePaths(path);
+ }
+
+ /* ------------------------------------------------------------ */
+ /*
+ * @see javax.servlet.ServletContext#log(java.lang.Exception, java.lang.String)
+ */
+ @Override
+ public void log(Exception exception, String msg)
+ {
+ _logger.warn(msg,exception);
+ }
+
+ /* ------------------------------------------------------------ */
+ /*
+ * @see javax.servlet.ServletContext#log(java.lang.String)
+ */
+ @Override
+ public void log(String msg)
+ {
+ _logger.info(msg);
+ }
+
+ /* ------------------------------------------------------------ */
+ /*
+ * @see javax.servlet.ServletContext#log(java.lang.String, java.lang.Throwable)
+ */
+ @Override
+ public void log(String message, Throwable throwable)
+ {
+ _logger.warn(message,throwable);
+ }
+
+ /* ------------------------------------------------------------ */
+ /*
+ * @see javax.servlet.ServletContext#getInitParameter(java.lang.String)
+ */
+ @Override
+ public String getInitParameter(String name)
+ {
+ return ContextHandler.this.getInitParameter(name);
+ }
+
+ /* ------------------------------------------------------------ */
+ /*
+ * @see javax.servlet.ServletContext#getInitParameterNames()
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public Enumeration<String> getInitParameterNames()
+ {
+ return ContextHandler.this.getInitParameterNames();
+ }
+
+ /* ------------------------------------------------------------ */
+ /*
+ * @see javax.servlet.ServletContext#getAttribute(java.lang.String)
+ */
+ @Override
+ public synchronized Object getAttribute(String name)
+ {
+ Object o = ContextHandler.this.getAttribute(name);
+ if (o == null)
+ o = super.getAttribute(name);
+ return o;
+ }
+
+ /* ------------------------------------------------------------ */
+ /*
+ * @see javax.servlet.ServletContext#getAttributeNames()
+ */
+ @Override
+ public synchronized Enumeration<String> getAttributeNames()
+ {
+ HashSet<String> set = new HashSet<String>();
+ Enumeration<String> e = super.getAttributeNames();
+ while (e.hasMoreElements())
+ set.add(e.nextElement());
+ e = _attributes.getAttributeNames();
+ while (e.hasMoreElements())
+ set.add(e.nextElement());
+
+ return Collections.enumeration(set);
+ }
+
+ /* ------------------------------------------------------------ */
+ /*
+ * @see javax.servlet.ServletContext#setAttribute(java.lang.String, java.lang.Object)
+ */
+ @Override
+ public synchronized void setAttribute(String name, Object value)
+ {
+ checkManagedAttribute(name,value);
+ Object old_value = super.getAttribute(name);
+
+ if (value == null)
+ super.removeAttribute(name);
+ else
+ super.setAttribute(name,value);
+
+ if (!_contextAttributeListeners.isEmpty())
+ {
+ ServletContextAttributeEvent event = new ServletContextAttributeEvent(_scontext,name,old_value == null?value:old_value);
+
+ for (ServletContextAttributeListener l : _contextAttributeListeners)
+ {
+ if (old_value == null)
+ l.attributeAdded(event);
+ else if (value == null)
+ l.attributeRemoved(event);
+ else
+ l.attributeReplaced(event);
+ }
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ /*
+ * @see javax.servlet.ServletContext#removeAttribute(java.lang.String)
+ */
+ @Override
+ public synchronized void removeAttribute(String name)
+ {
+ checkManagedAttribute(name,null);
+
+ Object old_value = super.getAttribute(name);
+ super.removeAttribute(name);
+ if (old_value != null &&!_contextAttributeListeners.isEmpty())
+ {
+ ServletContextAttributeEvent event = new ServletContextAttributeEvent(_scontext,name,old_value);
+
+ for (ServletContextAttributeListener l : _contextAttributeListeners)
+ l.attributeRemoved(event);
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ /*
+ * @see javax.servlet.ServletContext#getServletContextName()
+ */
+ @Override
+ public String getServletContextName()
+ {
+ String name = ContextHandler.this.getDisplayName();
+ if (name == null)
+ name = ContextHandler.this.getContextPath();
+ return name;
+ }
+
+ /* ------------------------------------------------------------ */
+ @Override
+ public String getContextPath()
+ {
+ if ((_contextPath != null) && _contextPath.equals(URIUtil.SLASH))
+ return "";
+
+ return _contextPath;
+ }
+
+ /* ------------------------------------------------------------ */
+ @Override
+ public String toString()
+ {
+ return "ServletContext@" + ContextHandler.this.toString();
+ }
+
+ /* ------------------------------------------------------------ */
+ @Override
+ public boolean setInitParameter(String name, String value)
+ {
+ if (ContextHandler.this.getInitParameter(name) != null)
+ return false;
+ ContextHandler.this.getInitParams().put(name,value);
+ return true;
+ }
+
+ @Override
+ public void addListener(String className)
+ {
+ if (!_enabled)
+ throw new UnsupportedOperationException();
+
+ try
+ {
+ Class<? extends EventListener> clazz = _classLoader==null?Loader.loadClass(ContextHandler.class,className):_classLoader.loadClass(className);
+ addListener(clazz);
+ }
+ catch (ClassNotFoundException e)
+ {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ @Override
+ public <T extends EventListener> void addListener(T t)
+ {
+ if (!_enabled)
+ throw new UnsupportedOperationException();
+
+ checkListener(t.getClass());
+
+ ContextHandler.this.addEventListener(t);
+ ContextHandler.this.addProgrammaticListener(t);
+ }
+
+ @Override
+ public void addListener(Class<? extends EventListener> listenerClass)
+ {
+ if (!_enabled)
+ throw new UnsupportedOperationException();
+
+ try
+ {
+ EventListener e = createListener(listenerClass);
+ addListener(e);
+ }
+ catch (ServletException e)
+ {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ @Override
+ public <T extends EventListener> T createListener(Class<T> clazz) throws ServletException
+ {
+ try
+ {
+ return createInstance(clazz);
+ }
+ catch (Exception e)
+ {
+ throw new ServletException(e);
+ }
+ }
+
+
+ public void checkListener (Class<? extends EventListener> listener) throws IllegalStateException
+ {
+ boolean ok = false;
+ int startIndex = (isExtendedListenerTypes()?EXTENDED_LISTENER_TYPE_INDEX:DEFAULT_LISTENER_TYPE_INDEX);
+ for (int i=startIndex;i<SERVLET_LISTENER_TYPES.length;i++)
+ {
+ if (SERVLET_LISTENER_TYPES[i].isAssignableFrom(listener))
+ {
+ ok = true;
+ break;
+ }
+ }
+ if (!ok)
+ throw new IllegalArgumentException("Inappropriate listener class "+listener.getName());
+ }
+
+ public void setExtendedListenerTypes (boolean extended)
+ {
+ _extendedListenerTypes = extended;
+ }
+
+ public boolean isExtendedListenerTypes()
+ {
+ return _extendedListenerTypes;
+ }
+
+
+ @Override
+ public ClassLoader getClassLoader()
+ {
+ if (!_enabled)
+ throw new UnsupportedOperationException();
+
+ //no security manager just return the classloader
+ if (System.getSecurityManager() == null)
+ return _classLoader;
+ else
+ {
+ //check to see if the classloader of the caller is the same as the context
+ //classloader, or a parent of it
+ try
+ {
+ Class reflect = Loader.loadClass(getClass(), "sun.reflect.Reflection");
+ Method getCallerClass = reflect.getMethod("getCallerClass", Integer.TYPE);
+ Class caller = (Class)getCallerClass.invoke(null, 2);
+
+ boolean ok = false;
+ ClassLoader callerLoader = caller.getClassLoader();
+ while (!ok && callerLoader != null)
+ {
+ if (callerLoader == _classLoader)
+ ok = true;
+ else
+ callerLoader = callerLoader.getParent();
+ }
+
+ if (ok)
+ return _classLoader;
+ }
+ catch (Exception e)
+ {
+ LOG.warn("Unable to check classloader of caller",e);
+ }
+
+ AccessController.checkPermission(new RuntimePermission("getClassLoader"));
+ return _classLoader;
+ }
+ }
+
+ @Override
+ public JspConfigDescriptor getJspConfigDescriptor()
+ {
+ LOG.warn(__unimplmented);
+ return null;
+ }
+
+ public void setJspConfigDescriptor(JspConfigDescriptor d)
+ {
+
+ }
+
+ @Override
+ public void declareRoles(String... roleNames)
+ {
+ if (!isStarting())
+ throw new IllegalStateException ();
+ if (!_enabled)
+ throw new UnsupportedOperationException();
+ }
+
+ public void setEnabled(boolean enabled)
+ {
+ _enabled = enabled;
+ }
+
+ public boolean isEnabled()
+ {
+ return _enabled;
+ }
+
+
+
+ public <T> T createInstance (Class<T> clazz) throws Exception
+ {
+ T o = clazz.newInstance();
+ return o;
+ }
+ }
+
+
+ public static class NoContext extends AttributesMap implements ServletContext
+ {
+ private int _effectiveMajorVersion = SERVLET_MAJOR_VERSION;
+ private int _effectiveMinorVersion = SERVLET_MINOR_VERSION;
+
+ /* ------------------------------------------------------------ */
+ public NoContext()
+ {
+ }
+
+ @Override
+ public ServletContext getContext(String uripath)
+ {
+ return null;
+ }
+
+ @Override
+ public int getMajorVersion()
+ {
+ return SERVLET_MAJOR_VERSION;
+ }
+
+ @Override
+ public String getMimeType(String file)
+ {
+ return null;
+ }
+
+ @Override
+ public int getMinorVersion()
+ {
+ return SERVLET_MINOR_VERSION;
+ }
+
+ @Override
+ public RequestDispatcher getNamedDispatcher(String name)
+ {
+ return null;
+ }
+
+ @Override
+ public RequestDispatcher getRequestDispatcher(String uriInContext)
+ {
+ return null;
+ }
+
+ @Override
+ public String getRealPath(String path)
+ {
+ return null;
+ }
+
+ @Override
+ public URL getResource(String path) throws MalformedURLException
+ {
+ return null;
+ }
+
+ @Override
+ public InputStream getResourceAsStream(String path)
+ {
+ return null;
+ }
+
+ @Override
+ public Set<String> getResourcePaths(String path)
+ {
+ return null;
+ }
+
+ @Override
+ public String getServerInfo()
+ {
+ return "jetty/" + Server.getVersion();
+ }
+
+ @Override
+ @Deprecated
+ public Servlet getServlet(String name) throws ServletException
+ {
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ @Deprecated
+ public Enumeration<String> getServletNames()
+ {
+ return Collections.enumeration(Collections.EMPTY_LIST);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ @Deprecated
+ public Enumeration<Servlet> getServlets()
+ {
+ return Collections.enumeration(Collections.EMPTY_LIST);
+ }
+
+ @Override
+ public void log(Exception exception, String msg)
+ {
+ LOG.warn(msg,exception);
+ }
+
+ @Override
+ public void log(String msg)
+ {
+ LOG.info(msg);
+ }
+
+ @Override
+ public void log(String message, Throwable throwable)
+ {
+ LOG.warn(message,throwable);
+ }
+
+ @Override
+ public String getInitParameter(String name)
+ {
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Enumeration<String> getInitParameterNames()
+ {
+ return Collections.enumeration(Collections.EMPTY_LIST);
+ }
+
+
+ @Override
+ public String getServletContextName()
+ {
+ return "No Context";
+ }
+
+ @Override
+ public String getContextPath()
+ {
+ return null;
+ }
+
+
+ @Override
+ public boolean setInitParameter(String name, String value)
+ {
+ return false;
+ }
+
+ @Override
+ public Dynamic addFilter(String filterName, Class<? extends Filter> filterClass)
+ {
+ LOG.warn(__unimplmented);
+ return null;
+ }
+
+ @Override
+ public Dynamic addFilter(String filterName, Filter filter)
+ {
+ LOG.warn(__unimplmented);
+ return null;
+ }
+
+ @Override
+ public Dynamic addFilter(String filterName, String className)
+ {
+ LOG.warn(__unimplmented);
+ return null;
+ }
+
+ @Override
+ public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, Class<? extends Servlet> servletClass)
+ {
+ LOG.warn(__unimplmented);
+ return null;
+ }
+
+ @Override
+ public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet)
+ {
+ LOG.warn(__unimplmented);
+ return null;
+ }
+
+ @Override
+ public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, String className)
+ {
+ LOG.warn(__unimplmented);
+ return null;
+ }
+
+ @Override
+ public <T extends Filter> T createFilter(Class<T> c) throws ServletException
+ {
+ LOG.warn(__unimplmented);
+ return null;
+ }
+
+ @Override
+ public <T extends Servlet> T createServlet(Class<T> c) throws ServletException
+ {
+ LOG.warn(__unimplmented);
+ return null;
+ }
+
+ @Override
+ public Set<SessionTrackingMode> getDefaultSessionTrackingModes()
+ {
+ LOG.warn(__unimplmented);
+ return null;
+ }
+
+ @Override
+ public Set<SessionTrackingMode> getEffectiveSessionTrackingModes()
+ {
+ LOG.warn(__unimplmented);
+ return null;
+ }
+
+ @Override
+ public FilterRegistration getFilterRegistration(String filterName)
+ {
+ LOG.warn(__unimplmented);
+ return null;
+ }
+
+ @Override
+ public Map<String, ? extends FilterRegistration> getFilterRegistrations()
+ {
+ LOG.warn(__unimplmented);
+ return null;
+ }
+
+ @Override
+ public ServletRegistration getServletRegistration(String servletName)
+ {
+ LOG.warn(__unimplmented);
+ return null;
+ }
+
+ @Override
+ public Map<String, ? extends ServletRegistration> getServletRegistrations()
+ {
+ LOG.warn(__unimplmented);
+ return null;
+ }
+
+ @Override
+ public SessionCookieConfig getSessionCookieConfig()
+ {
+ LOG.warn(__unimplmented);
+ return null;
+ }
+
+ @Override
+ public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes)
+ {
+ LOG.warn(__unimplmented);
+ }
+
+ @Override
+ public void addListener(String className)
+ {
+ LOG.warn(__unimplmented);
+ }
+
+ @Override
+ public <T extends EventListener> void addListener(T t)
+ {
+ LOG.warn(__unimplmented);
+ }
+
+ @Override
+ public void addListener(Class<? extends EventListener> listenerClass)
+ {
+ LOG.warn(__unimplmented);
+ }
+
+ @Override
+ public <T extends EventListener> T createListener(Class<T> clazz) throws ServletException
+ {
+ try
+ {
+ return clazz.newInstance();
+ }
+ catch (InstantiationException e)
+ {
+ throw new ServletException(e);
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new ServletException(e);
+ }
+ }
+
+ @Override
+ public ClassLoader getClassLoader()
+ {
+ AccessController.checkPermission(new RuntimePermission("getClassLoader"));
+ return ContextHandler.class.getClassLoader();
+ }
+
+ @Override
+ public int getEffectiveMajorVersion()
+ {
+ return _effectiveMajorVersion;
+ }
+
+ @Override
+ public int getEffectiveMinorVersion()
+ {
+ return _effectiveMinorVersion;
+ }
+
+ public void setEffectiveMajorVersion (int v)
+ {
+ _effectiveMajorVersion = v;
+ }
+
+ public void setEffectiveMinorVersion (int v)
+ {
+ _effectiveMinorVersion = v;
+ }
+
+ @Override
+ public JspConfigDescriptor getJspConfigDescriptor()
+ {
+ LOG.warn(__unimplmented);
+ return null;
+ }
+
+ @Override
+ public void declareRoles(String... roleNames)
+ {
+ LOG.warn(__unimplmented);
+ }
+
+ /**
+ * @see javax.servlet.ServletContext#getVirtualServerName()
+ */
+ @Override
+ public String getVirtualServerName()
+ {
+ // TODO 3.1 Auto-generated method stub
+ return null;
+ }
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /** Interface to check aliases
+ */
+ public interface AliasCheck
+ {
+ /* ------------------------------------------------------------ */
+ /** Check an alias
+ * @param path The path the aliased resource was created for
+ * @param resource The aliased resourced
+ * @return True if the resource is OK to be served.
+ */
+ boolean check(String path, Resource resource);
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /** Approve all aliases.
+ */
+ public static class ApproveAliases implements AliasCheck
+ {
+ @Override
+ public boolean check(String path, Resource resource)
+ {
+ return true;
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ /** Approve Aliases with same suffix.
+ * Eg. a symbolic link from /foobar.html to /somewhere/wibble.html would be
+ * approved because both the resource and alias end with ".html".
+ */
+ @Deprecated
+ public static class ApproveSameSuffixAliases implements AliasCheck
+ {
+ {
+ LOG.warn("ApproveSameSuffixAlias is not safe for production");
+ }
+
+ @Override
+ public boolean check(String path, Resource resource)
+ {
+ int dot = path.lastIndexOf('.');
+ if (dot<0)
+ return false;
+ String suffix=path.substring(dot);
+ return resource.toString().endsWith(suffix);
+ }
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /** Approve Aliases with a path prefix.
+ * Eg. a symbolic link from /dirA/foobar.html to /dirB/foobar.html would be
+ * approved because both the resource and alias end with "/foobar.html".
+ */
+ @Deprecated
+ public static class ApprovePathPrefixAliases implements AliasCheck
+ {
+ {
+ LOG.warn("ApprovePathPrefixAliases is not safe for production");
+ }
+
+ @Override
+ public boolean check(String path, Resource resource)
+ {
+ int slash = path.lastIndexOf('/');
+ if (slash<0 || slash==path.length()-1)
+ return false;
+ String suffix=path.substring(slash);
+ return resource.toString().endsWith(suffix);
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ /** Approve Aliases of a non existent directory.
+ * If a directory "/foobar/" does not exist, then the resource is
+ * aliased to "/foobar". Accept such aliases.
+ */
+ public static class ApproveNonExistentDirectoryAliases implements AliasCheck
+ {
+ @Override
+ public boolean check(String path, Resource resource)
+ {
+ if (resource.exists())
+ return false;
+
+ String a=resource.getAlias().toString();
+ String r=resource.getURL().toString();
+
+ if (a.length()>r.length())
+ return a.startsWith(r) && a.length()==r.length()+1 && a.endsWith("/");
+ if (a.length()<r.length())
+ return r.startsWith(a) && r.length()==a.length()+1 && r.endsWith("/");
+
+ return a.equals(r);
+ }
+ }
+
+}