view src/org/eclipse/jetty/server/handler/ContextHandler.java @ 991:688c39c50ee3

simplify ContextHandler
author Franklin Schmidt <fschmidt@gmail.com>
date Tue, 18 Oct 2016 00:22:46 -0600
parents 900e5b8ccd19
children 0608a6664bee
line wrap: on
line source

//
//  ========================================================================
//  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.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.Handler;
import org.eclipse.jetty.server.HandlerContainer;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.AttributesMap;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 final class ContextHandler extends ScopedHandler implements Server.Graceful
{
	private static final Logger LOG = LoggerFactory.getLogger(ContextHandler.class);

	private static final ThreadLocal<Context> __context = new ThreadLocal<Context>();

	/* ------------------------------------------------------------ */
	/**
	 * Get the current ServletContext implementation.
	 *
	 * @return ServletContext implementation
	 */
	public static ContextHandler getCurrentContext()
	{
		Context context = __context.get();
		return context==null ? null : context.getContextHandler();
	}

	private Context _scontext;

	private final AttributesMap _contextAttributes;
	private String _contextPath = "/";
	private Resource _baseResource;
	private Logger _logger;

	private boolean _shutdown = false;
	private volatile int _availability; // 0=STOPPED, 1=AVAILABLE, 2=SHUTDOWN, 3=UNAVAILABLE

	private final static int __STOPPED = 0, __AVAILABLE = 1, __SHUTDOWN = 2;

	public ContextHandler()
	{
		super();
		_scontext = new Context();
		_contextAttributes = new AttributesMap();
	}

	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,TypeUtil.asList(getHandlers()),getBeans(),
				_contextAttributes.getAttributeEntrySet());
	}

	public Context getServletContext()
	{
		return _scontext;
	}

	public String getContextPath()
	{
		return _contextPath;
	}

	/* ------------------------------------------------------------ */
	/**
	 * 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):__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 = LoggerFactory.getLogger(getContextPath());
		ClassLoader old_classloader = null;
		Thread current_thread = null;
		Context old_context = null;

		try
		{
			old_context = __context.get();
			__context.set(_scontext);

			// defers the calling of super.doStart()
			startContext();

			synchronized(this)
			{
				_availability = _shutdown?__SHUTDOWN:__AVAILABLE;
			}
		}
		finally
		{
			__context.set(old_context);
		}
	}

	/* ------------------------------------------------------------ */
	/**
	 * 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
	{
		super.doStart();
	}

	/* ------------------------------------------------------------ */
	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
		{
			super.doStop();
		}
		finally
		{
			LOG.info("stopped {}",this);
			__context.set(old_context);
		}

		_contextAttributes.clearAttributes();
	}

	/* ------------------------------------------------------------ */
	/*
	 * @see org.eclipse.jetty.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
	 */
	private boolean checkContext(final String target, final Request baseRequest, final HttpServletResponse response) throws IOException, ServletException
	{
		switch (_availability)
		{
			case __STOPPED:
			case __SHUTDOWN:
				return false;
			default:
				if (baseRequest.isHandled())
					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 (_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;

		old_context = baseRequest.getContext();

		// Are we already in this context?
		if (old_context != _scontext)
		{
			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;
			}
		}

		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 (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 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
	{
		try
		{
			// 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());
		}
	}

	public void setContextPath(String contextPath)
	{
		if (contextPath != null && contextPath.length() > 1 && contextPath.endsWith("/"))
			throw new IllegalArgumentException("ends with /");
		_contextPath = contextPath;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @return Returns the resourceBase.
	 */
	public Resource getBaseResource()
	{
		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(Resource.newResource(resourceBase));
		}
		catch (Exception e)
		{
			LOG.warn(e.toString());
			LOG.debug("",e);
			throw new IllegalArgumentException(resourceBase);
		}
	}

	@Override
	public String toString()
	{
		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());

		b.append('}');

		return b.toString();
	}

	/* ------------------------------------------------------------ */
	/*
	 */
	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 (resource.getAlias() == null)  // check not alias
				return resource;
			return null;
		}
		catch (Exception e)
		{
			LOG.trace("",e);
		}

		return null;
	}
	
	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.trace("",e);
		}
		return Collections.emptySet();
	}

	/* ------------------------------------------------------------ */
	/**
	 * Context.
	 * <p>
	 * A partial implementation of {@link javax.servlet.ServletContext}. A complete implementation is provided by the derived {@link ContextHandler}.
	 * </p>
	 *
	 *
	 */
	public final class Context implements ServletContext
	{
		protected int _majorVersion = 3;
		protected int _minorVersion = 0;

		/* ------------------------------------------------------------ */
		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))
				{
					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)
		{
			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)
		{
			throw new UnsupportedOperationException();
		}

		/* ------------------------------------------------------------ */
		/*
		 * @see javax.servlet.ServletContext#getRequestDispatcher(java.lang.String)
		 */
		@Override
		public RequestDispatcher getRequestDispatcher(String uriInContext)
		{
			throw new UnsupportedOperationException();
		}

		/* ------------------------------------------------------------ */
		/*
		 * @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.trace("",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.trace("",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.version;
		}

		/* ------------------------------------------------------------ */
		/*
		 * @see javax.servlet.ServletContext#getServlet(java.lang.String)
		 */
		@Override
		@Deprecated
		public Servlet getServlet(String name) throws ServletException
		{
			throw new UnsupportedOperationException();
		}

		/* ------------------------------------------------------------ */
		/*
		 * @see javax.servlet.ServletContext#getServletNames()
		 */
		@SuppressWarnings("unchecked")
		@Override
		@Deprecated
		public Enumeration getServletNames()
		{
			throw new UnsupportedOperationException();
		}

		/* ------------------------------------------------------------ */
		/*
		 * @see javax.servlet.ServletContext#getServlets()
		 */
		@SuppressWarnings("unchecked")
		@Override
		@Deprecated
		public Enumeration getServlets()
		{
			throw new UnsupportedOperationException();
		}

		/* ------------------------------------------------------------ */
		/*
		 * @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 null;
		}

		/* ------------------------------------------------------------ */
		/*
		 * @see javax.servlet.ServletContext#getInitParameterNames()
		 */
		@SuppressWarnings("unchecked")
		@Override
		public Enumeration getInitParameterNames()
		{
			return null;
		}

		/* ------------------------------------------------------------ */
		/*
		 * @see javax.servlet.ServletContext#getAttribute(java.lang.String)
		 */
		@Override
		public synchronized Object getAttribute(String name)
		{
			if (_contextAttributes != null)
				return _contextAttributes.getAttribute(name);
			return null;
		}

		/* ------------------------------------------------------------ */
		/*
		 * @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());
			}

			return Collections.enumeration(set);
		}

		/* ------------------------------------------------------------ */
		/*
		 * @see javax.servlet.ServletContext#setAttribute(java.lang.String, java.lang.Object)
		 */
		@Override
		public synchronized void setAttribute(String name, Object value)
		{
			Object old_value = _contextAttributes.getAttribute(name);

			if (value == null)
				_contextAttributes.removeAttribute(name);
			else
				_contextAttributes.setAttribute(name,value);
		}

		/* ------------------------------------------------------------ */
		/*
		 * @see javax.servlet.ServletContext#removeAttribute(java.lang.String)
		 */
		@Override
		public synchronized void removeAttribute(String name)
		{
			if (_contextAttributes == null)
			{
				// Set it on the handler
				return;
			}

			_contextAttributes.removeAttribute(name);
		}

		/* ------------------------------------------------------------ */
		/*
		 * @see javax.servlet.ServletContext#getServletContextName()
		 */
		@Override
		public String getServletContextName()
		{
			return ContextHandler.this.getContextPath();
		}

		/* ------------------------------------------------------------ */
		@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)
		{
			return false;
		}

		/* ------------------------------------------------------------ */
		final private static String __unimplmented="Unimplemented - use org.eclipse.jetty.servlet.ServletContextHandler";

		@Override
		public Dynamic addFilter(String filterName, Class<? extends Filter> filterClass)
		{
			throw new UnsupportedOperationException();
		}

		@Override
		public Dynamic addFilter(String filterName, Filter filter)
		{
			throw new UnsupportedOperationException();
		}

		@Override
		public Dynamic addFilter(String filterName, String className)
		{
			throw new UnsupportedOperationException();
		}

		@Override
		public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, Class<? extends Servlet> servletClass)
		{
			throw new UnsupportedOperationException();
		}

		@Override
		public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet)
		{
			throw new UnsupportedOperationException();
		}

		@Override
		public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, String className)
		{
			throw new UnsupportedOperationException();
		}

		@Override
		public <T extends Filter> T createFilter(Class<T> c) throws ServletException
		{
			throw new UnsupportedOperationException();
		}

		@Override
		public <T extends Servlet> T createServlet(Class<T> c) throws ServletException
		{
			throw new UnsupportedOperationException();
		}

		@Override
		public Set<SessionTrackingMode> getDefaultSessionTrackingModes()
		{
			throw new UnsupportedOperationException();
		}

		@Override
		public Set<SessionTrackingMode> getEffectiveSessionTrackingModes()
		{
			throw new UnsupportedOperationException();
		}

		@Override
		public FilterRegistration getFilterRegistration(String filterName)
		{
			throw new UnsupportedOperationException();
		}

		@Override
		public Map<String, ? extends FilterRegistration> getFilterRegistrations()
		{
			throw new UnsupportedOperationException();
		}

		@Override
		public ServletRegistration getServletRegistration(String servletName)
		{
			throw new UnsupportedOperationException();
		}

		@Override
		public Map<String, ? extends ServletRegistration> getServletRegistrations()
		{
			throw new UnsupportedOperationException();
		}

		@Override
		public SessionCookieConfig getSessionCookieConfig()
		{
			throw new UnsupportedOperationException();
		}

		@Override
		public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes)
		{
			throw new UnsupportedOperationException();
		}

		@Override
		public void addListener(String className)
		{
			throw new UnsupportedOperationException();
		}

		@Override
		public <T extends EventListener> void addListener(T t)
		{            
			throw new UnsupportedOperationException();
		}

		@Override
		public void addListener(Class<? extends EventListener> listenerClass)
		{            
			throw new UnsupportedOperationException();
		}

		@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 null;
		}

		@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()
		{
			throw new UnsupportedOperationException();
		}

		public void setJspConfigDescriptor(JspConfigDescriptor d)
		{
			throw new UnsupportedOperationException();
		}
		
		@Override
		public void declareRoles(String... roleNames)
		{
			throw new UnsupportedOperationException();
		}
	}

}