diff src/org/eclipse/jetty/server/handler/ContextHandler.java @ 802:3428c60d7cfc

replace jetty jars with source
author Franklin Schmidt <fschmidt@gmail.com>
date Wed, 07 Sep 2016 21:15:48 -0600
parents
children 8e9db0bbf4f9
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/handler/ContextHandler.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,2570 @@
+//
+//  ========================================================================
+//  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.net.MalformedURLException;
+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.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.servlet.DispatcherType;
+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.Filter;
+import javax.servlet.FilterRegistration;
+import javax.servlet.FilterRegistration.Dynamic;
+import javax.servlet.descriptor.JspConfigDescriptor;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpException;
+import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.server.AbstractHttpConnection;
+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.LazyList;
+import org.eclipse.jetty.util.Loader;
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.TypeUtil;
+import org.eclipse.jetty.util.URIUtil;
+import org.eclipse.jetty.util.component.AggregateLifeCycle;
+import org.eclipse.jetty.util.component.Dumpable;
+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)}
+ * 
+ * @org.apache.xbean.XBean description="Creates a basic HTTP context"
+ */
+public class ContextHandler extends ScopedHandler implements Attributes, Server.Graceful
+{
+	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();
+	}
+
+	protected Context _scontext;
+
+	private final AttributesMap _attributes;
+	private final AttributesMap _contextAttributes;
+	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 Set<String> _connectors;
+	private EventListener[] _eventListeners;
+	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 boolean _aliasesAllowed = false;
+
+	private Object _contextListeners;
+	private Object _contextAttributeListeners;
+	private Object _requestListeners;
+	private Object _requestAttributeListeners;
+	private Object _durableListeners;
+	private Map<String, Object> _managedAttributes;
+	private String[] _protectedTargets;
+	private final CopyOnWriteArrayList<AliasCheck> _aliasChecks = new CopyOnWriteArrayList<ContextHandler.AliasCheck>();
+
+	private boolean _shutdown = false;
+	private boolean _available = true;
+	private volatile int _availability; // 0=STOPPED, 1=AVAILABLE, 2=SHUTDOWN, 3=UNAVAILABLE
+
+	private final static int __STOPPED = 0, __AVAILABLE = 1, __SHUTDOWN = 2, __UNAVAILABLE = 3;
+
+	/* ------------------------------------------------------------ */
+	/**
+	 *
+	 */
+	public ContextHandler()
+	{
+		super();
+		_scontext = new Context();
+		_attributes = new AttributesMap();
+		_contextAttributes = new AttributesMap();
+		_initParams = new HashMap<String, String>();
+		addAliasCheck(new ApproveNonExistentDirectoryAliases());
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 *
+	 */
+	protected ContextHandler(Context context)
+	{
+		super();
+		_scontext = context;
+		_attributes = new AttributesMap();
+		_contextAttributes = 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
+	{
+		dumpThis(out);
+		dump(out,indent,Collections.singletonList(new CLDump(getClassLoader())),TypeUtil.asList(getHandlers()),getBeans(),_initParams.entrySet(),
+				_attributes.getAttributeEntrySet(),_contextAttributes.getAttributeEntrySet());
+	}
+
+	/* ------------------------------------------------------------ */
+	public Context getServletContext()
+	{
+		return _scontext;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @return the allowNullPathInfo true if /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)
+	{
+		if (_errorHandler != null)
+		{
+			Server old_server = getServer();
+			if (old_server != null && old_server != server)
+				old_server.getContainer().update(this,_errorHandler,null,"error",true);
+			super.setServer(server);
+			if (server != null && server != old_server)
+				server.getContainer().update(this,null,_errorHandler,"error",true);
+			_errorHandler.setServer(server);
+		}
+		else
+			super.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.
+	 */
+	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.
+	 */
+	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.
+	 */
+	public String[] getVirtualHosts()
+	{
+		return _vhosts;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @return an array of connector names that this context will accept a request from.
+	 */
+	public String[] getConnectorNames()
+	{
+		if (_connectors == null || _connectors.size() == 0)
+			return null;
+
+		return _connectors.toArray(new String[_connectors.size()]);
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * Set the names of accepted connectors.
+	 *
+	 * Names are either "host:port" or a specific configured name for a connector.
+	 *
+	 * @param connectors
+	 *            If non null, an array of connector names that this context will accept a request from.
+	 */
+	public void setConnectorNames(String[] connectors)
+	{
+		if (connectors == null || connectors.length == 0)
+			_connectors = null;
+		else
+			_connectors = new HashSet<String>(Arrays.asList(connectors));
+	}
+
+	/* ------------------------------------------------------------ */
+	/*
+	 * @see javax.servlet.ServletContext#getAttribute(java.lang.String)
+	 */
+	public Object getAttribute(String name)
+	{
+		return _attributes.getAttribute(name);
+	}
+
+	/* ------------------------------------------------------------ */
+	/*
+	 * @see javax.servlet.ServletContext#getAttributeNames()
+	 */
+	@SuppressWarnings("unchecked")
+	public Enumeration 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.
+	 */
+	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.
+	 */
+	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.
+	 */
+	public Map<String, String> getInitParams()
+	{
+		return _initParams;
+	}
+
+	/* ------------------------------------------------------------ */
+	/*
+	 * @see javax.servlet.ServletContext#getServletContextName()
+	 */
+	public String getDisplayName()
+	{
+		return _displayName;
+	}
+
+	/* ------------------------------------------------------------ */
+	public EventListener[] getEventListeners()
+	{
+		return _eventListeners;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * Set the context event listeners.
+	 *
+	 * @param eventListeners
+	 *            the event listeners
+	 * @see ServletContextListener
+	 * @see ServletContextAttributeListener
+	 * @see ServletRequestListener
+	 * @see ServletRequestAttributeListener
+	 */
+	public void setEventListeners(EventListener[] eventListeners)
+	{
+		_contextListeners = null;
+		_contextAttributeListeners = null;
+		_requestListeners = null;
+		_requestAttributeListeners = null;
+
+		_eventListeners = eventListeners;
+
+		for (int i = 0; eventListeners != null && i < eventListeners.length; i++)
+		{
+			EventListener listener = _eventListeners[i];
+
+			if (listener instanceof ServletContextListener)
+				_contextListeners = LazyList.add(_contextListeners,listener);
+
+			if (listener instanceof ServletContextAttributeListener)
+				_contextAttributeListeners = LazyList.add(_contextAttributeListeners,listener);
+
+			if (listener instanceof ServletRequestListener)
+				_requestListeners = LazyList.add(_requestListeners,listener);
+
+			if (listener instanceof ServletRequestAttributeListener)
+				_requestAttributeListeners = LazyList.add(_requestAttributeListeners,listener);
+		}
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * Add a context event listeners.
+	 *
+	 * @see ServletContextListener
+	 * @see ServletContextAttributeListener
+	 * @see ServletRequestListener
+	 * @see ServletRequestAttributeListener
+	 */
+	public void addEventListener(EventListener listener)
+	{
+		//Only listeners added before the context starts last through a stop/start cycle
+		if (!(isStarted() || isStarting()))
+			_durableListeners = LazyList.add(_durableListeners, listener);
+		
+		setEventListeners((EventListener[])LazyList.addToArray(getEventListeners(),listener,EventListener.class));
+	}
+	
+   
+	/**
+	 * Apply any necessary restrictions on a programmatically added
+	 * listener.
+	 * 
+	 * Superclasses should implement.
+	 * 
+	 * @param listener
+	 */
+	public void restrictEventListener (EventListener listener)
+	{
+	}
+
+	
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @return true if this context is accepting new requests
+	 */
+	public boolean isShutdown()
+	{
+		synchronized (this)
+		{
+			return !_shutdown;
+		}
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * 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.
+	 *
+	 * @param shutdown
+	 *            true if this context is (not?) accepting new requests
+	 */
+	public void setShutdown(boolean shutdown)
+	{
+		synchronized (this)
+		{
+			_shutdown = shutdown;
+			_availability = isRunning()?(_shutdown?__SHUTDOWN:_available?__AVAILABLE:__UNAVAILABLE):__STOPPED;
+		}
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @return false if this context is unavailable (sends 503)
+	 */
+	public boolean isAvailable()
+	{
+		synchronized (this)
+		{
+			return _available;
+		}
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * Set Available status.
+	 */
+	public void setAvailable(boolean available)
+	{
+		synchronized (this)
+		{
+			_available = available;
+			_availability = isRunning()?(_shutdown?__SHUTDOWN:_available?__AVAILABLE:__UNAVAILABLE):__STOPPED;
+		}
+	}
+
+	/* ------------------------------------------------------------ */
+	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 = __STOPPED;
+
+		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;
+
+		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();
+
+			synchronized(this)
+			{
+				_availability = _shutdown?__SHUTDOWN:_available?__AVAILABLE:__UNAVAILABLE;
+			}
+		}
+		finally
+		{
+			__context.set(old_context);
+
+			// reset the classloader
+			if (_classLoader != 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 e = _scontext.getAttributeNames();
+			while (e.hasMoreElements())
+			{
+				String name = (String)e.nextElement();
+				Object value = _scontext.getAttribute(name);
+				checkManagedAttribute(name,value);
+			}
+		}
+
+		super.doStart();
+
+		if (_errorHandler != null)
+			_errorHandler.start();
+
+		// Context listeners
+		if (_contextListeners != null)
+		{
+			ServletContextEvent event = new ServletContextEvent(_scontext);
+			for (int i = 0; i < LazyList.size(_contextListeners); i++)
+			{
+				callContextInitialized(((ServletContextListener)LazyList.get(_contextListeners, i)), event);
+			}
+		}
+	}
+
+	/* ------------------------------------------------------------ */
+	public void callContextInitialized (ServletContextListener l, ServletContextEvent e)
+	{
+		l.contextInitialized(e);
+	}
+
+	/* ------------------------------------------------------------ */
+	public void callContextDestroyed (ServletContextListener l, ServletContextEvent e)
+	{
+		l.contextDestroyed(e);
+	}
+	
+	/* ------------------------------------------------------------ */
+	/*
+	 * @see org.eclipse.thread.AbstractLifeCycle#doStop()
+	 */
+	@Override
+	protected void doStop() throws Exception
+	{
+		_availability = __STOPPED;
+
+		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 != null)
+			{
+				ServletContextEvent event = new ServletContextEvent(_scontext);
+				for (int i = LazyList.size(_contextListeners); i-- > 0;)
+				{
+					((ServletContextListener)LazyList.get(_contextListeners,i)).contextDestroyed(event);
+				}
+			}
+			
+			//remove all non-durable listeners
+			setEventListeners((EventListener[])LazyList.toArray(_durableListeners, EventListener.class));
+			_durableListeners = null;
+
+			if (_errorHandler != null)
+				_errorHandler.stop();
+
+			Enumeration e = _scontext.getAttributeNames();
+			while (e.hasMoreElements())
+			{
+				String name = (String)e.nextElement();
+				checkManagedAttribute(name,null);
+			}
+		}
+		finally
+		{
+			LOG.info("stopped {}",this);
+			__context.set(old_context);
+			// reset the classloader
+			if (_classLoader != null)
+				current_thread.setContextClassLoader(old_classloader);
+		}
+
+		_contextAttributes.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, ServletException
+	{
+		DispatcherType dispatch = baseRequest.getDispatcherType();
+
+		switch (_availability)
+		{
+			case __STOPPED:
+			case __SHUTDOWN:
+				return false;
+			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;
+
+			// TODO non-linear lookup
+			for (int i = 0; !match && i < _vhosts.length; i++)
+			{
+				String contextVhost = _vhosts[i];
+				if (contextVhost == null)
+					continue;
+				if (contextVhost.startsWith("*."))
+				{
+					// wildcard only at the beginning, and only for one additional subdomain level
+					match = contextVhost.regionMatches(true,2,vhost,vhost.indexOf(".") + 1,contextVhost.length() - 2);
+				}
+				else
+					match = contextVhost.equalsIgnoreCase(vhost);
+			}
+			if (!match)
+				return false;
+		}
+
+		// Check the connector
+		if (_connectors != null && _connectors.size() > 0)
+		{
+			String connector = AbstractHttpConnection.getCurrentConnection().getConnector().getName();
+			if (connector == null || !_connectors.contains(connector))
+				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.getAsyncContinuation().isExpired()))
+			{
+				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.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 != null)
+				{
+					final int s = LazyList.size(_requestAttributeListeners);
+					for (int i = 0; i < s; i++)
+						baseRequest.addEventListener(((EventListener)LazyList.get(_requestAttributeListeners,i)));
+				}
+
+				if (_requestListeners != null)
+				{
+					final int s = LazyList.size(_requestListeners);
+					final ServletRequestEvent sre = new ServletRequestEvent(_scontext,request);
+					for (int i = 0; i < s; i++)
+						((ServletRequestListener)LazyList.get(_requestListeners,i)).requestInitialized(sre);
+				}
+			}
+
+			if (DispatcherType.REQUEST.equals(dispatch) && isProtectedTarget(target))
+				throw new HttpException(HttpServletResponse.SC_NOT_FOUND);
+
+			// 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
+		}
+		catch (HttpException e)
+		{
+			LOG.debug(e);
+			baseRequest.setHandled(true);
+			response.sendError(e.getStatus(),e.getReason());
+		}
+		finally
+		{
+			// Handle more REALLY SILLY request events!
+			if (new_context)
+			{
+				if (_requestListeners != null)
+				{
+					final ServletRequestEvent sre = new ServletRequestEvent(_scontext,request);
+					for (int i = LazyList.size(_requestListeners); i-- > 0;)
+						((ServletRequestListener)LazyList.get(_requestListeners,i)).requestDestroyed(sre);
+				}
+
+				if (_requestAttributeListeners != null)
+				{
+					for (int i = LazyList.size(_requestAttributeListeners); i-- > 0;)
+						baseRequest.removeEventListener(((EventListener)LazyList.get(_requestAttributeListeners,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.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);
+		
+		boolean isProtected = false;
+		int i=0;
+		while (!isProtected && i<_protectedTargets.length)
+		{
+			isProtected = StringUtil.startsWithIgnoreCase(target, _protectedTargets[i++]);
+		}
+		return isProtected;
+	}
+	
+	
+	public void setProtectedTargets (String[] targets)
+	{
+		if (targets == null)
+		{
+			_protectedTargets = null;
+			return;
+		}
+		
+		_protectedTargets = new String[targets.length];
+		System.arraycopy(targets, 0, _protectedTargets, 0, targets.length);
+	}
+	
+	public String[] getProtectedTargets ()
+	{
+		if (_protectedTargets == null)
+			return null;
+		
+		String[] tmp = new String[_protectedTargets.length];
+		System.arraycopy(_protectedTargets, 0, tmp, 0, _protectedTargets.length);
+		return tmp;
+	}
+	
+
+	/* ------------------------------------------------------------ */
+	/*
+	 * @see javax.servlet.ServletContext#removeAttribute(java.lang.String)
+	 */
+	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)
+	 */
+	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 e = _attributes.getAttributeNames();
+		while (e.hasMoreElements())
+		{
+			String name = (String)e.nextElement();
+			checkManagedAttribute(name,attributes.getAttribute(name));
+		}
+	}
+
+	/* ------------------------------------------------------------ */
+	public void clearAttributes()
+	{
+		Enumeration e = _attributes.getAttributeNames();
+		while (e.hasMoreElements())
+		{
+			String name = (String)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);
+		getServer().getContainer().update(this,old,value,name,true);
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @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 && contextPath.length() > 1 && contextPath.endsWith("/"))
+			throw new IllegalArgumentException("ends with /");
+		_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.
+	 */
+	public String getResourceBase()
+	{
+		if (_baseResource == null)
+			return null;
+		return _baseResource.toString();
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @param base
+	 *            The resourceBase to set.
+	 */
+	public void setBaseResource(Resource base)
+	{
+		_baseResource = base;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @param resourceBase
+	 *            The base resource as a string.
+	 */
+	public void setResourceBase(String resourceBase)
+	{
+		try
+		{
+			setBaseResource(newResource(resourceBase));
+		}
+		catch (Exception e)
+		{
+			LOG.warn(e.toString());
+			LOG.debug(e);
+			throw new IllegalArgumentException(resourceBase);
+		}
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @return True if aliases are allowed
+	 */
+	public boolean isAliases()
+	{
+		return _aliasesAllowed;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @param aliases
+	 *            aliases are allowed
+	 */
+	public void setAliases(boolean aliases)
+	{
+		_aliasesAllowed = aliases;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @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
+	 */
+	public String[] getWelcomeFiles()
+	{
+		return _welcomeFiles;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @return Returns the errorHandler.
+	 */
+	public ErrorHandler getErrorHandler()
+	{
+		return _errorHandler;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @param errorHandler
+	 *            The errorHandler to set.
+	 */
+	public void setErrorHandler(ErrorHandler errorHandler)
+	{
+		if (errorHandler != null)
+			errorHandler.setServer(getServer());
+		if (getServer() != null)
+			getServer().getContainer().update(this,_errorHandler,errorHandler,"errorHandler",true);
+		_errorHandler = errorHandler;
+	}
+
+	/* ------------------------------------------------------------ */
+	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());
+		b.append('{').append(getContextPath()).append(',').append(getBaseResource());
+
+		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;
+	}
+
+	/* ------------------------------------------------------------ */
+	/*
+	 */
+	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 (!_aliasesAllowed && 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 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;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * Context.
+	 * <p>
+	 * A partial implementation of {@link javax.servlet.ServletContext}. A complete implementation is provided by the derived {@link ContextHandler}.
+	 * </p>
+	 *
+	 *
+	 */
+	public class Context implements ServletContext
+	{
+		protected int _majorVersion = 3;
+		protected int _minorVersion = 0;
+		protected boolean _enabled = true; //whether or not the dynamic API is enabled for callers
+
+		/* ------------------------------------------------------------ */
+		protected Context()
+		{
+		}
+
+		/* ------------------------------------------------------------ */
+		public ContextHandler getContextHandler()
+		{
+			// TODO reduce visibility of this method
+			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#getMajorVersion()
+		 */
+		@Override
+		public int getMajorVersion()
+		{
+			return 3;
+		}
+	  
+
+		/* ------------------------------------------------------------ */
+		/*
+		 * @see javax.servlet.ServletContext#getMimeType(java.lang.String)
+		 */
+		@Override
+		public String getMimeType(String file)
+		{
+			if (_mimeTypes == null)
+				return null;
+			Buffer mime = _mimeTypes.getMimeByExtension(file);
+			if (mime != null)
+				return mime.toString();
+			return null;
+		}
+
+		/* ------------------------------------------------------------ */
+		/*
+		 * @see javax.servlet.ServletContext#getMinorVersion()
+		 */
+		@Override
+		public int getMinorVersion()
+		{
+			return 0;
+		}
+
+		/* ------------------------------------------------------------ */
+		/*
+		 * @see javax.servlet.ServletContext#getNamedDispatcher(java.lang.String)
+		 */
+		@Override
+		public RequestDispatcher getNamedDispatcher(String name)
+		{
+			return null;
+		}
+
+		/* ------------------------------------------------------------ */
+		/*
+		 * @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 getResourcePaths(String path)
+		{
+			return ContextHandler.this.getResourcePaths(path);
+		}
+
+		/* ------------------------------------------------------------ */
+		/*
+		 * @see javax.servlet.ServletContext#getServerInfo()
+		 */
+		@Override
+		public String getServerInfo()
+		{
+			return "jetty/" + Server.getVersion();
+		}
+
+		/* ------------------------------------------------------------ */
+		/*
+		 * @see javax.servlet.ServletContext#getServlet(java.lang.String)
+		 */
+		@Override
+		@Deprecated
+		public Servlet getServlet(String name) throws ServletException
+		{
+			return null;
+		}
+
+		/* ------------------------------------------------------------ */
+		/*
+		 * @see javax.servlet.ServletContext#getServletNames()
+		 */
+		@SuppressWarnings("unchecked")
+		@Override
+		@Deprecated
+		public Enumeration getServletNames()
+		{
+			return Collections.enumeration(Collections.EMPTY_LIST);
+		}
+
+		/* ------------------------------------------------------------ */
+		/*
+		 * @see javax.servlet.ServletContext#getServlets()
+		 */
+		@SuppressWarnings("unchecked")
+		@Override
+		@Deprecated
+		public Enumeration getServlets()
+		{
+			return Collections.enumeration(Collections.EMPTY_LIST);
+		}
+
+		/* ------------------------------------------------------------ */
+		/*
+		 * @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 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 && _contextAttributes != null)
+				o = _contextAttributes.getAttribute(name);
+			return o;
+		}
+
+		/* ------------------------------------------------------------ */
+		/*
+		 * @see javax.servlet.ServletContext#getAttributeNames()
+		 */
+		@SuppressWarnings("unchecked")
+		@Override
+		public synchronized Enumeration getAttributeNames()
+		{
+			HashSet<String> set = new HashSet<String>();
+			if (_contextAttributes != null)
+			{
+				Enumeration<String> e = _contextAttributes.getAttributeNames();
+				while (e.hasMoreElements())
+					set.add(e.nextElement());
+			}
+			Enumeration<String> 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 = _contextAttributes.getAttribute(name);
+
+			if (value == null)
+				_contextAttributes.removeAttribute(name);
+			else
+				_contextAttributes.setAttribute(name,value);
+
+			if (_contextAttributeListeners != null)
+			{
+				ServletContextAttributeEvent event = new ServletContextAttributeEvent(_scontext,name,old_value == null?value:old_value);
+
+				for (int i = 0; i < LazyList.size(_contextAttributeListeners); i++)
+				{
+					ServletContextAttributeListener l = (ServletContextAttributeListener)LazyList.get(_contextAttributeListeners,i);
+
+					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);
+
+			if (_contextAttributes == null)
+			{
+				// Set it on the handler
+				_attributes.removeAttribute(name);
+				return;
+			}
+
+			Object old_value = _contextAttributes.getAttribute(name);
+			_contextAttributes.removeAttribute(name);
+			if (old_value != null)
+			{
+				if (_contextAttributeListeners != null)
+				{
+					ServletContextAttributeEvent event = new ServletContextAttributeEvent(_scontext,name,old_value);
+
+					for (int i = 0; i < LazyList.size(_contextAttributeListeners); i++)
+						((ServletContextAttributeListener)LazyList.get(_contextAttributeListeners,i)).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;
+		}
+
+		/* ------------------------------------------------------------ */
+		final private static String __unimplmented="Unimplemented - use org.eclipse.jetty.servlet.ServletContextHandler";
+
+		@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)
+		{
+			if (!_enabled)
+				throw new UnsupportedOperationException();
+			
+			try
+			{
+//				Class<? extends EventListener> clazz = _classLoader==null?Loader.loadClass(ContextHandler.class,className):_classLoader.loadClass(className);
+				Class 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();
+			ContextHandler.this.addEventListener(t);
+			ContextHandler.this.restrictEventListener(t);
+		}
+
+		@Override
+		public void addListener(Class<? extends EventListener> listenerClass)
+		{            
+			if (!_enabled)
+				throw new UnsupportedOperationException();
+
+			try
+			{
+				EventListener e = createListener(listenerClass);
+				ContextHandler.this.addEventListener(e);
+				ContextHandler.this.restrictEventListener(e);
+			}
+			catch (ServletException e)
+			{
+				throw new IllegalArgumentException(e);
+			}
+		}
+
+		@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 _classLoader;
+		}
+
+		@Override
+		public int getEffectiveMajorVersion()
+		{
+			return _majorVersion;
+		}
+
+		@Override
+		public int getEffectiveMinorVersion()
+		{
+			return _minorVersion;
+		}
+
+		public void setEffectiveMajorVersion (int v)
+		{
+			_majorVersion = v;
+		}
+		
+		public void setEffectiveMinorVersion (int v)
+		{
+			_minorVersion = v;
+		}
+		
+		@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();
+			
+			// TODO Auto-generated method stub
+			
+		}
+
+		public void setEnabled(boolean enabled)
+		{
+			_enabled = enabled;
+		}
+
+		public boolean isEnabled()
+		{
+			return _enabled;
+		}
+	}
+
+	private static class CLDump implements Dumpable
+	{
+		final ClassLoader _loader;
+
+		CLDump(ClassLoader loader)
+		{
+			_loader = loader;
+		}
+
+		public String dump()
+		{
+			return AggregateLifeCycle.dump(this);
+		}
+
+		public void dump(Appendable out, String indent) throws IOException
+		{
+			out.append(String.valueOf(_loader)).append("\n");
+
+			if (_loader != null)
+			{
+				Object parent = _loader.getParent();
+				if (parent != null)
+				{
+					if (!(parent instanceof Dumpable))
+						parent = new CLDump((ClassLoader)parent);
+
+					if (_loader instanceof URLClassLoader)
+						AggregateLifeCycle.dump(out,indent,TypeUtil.asList(((URLClassLoader)_loader).getURLs()),Collections.singleton(parent));
+					else
+						AggregateLifeCycle.dump(out,indent,Collections.singleton(parent));
+				}
+			}
+		}
+	}
+	
+	
+	/* ------------------------------------------------------------ */
+	/** 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 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");
+		}
+		
+		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");
+		}
+		
+		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
+	{
+		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("/");
+			else
+				return r.startsWith(a) && r.length()==a.length()+1 && r.endsWith("/");
+		}
+	}
+	
+}