view src/org/eclipse/jetty/server/handler/ContextHandler.java @ 994:4e9d373bf6e9

remove ContextHandler.Context
author Franklin Schmidt <fschmidt@gmail.com>
date Tue, 18 Oct 2016 16:34:18 -0600
parents d9d95acded81
children 0eba8f555c19
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.
 *
 */
public final class ContextHandler extends HandlerWrapper implements Server.Graceful
{
	private static final Logger LOG = LoggerFactory.getLogger(ContextHandler.class);

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

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

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

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

		super.doStart();

		synchronized(this)
		{
			_availability = _shutdown?__SHUTDOWN:__AVAILABLE;
		}
	}

	/* ------------------------------------------------------------ */
	/*
	 * @see org.eclipse.thread.AbstractLifeCycle#doStop()
	 */
	@Override
	protected void doStop() throws Exception
	{
		_availability = __STOPPED;

		super.doStop();
		LOG.info("stopped {}",this);
	}

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

	@Override
	public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
	{
		if (LOG.isDebugEnabled())
			LOG.debug("scope {}|{}|{} @ {}",baseRequest.getContextPath(),baseRequest.getServletPath(),baseRequest.getPathInfo(),this);

		String old_context_path = null;
		String old_servlet_path = null;
		String old_path_info = null;
		String pathInfo = target;

		ContextHandler oldContextHandler = baseRequest._contextHandler;

		// Are we already in this context?
		if (oldContextHandler != this)
		{
			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._contextHandler = this;
			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);

			try
			{
				super.handle(target,baseRequest,request,response);
			}
			catch (HttpException e)
			{
				LOG.debug("",e);
				baseRequest.setHandled(true);
				response.sendError(e.getStatus(),e.getReason());
			}
		}
		finally
		{
			if (oldContextHandler != this)
			{
				// reset the context and servlet path.
				baseRequest._contextHandler = oldContextHandler;
				baseRequest.setContextPath(old_context_path);
				baseRequest.setServletPath(old_servlet_path);
				baseRequest.setPathInfo(old_path_info);
			}
		}
	}

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

	public Resource getBaseResource()
	{
		return _baseResource;
	}

	public String getResourceBase()
	{
		if (_baseResource == null)
			return null;
		return _baseResource.toString();
	}

	public void setBaseResource(Resource base)
	{
		_baseResource = base;
	}

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