|
From: <bra...@us...> - 2010-04-27 22:12:38
|
Revision: 3074
http://archive-access.svn.sourceforge.net/archive-access/?rev=3074&view=rev
Author: bradtofel
Date: 2010-04-27 22:12:31 +0000 (Tue, 27 Apr 2010)
Log Message:
-----------
REFACTOR: moved all the glue functionality, which ties Wayback to a ServletContext, into this package, including:
* Top-level RequestFilter Filter implementation
* Spring XML parsing
* setting up mapping between RequestHandler (formerly ServletRequestContext) and the RequestMapper (see below)
* RequestMapper functionality
** Now provides for more flexible mapping of RequestHandler to host, port, and path
** includes registration of notification of ServletContext destruction
** includes ability to register a global RequestHandler to possibly handle incoming requests before normal mapping, and after normal mapping, should no RequestHandler match the incoming request
Added Paths:
-----------
trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/
trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/AbstractRequestHandler.java
trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/BeanNameRegistrar.java
trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/PortMapper.java
trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/RequestFilter.java
trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/RequestHandler.java
trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/RequestHandlerContext.java
trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/RequestMapper.java
trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/ShutdownListener.java
trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/SpringReader.java
trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/StaticFileRequestHandler.java
Added: trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/AbstractRequestHandler.java
===================================================================
--- trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/AbstractRequestHandler.java (rev 0)
+++ trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/AbstractRequestHandler.java 2010-04-27 22:12:31 UTC (rev 3074)
@@ -0,0 +1,47 @@
+/**
+ *
+ */
+package org.archive.wayback.util.webapp;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+
+
+/**
+ * Abstract RequestHandler implementation which performs the minimal behavior
+ * for self registration with a RequestMapper, requiring subclasses to implement
+ * only handleRequest().
+ *
+ * @author brad
+ *
+ */
+public abstract class AbstractRequestHandler implements RequestHandler {
+ private String beanName = null;
+ private ServletContext servletContext = null;
+
+ public void setBeanName(final String beanName) {
+ this.beanName = beanName;
+ }
+ public String getBeanName() {
+ return beanName;
+ }
+
+ public void setServletContext(ServletContext servletContext) {
+ this.servletContext = servletContext;
+ }
+ public ServletContext getServletContext() {
+ return servletContext;
+ }
+
+ public void registerPortListener(RequestMapper requestMapper) {
+ BeanNameRegistrar.registerHandler(this, requestMapper);
+ }
+
+ public String translateRequestPath(HttpServletRequest httpRequest) {
+ return RequestMapper.getRequestContextPath(httpRequest);
+ }
+
+ public String translateRequestPathQuery(HttpServletRequest httpRequest) {
+ return RequestMapper.getRequestContextPathQuery(httpRequest);
+ }
+}
Property changes on: trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/AbstractRequestHandler.java
___________________________________________________________________
Added: svn:keywords
+ Author Date Revision Id
Added: trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/BeanNameRegistrar.java
===================================================================
--- trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/BeanNameRegistrar.java (rev 0)
+++ trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/BeanNameRegistrar.java 2010-04-27 22:12:31 UTC (rev 3074)
@@ -0,0 +1,150 @@
+/**
+ *
+ */
+package org.archive.wayback.util.webapp;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Helper static methods to implement registration of a RequestHandler with a
+ * RequestMapper, based on the beanName() method.
+ *
+ * @author brad
+ *
+ */
+public class BeanNameRegistrar {
+
+ private static final Logger LOGGER = Logger.getLogger(
+ BeanNameRegistrar.class.getName());
+
+ private static final String PORT_PATTERN_STRING =
+ "([0-9]+):?";
+ private static final String PORT_PATH_PATTERN_STRING =
+ "([0-9]+):([0-9a-zA-Z_.-]+)";
+ private static final String HOST_PORT_PATTERN_STRING =
+ "([0-9a-z_.-]+):([0-9]+):?";
+ private static final String HOST_PORT_PATH_PATTERN_STRING =
+ "([0-9a-z_.-]+):([0-9]+):([0-9a-zA-Z_.-]+)";
+
+ private static final Pattern PORT_PATTERN =
+ Pattern.compile(PORT_PATTERN_STRING);
+ private static final Pattern PORT_PATH_PATTERN =
+ Pattern.compile(PORT_PATH_PATTERN_STRING);
+ private static final Pattern HOST_PORT_PATTERN =
+ Pattern.compile(HOST_PORT_PATTERN_STRING);
+ private static final Pattern HOST_PORT_PATH_PATTERN =
+ Pattern.compile(HOST_PORT_PATH_PATTERN_STRING);
+
+ /*
+ * matches:
+ * 8080
+ * 8080:
+ */
+ private static boolean registerPort(String name, RequestHandler handler,
+ RequestMapper mapper) {
+ Matcher m = null;
+ m = PORT_PATTERN.matcher(name);
+ if(m.matches()) {
+ int port = Integer.parseInt(m.group(1));
+ mapper.addRequestHandler(port, null, null, handler);
+ return true;
+ }
+ return false;
+ }
+ /*
+ * matches:
+ * 8080:blue
+ * 8080:fish
+ */
+ private static boolean registerPortPath(String name, RequestHandler handler,
+ RequestMapper mapper) {
+ Matcher m = null;
+ m = PORT_PATH_PATTERN.matcher(name);
+ if(m.matches()) {
+ int port = Integer.parseInt(m.group(1));
+ mapper.addRequestHandler(port, null, m.group(2), handler);
+ return true;
+ }
+ return false;
+ }
+ /*
+ * matches:
+ * localhost.archive.org:8080
+ * static.localhost.archive.org:8080
+ */
+ private static boolean registerHostPort(String name, RequestHandler handler,
+ RequestMapper mapper) {
+ Matcher m = null;
+ m = HOST_PORT_PATTERN.matcher(name);
+ if(m.matches()) {
+ int port = Integer.parseInt(m.group(2));
+ mapper.addRequestHandler(port, m.group(1), null, handler);
+ return true;
+ }
+ return false;
+ }
+ /*
+ * matches:
+ * localhost.archive.org:8080:two
+ * static.localhost.archive.org:8080:fish
+ */
+ private static boolean registerHostPortPath(String name,
+ RequestHandler handler, RequestMapper mapper) {
+ Matcher m = null;
+ m = HOST_PORT_PATH_PATTERN.matcher(name);
+ if(m.matches()) {
+ int port = Integer.parseInt(m.group(2));
+ mapper.addRequestHandler(port, m.group(1), m.group(3), handler);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Extract the RequestHandler objects beanName, parse it, and register the
+ * RequestHandler with the RequestMapper according to the beanNames
+ * semantics.
+ * @param handler The RequestHandler to register
+ * @param mapper the RequestMapper where the RequestHandler should be
+ * registered.
+ */
+ public static void registerHandler(RequestHandler handler,
+ RequestMapper mapper) {
+ String name = handler.getBeanName();
+ if(name != null) {
+ if(name.equals(RequestMapper.GLOBAL_PRE_REQUEST_HANDLER)) {
+
+ mapper.addGlobalPreRequestHandler(handler);
+
+ } else if(name.equals(RequestMapper.GLOBAL_POST_REQUEST_HANDLER)) {
+
+ mapper.addGlobalPostRequestHandler(handler);
+
+ } else {
+ try {
+
+ boolean registered =
+ registerPort(name, handler, mapper) ||
+ registerPortPath(name, handler, mapper) ||
+ registerHostPort(name, handler, mapper) ||
+ registerHostPortPath(name, handler, mapper);
+
+ if(!registered) {
+ LOGGER.error("Unable to register (" + name + ")");
+ }
+ } catch(NumberFormatException e) {
+ LOGGER.error("FAILED parseInt(" + name + ")");
+ }
+ }
+ } else {
+ LOGGER.info("Unable to register RequestHandler - null beanName");
+ }
+ if(handler instanceof ShutdownListener) {
+ ShutdownListener s = (ShutdownListener) handler;
+ mapper.addShutdownListener(s);
+ }
+ }
+}
Property changes on: trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/BeanNameRegistrar.java
___________________________________________________________________
Added: svn:keywords
+ Author Date Revision Id
Added: trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/PortMapper.java
===================================================================
--- trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/PortMapper.java (rev 0)
+++ trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/PortMapper.java 2010-04-27 22:12:31 UTC (rev 3074)
@@ -0,0 +1,164 @@
+/* PortMapper
+ *
+ * $Id$:
+ *
+ * Created on Mar 23, 2010.
+ *
+ * Copyright (C) 2006 Internet Archive.
+ *
+ * This file is part of Wayback.
+ *
+ * Wayback is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * any later version.
+ *
+ * Wayback is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser Public License
+ * along with Wayback; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+package org.archive.wayback.util.webapp;
+
+import java.util.HashMap;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.log4j.Logger;
+
+/**
+ *
+ * Class which allows semi-efficient translation of requests on a specific local
+ * port to a RequestHandler object.
+ *
+ * Mapping within a port is based on the HTTP 1.1 Host field and the first
+ * segment of the requested PATH, that is, whatever is after the context where
+ * the wayback webapp was deployed, and before the first '/'.
+ *
+ * @author brad
+ */
+public class PortMapper {
+ private static final Logger LOGGER = Logger.getLogger(
+ PortMapper.class.getName());
+ private int port = -1;
+ private HashMap<String, RequestHandler> pathMap = null;
+ /**
+ * @param port which this PortMapper is responsible for handling
+ */
+ public PortMapper(int port) {
+ this.port = port;
+ pathMap = new HashMap<String, RequestHandler>();
+ }
+ private String hostPathToKey(String host, String firstPath) {
+ StringBuilder sb = null;
+ if((host == null) && (firstPath == null)) {
+ return null;
+ }
+ sb = new StringBuilder();
+ if(host != null) {
+ sb.append(host);
+ }
+ sb.append("/");
+ if(firstPath != null) {
+ sb.append(firstPath);
+ }
+ return sb.toString();
+ }
+ /**
+ * Register the RequestHandler to accept requests for the given host and
+ * port.
+ * @param host the HTTP 1.1 "Host" header which the RequestHandler should
+ * match. If null, the RequestHandler matches any "Host" header value.
+ * @param firstPath the first path of the GET request path which the
+ * RequestHandler should match. This is the first path AFTER the name the
+ * Wayback webapp is deployed under. If null, the RequestHandler matches
+ * all paths.
+ * @param requestHandler The RequestHandler to register.
+ */
+ public void addRequestHandler(String host, String firstPath,
+ RequestHandler requestHandler) {
+ String key = hostPathToKey(host,firstPath);
+ if(pathMap.containsKey(key)) {
+ LOGGER.warn("Duplicate port:path map for " + port +
+ ":" + key);
+ } else {
+ LOGGER.info("Registered requestHandler(port/host/path) (" +
+ port + "/" + host + "/" + firstPath + "): " + key);
+ pathMap.put(key,requestHandler);
+ }
+ }
+
+ private String requestToFirstPath(HttpServletRequest request) {
+ String firstPath = null;
+ String requestPath = request.getRequestURI();
+ String contextPath = request.getContextPath();
+ if((contextPath.length() > 0) && requestPath.startsWith(contextPath)) {
+ requestPath = requestPath.substring(contextPath.length());
+ }
+ while(requestPath.startsWith("/")) {
+ requestPath = requestPath.substring(1);
+ }
+
+ int slashIdx = requestPath.indexOf("/",1);
+ if(slashIdx == -1) {
+ firstPath = requestPath;
+ } else {
+ firstPath = requestPath.substring(0,slashIdx);
+ }
+ return firstPath;
+ }
+
+ private String requestToHost(HttpServletRequest request) {
+ return request.getServerName();
+ }
+
+ /**
+ * Attempts to locate the most strictly matching RequestHandler mapped to
+ * this port. Strictly matching means the lowest number in the following
+ * list:
+ *
+ * 1) request handler matching both HOST and PATH
+ * 2) request handler matching host, registered with an empty PATH
+ * 3) request handler matching path, registered with an empty HOST
+ * 4) request handler registered with empty HOST and PATH
+ *
+ * @param request the HttpServletRequest to be mapped to a RequestHandler
+ * @return the RequestHandlerContext, containing the RequestHandler and the
+ * prefix of the original request path that indicated the RequestHandler,
+ * or null, if no RequestHandler matches.
+ */
+ public RequestHandlerContext getRequestHandlerContext(
+ HttpServletRequest request) {
+ String host = requestToHost(request);
+ String contextPath = request.getContextPath();
+ StringBuilder pathPrefix = new StringBuilder(contextPath);
+ if(contextPath.length() == 0) {
+ pathPrefix.append("/");
+ }
+ String firstPath = requestToFirstPath(request);
+ RequestHandler handler = pathMap.get(hostPathToKey(host,firstPath));
+ if(handler != null) {
+ return new RequestHandlerContext(handler,
+ pathPrefix.append(firstPath).toString());
+ }
+ handler = pathMap.get(hostPathToKey(host,null));
+ if(handler != null) {
+ return new RequestHandlerContext(handler,contextPath);
+ }
+ handler = pathMap.get(hostPathToKey(null,firstPath));
+ if(handler != null) {
+ return new RequestHandlerContext(handler,
+ pathPrefix.append(firstPath).toString());
+ }
+ handler = pathMap.get(null);
+ if(handler != null) {
+ return new RequestHandlerContext(handler,contextPath);
+ }
+ return null;
+ }
+}
Property changes on: trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/PortMapper.java
___________________________________________________________________
Added: svn:keywords
+ Author Date Revision Id
Added: trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/RequestFilter.java
===================================================================
--- trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/RequestFilter.java (rev 0)
+++ trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/RequestFilter.java 2010-04-27 22:12:31 UTC (rev 3074)
@@ -0,0 +1,91 @@
+/* RequestHandler
+ *
+ * $Id$
+ *
+ * Created on 4:24:06 PM Apr 20, 2007.
+ *
+ * Copyright (C) 2007 Internet Archive.
+ *
+ * This file is part of wayback-webapp.
+ *
+ * wayback-webapp is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * any later version.
+ *
+ * wayback-webapp is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser Public License
+ * along with wayback-webapp; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package org.archive.wayback.util.webapp;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Top-Level integration point between a series of RequestHandler mappings and
+ * a generic ServletContext. This filter is assumed to be responsible for
+ * matching ALL requests received by the webapp ("*") and uses a RequestMapper
+ * to delegate incoming HttpServletRequests to the appropriate RequestHandler,
+ * via the doFilter() method.
+ *
+ * @author brad
+ */
+public class RequestFilter implements Filter {
+ private static final Logger LOGGER = Logger.getLogger(RequestFilter.class
+ .getName());
+ private RequestMapper mapper = null;
+ private final static String CONFIG_PATH = "config-path";
+
+ public void init(FilterConfig config) throws ServletException {
+ ServletContext servletContext = config.getServletContext();
+
+ String configPath = servletContext.getInitParameter(CONFIG_PATH);
+ if(configPath == null) {
+ throw new ServletException("Missing " + CONFIG_PATH
+ + " parameter");
+ }
+ String resolvedPath = servletContext.getRealPath(configPath);
+
+ LOGGER.info("Initializing Spring config at: " + resolvedPath);
+ mapper = SpringReader.readSpringConfig(resolvedPath,servletContext);
+ LOGGER.info("Initialized Spring config at: " + resolvedPath);
+ }
+
+ public void destroy() {
+ LOGGER.info("Shutdown starting.");
+ mapper.shutdown();
+ LOGGER.info("Shutdown complete.");
+ }
+
+ public void doFilter(ServletRequest request, ServletResponse response,
+ FilterChain chain) throws IOException, ServletException {
+ boolean handled = false;
+
+ if(request instanceof HttpServletRequest) {
+ if(response instanceof HttpServletResponse) {
+ handled = mapper.handleRequest((HttpServletRequest) request,
+ (HttpServletResponse) response);
+ }
+ }
+ if(!handled) {
+ chain.doFilter(request,response);
+ }
+ }
+}
Property changes on: trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/RequestFilter.java
___________________________________________________________________
Added: svn:keywords
+ Author Date Revision Id
Added: trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/RequestHandler.java
===================================================================
--- trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/RequestHandler.java (rev 0)
+++ trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/RequestHandler.java 2010-04-27 22:12:31 UTC (rev 3074)
@@ -0,0 +1,85 @@
+/**
+ *
+ */
+package org.archive.wayback.util.webapp;
+
+import java.io.IOException;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.beans.factory.BeanNameAware;
+
+/**
+ * A generic handler of HttpServletRequests. very similar to an HttpServlet, but
+ * the handleRequest() method returns a boolean indicating if the RequestHandler
+ * returned data to the user.
+ *
+ * This interface further defines methods to facilitate automatic registration
+ * when loaded as a Spring configuration, and maintains a reference to the
+ * ServletContext under which it accepts incoming requests.
+ *
+ * @author brad
+ *
+ */
+public interface RequestHandler extends BeanNameAware {
+
+ /**
+ * Possibly handle an incoming HttpServletRequest, much like a normal
+ * HttpServlet, but includes a return value.
+ * @param httpRequest the incoming HttpServletRequest
+ * @param httpResponse the HttpServletResponse to return data to the client.
+ * @return true if the RequestHandler returned a response to the client,
+ * false otherwise
+ * @throws ServletException for usual reasons.
+ * @throws IOException for usual reasons.
+ */
+ public boolean handleRequest(HttpServletRequest httpRequest,
+ HttpServletResponse httpResponse)
+ throws ServletException, IOException;
+
+ /**
+ * @return the "name" property of the bean from the SpringConfiguration
+ */
+ public String getBeanName();
+
+ /**
+ * Called before registerPortListener(), to enable the registration process
+ * and subsequent handleRequest() calls to access the ServletContext, via
+ * the getServletContext() method.
+ * @param servletContext the ServletContext where the RequestHandler is
+ * registered.
+ */
+ public void setServletContext(ServletContext servletContext);
+
+ /**
+ * @return the ServletContext where the RequestHandler is registered.
+ */
+ public ServletContext getServletContext();
+
+ /**
+ * Called at webapp context initialization, to allow the RequestHandler to
+ * register itself with the RequestMapper, which will delegate request
+ * handling to the appropriate RequestHandler.
+ * @param requestMapper the RequestMapper on which this RequestHandler
+ * should register itself, including to register for notification of context
+ * shutdown.
+ */
+ public void registerPortListener(RequestMapper requestMapper);
+
+ /**
+ * @param httpRequest the HttpServletRequest being handled
+ * @return the portion of the original incoming request that falls within
+ * this RequestHandler, not including any query information
+ */
+ public String translateRequestPath(HttpServletRequest httpRequest);
+
+ /**
+ * @param httpRequest the HttpServletRequest being handled
+ * @return the portion of the original incoming request that falls within
+ * this RequestHandler, including any query information
+ */
+ public String translateRequestPathQuery(HttpServletRequest httpRequest);
+}
Property changes on: trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/RequestHandler.java
___________________________________________________________________
Added: svn:keywords
+ Author Date Revision Id
Added: trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/RequestHandlerContext.java
===================================================================
--- trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/RequestHandlerContext.java (rev 0)
+++ trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/RequestHandlerContext.java 2010-04-27 22:12:31 UTC (rev 3074)
@@ -0,0 +1,66 @@
+/* RequestHandlerContext
+ *
+ * $Id$:
+ *
+ * Created on Apr 26, 2010.
+ *
+ * Copyright (C) 2006 Internet Archive.
+ *
+ * This file is part of Wayback.
+ *
+ * Wayback is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * any later version.
+ *
+ * Wayback is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser Public License
+ * along with Wayback; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+package org.archive.wayback.util.webapp;
+
+/**
+ * A simple composition of the RequestHandler which an HttpServletRequest was
+ * mapped to, and the path prefix which indicated the RequestHandler. This
+ * allows computing the portion of the original request path within the
+ * RequestHandler.
+ *
+ * @author brad
+ *
+ */
+public class RequestHandlerContext {
+
+ private RequestHandler handler = null;
+ private String pathPrefix = null;
+
+ /**
+ * Constructor
+ * @param handler the RequestHandler to which the incoming request was
+ * mapped
+ * @param pathPrefix the leading portion of the original request path that
+ * indicated the RequestHandler
+ */
+ public RequestHandlerContext(RequestHandler handler, String pathPrefix) {
+ this.handler = handler;
+ this.pathPrefix = pathPrefix;
+ }
+ /**
+ * @return the RequestHandler to which the incoming request was mapped.
+ */
+ public RequestHandler getRequestHandler() {
+ return handler;
+ }
+ /**
+ * @return the leading portion of the original request path that
+ * indicated the RequestHandler
+ */
+ public String getPathPrefix() {
+ return pathPrefix;
+ }
+}
Property changes on: trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/RequestHandlerContext.java
___________________________________________________________________
Added: svn:keywords
+ Author Date Revision Id
Added: trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/RequestMapper.java
===================================================================
--- trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/RequestMapper.java (rev 0)
+++ trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/RequestMapper.java 2010-04-27 22:12:31 UTC (rev 3074)
@@ -0,0 +1,266 @@
+/* RequestMapper
+ *
+ * $Id$:
+ *
+ * Created on Mar 23, 2010.
+ *
+ * Copyright (C) 2006 Internet Archive.
+ *
+ * This file is part of Wayback.
+ *
+ * Wayback is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * any later version.
+ *
+ * Wayback is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser Public License
+ * along with Wayback; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+package org.archive.wayback.util.webapp;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.log4j.Logger;
+
+/**
+ * This class maintains a mapping of RequestHandlers and ShutDownListeners, to
+ * allow (somewhat) efficient mapping and delegation of incoming requests to
+ * the appropriate RequestHandler.
+ *
+ * This class uses PortMapper to delegate some of the responsibility of mapping
+ * requests received on a particular port, and also allows configuration of a
+ * global PRE RequestHandler, which gets first dibs on EVERY incoming request,
+ * as well as a global POST RequestHandler, which may attempt to handle any
+ * incoming request not handled by the normal RequestHandler mapping.
+ *
+ * @author brad
+ *
+ */
+public class RequestMapper {
+
+ private static final Logger LOGGER = Logger.getLogger(
+ RequestMapper.class.getName());
+
+ private ArrayList<ShutdownListener> shutdownListeners = null;
+
+ private HashMap<Integer,PortMapper> portMap = null;
+ private RequestHandler globalPreRequestHandler = null;
+ private RequestHandler globalPostRequestHandler = null;
+
+ private final static String REQUEST_CONTEXT_PREFIX =
+ "webapp-request-context-path-prefix";
+
+ /**
+ * Bean name used to register the special global PRE RequestHandler.
+ */
+ public final static String GLOBAL_PRE_REQUEST_HANDLER = "-";
+ /**
+ * Bean name used to register the special global POST RequestHandler.
+ */
+ public final static String GLOBAL_POST_REQUEST_HANDLER = "+";
+
+ /**
+ * Construct a RequestMapper, for the given RequestHandler objects, on the
+ * specified ServletContext. This method will call setServletContext() on
+ * each RequestMapper, followed immediately by registerPortListener()
+ *
+ * @param requestHandlers Collection of RequestHandlers which handle
+ * requests
+ * @param servletContext the webapp ServletContext where this RequestMapper
+ * is configured.
+ */
+ public RequestMapper(Collection<RequestHandler> requestHandlers,
+ ServletContext servletContext) {
+ portMap = new HashMap<Integer, PortMapper>();
+ shutdownListeners = new ArrayList<ShutdownListener>();
+ Iterator<RequestHandler> itr = requestHandlers.iterator();
+ LOGGER.info("Registering handlers.");
+ while(itr.hasNext()) {
+ RequestHandler requestHandler = itr.next();
+ requestHandler.setServletContext(servletContext);
+ requestHandler.registerPortListener(this);
+ }
+ LOGGER.info("Registering handlers complete.");
+ }
+
+ /**
+ * Request the shutdownListener object to be notified of ServletContext
+ * shutdown.
+ * @param shutdownListener the object which needs to have shutdown() called
+ * when the ServletContext is destroyed.
+ */
+ public void addShutdownListener(ShutdownListener shutdownListener) {
+ shutdownListeners.add(shutdownListener);
+ }
+ /**
+ * Configure the specified RequestHandler to handle ALL incoming requests
+ * before any other normal mapping.
+ * @param requestHandler the global PRE RequestHandler
+ */
+ public void addGlobalPreRequestHandler(RequestHandler requestHandler) {
+ globalPreRequestHandler = requestHandler;
+ }
+ /**
+ * Configure the specified RequestHandler to handle ALL incoming requests
+ * after all other normal mapping has been attempted
+ * @param requestHandler the global POST RequestHandler
+ */
+ public void addGlobalPostRequestHandler(RequestHandler requestHandler) {
+ globalPostRequestHandler = requestHandler;
+ }
+ /**
+ * Register the RequestHandler to accept requests on the given port, for the
+ * specified host and path.
+ * @param port the integer port on which the RequestHandler gets requests.
+ * @param host the String Host which the RequestHandler matches, or null, if
+ * the RequestHandler should match ALL hosts.
+ * @param path the String path which the RequestHandler matches, or null, if
+ * the RequestHandler should match ALL paths.
+ * @param requestHandler the RequestHandler to register.
+ */
+ public void addRequestHandler(int port, String host, String path,
+ RequestHandler requestHandler) {
+ LOGGER.info("Registered:" + port + ":" +
+ (host == null ? "(null)" : host) + ":"
+ + (path == null ? "(null)" : path));
+ Integer portInt = Integer.valueOf(port);
+ PortMapper portMapper = portMap.get(portInt);
+ if(portMapper == null) {
+ portMapper = new PortMapper(portInt);
+ portMap.put(portInt, portMapper);
+ }
+ portMapper.addRequestHandler(host, path, requestHandler);
+ }
+
+ private RequestHandlerContext mapRequest(HttpServletRequest request) {
+ RequestHandlerContext handlerContext = null;
+
+ int port = request.getLocalPort();
+ Integer portInt = Integer.valueOf(port);
+ PortMapper portMapper = portMap.get(portInt);
+ if(portMapper != null) {
+ handlerContext = portMapper.getRequestHandlerContext(request);
+ }
+ return handlerContext;
+ }
+
+ /**
+ * Map the incoming request to the appropriate RequestHandler, including
+ * the PRE and POST RequestHandlers, if configured.
+ * @param request the incoming HttpServletRequest
+ * @param response the HttpServletResponse to return data to the client
+ * @return true if a response was returned to the client.
+ * @throws ServletException for usual reasons.
+ * @throws IOException for usual reasons.
+ */
+ public boolean handleRequest(HttpServletRequest request,
+ HttpServletResponse response) throws IOException, ServletException {
+ boolean handled = false;
+ if(globalPreRequestHandler != null) {
+ handled =
+ globalPreRequestHandler.handleRequest(request, response);
+ }
+ if(handled == false) {
+ RequestHandlerContext handlerContext = mapRequest(request);
+ if(handlerContext != null) {
+ RequestHandler requestHandler =
+ handlerContext.getRequestHandler();
+ request.setAttribute(REQUEST_CONTEXT_PREFIX,
+ handlerContext.getPathPrefix() + "/");
+ handled = requestHandler.handleRequest(request, response);
+ }
+ }
+ if(handled == false) {
+ if(globalPostRequestHandler != null) {
+ handled =
+ globalPostRequestHandler.handleRequest(request, response);
+ }
+ }
+ return handled;
+ }
+
+ /**
+ * notify all registered ShutdownListener objects that the ServletContext is
+ * being destroyed.
+ */
+ public void shutdown() {
+ for(ShutdownListener shutdownListener : shutdownListeners) {
+ try {
+ shutdownListener.shutdown();
+ } catch(Exception e) {
+ LOGGER.error("failed shutdown", e);
+ }
+ }
+ }
+
+ /**
+ * Extract the request path prefix, as computed at RequestHandler mapping,
+ * from the HttpServletRequest object.
+ *
+ * @param request HttpServlet request object being handled
+ * @return the portion of the original request path which indicated the
+ * RequestHandler, including the trailing '/'.
+ */
+ public static String getRequestPathPrefix(HttpServletRequest request) {
+ return (String) request.getAttribute(REQUEST_CONTEXT_PREFIX);
+ }
+
+ /**
+ * @param request HttpServlet request object being handled
+ * @return the portion of the incoming path within the RequestHandler
+ * handling the request, not including a leading "/", and not including
+ * query arguments.
+ */
+ public static String getRequestContextPath(HttpServletRequest request) {
+ String prefix = (String) request.getAttribute(REQUEST_CONTEXT_PREFIX);
+ String requestUrl = request.getRequestURI();
+ if(prefix == null) {
+ return requestUrl;
+ }
+ if(requestUrl.startsWith(prefix)) {
+ return requestUrl.substring(prefix.length());
+ }
+ return requestUrl;
+ }
+
+ /**
+ * @param request HttpServlet request object being handled
+ * @return the portion of the incoming path within the RequestHandler
+ * handling the request, not including a leading "/", including query
+ * arguments.
+ */
+ public static String getRequestContextPathQuery(HttpServletRequest request) {
+ String prefix = (String) request.getAttribute(REQUEST_CONTEXT_PREFIX);
+ StringBuilder sb = new StringBuilder(request.getRequestURI());
+ String requestUrl = null;
+ String query = request.getQueryString();
+ if(query != null) {
+ requestUrl = sb.append("?").append(query).toString();
+ } else {
+ requestUrl = sb.toString();
+ }
+ if(prefix == null) {
+ return requestUrl;
+ }
+ if(requestUrl.startsWith(prefix)) {
+ return requestUrl.substring(prefix.length());
+ }
+ return requestUrl;
+ }
+}
Property changes on: trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/RequestMapper.java
___________________________________________________________________
Added: svn:keywords
+ Author Date Revision Id
Added: trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/ShutdownListener.java
===================================================================
--- trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/ShutdownListener.java (rev 0)
+++ trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/ShutdownListener.java 2010-04-27 22:12:31 UTC (rev 3074)
@@ -0,0 +1,18 @@
+/**
+ *
+ */
+package org.archive.wayback.util.webapp;
+
+/**
+ * Interface representing a desire to be notified when the containing
+ * ServletContext is being destroyed.
+ *
+ * @author brad
+ *
+ */
+public interface ShutdownListener {
+ /**
+ * Called when the ServletContext is being destroyed.
+ */
+ public void shutdown();
+}
Property changes on: trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/ShutdownListener.java
___________________________________________________________________
Added: svn:keywords
+ Author Date Revision Id
Added: trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/SpringReader.java
===================================================================
--- trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/SpringReader.java (rev 0)
+++ trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/SpringReader.java 2010-04-27 22:12:31 UTC (rev 3074)
@@ -0,0 +1,61 @@
+/**
+ *
+ */
+package org.archive.wayback.util.webapp;
+import java.util.Collection;
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+
+import org.apache.log4j.Logger;
+import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
+import org.springframework.beans.factory.xml.XmlBeanFactory;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.core.io.Resource;
+
+/**
+ * Single static method to read a Spring XML configuration, extract
+ * RequestHandlers, and return a RequestMapper which delegates requests to
+ * those RequestHandlers.
+ *
+ * @author brad
+ *
+ */
+public class SpringReader {
+ private static final Logger LOGGER = Logger.getLogger(
+ SpringReader.class.getName());
+
+ /**
+ * Read the single Spring XML configuration file located at the specified
+ * path, performing PropertyPlaceHolder interpolation, extracting all beans
+ * which implement the RequestHandler interface, and construct a
+ * RequestMapper for those RequestHandlers, on the specified ServletContext.
+ * @param configPath the path to the Spring XML file containing the
+ * configuration.
+ * @param servletContext the ServletContext where the RequestHandlers should
+ * be mapped
+ * @return a new ReqeustMapper which delegates requests for the
+ * ServletContext
+ */
+ @SuppressWarnings("unchecked")
+ public static RequestMapper readSpringConfig(String configPath,
+ ServletContext servletContext) {
+ LOGGER.info("Loading from config file " + configPath);
+
+ Resource resource = new FileSystemResource(configPath);
+ XmlBeanFactory factory = new XmlBeanFactory(resource);
+ Map map = factory.getBeansOfType(PropertyPlaceholderConfigurer.class);
+ if(map != null) {
+ Collection<PropertyPlaceholderConfigurer> macros = map.values();
+ for(PropertyPlaceholderConfigurer macro : macros) {
+ macro.postProcessBeanFactory(factory);
+ }
+ }
+ LOGGER.info("Pre-instanting Singletons starting");
+ factory.preInstantiateSingletons();
+ LOGGER.info("Pre-instanting Singletons complete");
+ Map<String,RequestHandler> beans =
+ factory.getBeansOfType(RequestHandler.class,false,false);
+ return new RequestMapper(beans.values(), servletContext);
+ }
+}
Property changes on: trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/SpringReader.java
___________________________________________________________________
Added: svn:keywords
+ Author Date Revision Id
Added: trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/StaticFileRequestHandler.java
===================================================================
--- trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/StaticFileRequestHandler.java (rev 0)
+++ trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/StaticFileRequestHandler.java 2010-04-27 22:12:31 UTC (rev 3074)
@@ -0,0 +1,49 @@
+/**
+ *
+ */
+package org.archive.wayback.util.webapp;
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.log4j.Logger;
+
+/**
+ * RequestHandler implementation which allows serving of static files, and
+ * .jsp files within a ServletContext.
+ *
+ * @author brad
+ */
+public class StaticFileRequestHandler extends AbstractRequestHandler {
+
+ private static final Logger LOGGER = Logger.getLogger(
+ StaticFileRequestHandler.class.getName());
+
+ public boolean handleRequest(HttpServletRequest httpRequest,
+ HttpServletResponse httpResponse) throws ServletException, IOException {
+ boolean handled = false;
+ String contextRelativePath = httpRequest.getServletPath();
+ String absPath = getServletContext().getRealPath(contextRelativePath);
+ File test = new File(absPath);
+ // TODO: check for index.jsp(or configurable equivalent),
+ // if it's a directory?
+ if(test.isFile()) {
+ LOGGER.trace("static path:" + absPath);
+ RequestDispatcher dispatcher =
+ httpRequest.getRequestDispatcher(contextRelativePath);
+// try {
+ dispatcher.forward(httpRequest, httpResponse);
+ handled = true;
+// } catch(Exception e) {
+// }
+ } else {
+ LOGGER.trace("Not-static path:" + absPath);
+ }
+ return handled;
+ }
+}
Property changes on: trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/util/webapp/StaticFileRequestHandler.java
___________________________________________________________________
Added: svn:keywords
+ Author Date Revision Id
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|