/*
* Copyright(c) 2005 Center for E-Commerce Infrastructure Development, The
* University of Hong Kong (HKU). All Rights Reserved.
*
* This software is licensed under the GNU GENERAL PUBLIC LICENSE Version 2.0 [1]
*
* [1] http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
*/
package hk.hku.cecid.piazza.commons.servlet.http;
import hk.hku.cecid.piazza.commons.Sys;
import hk.hku.cecid.piazza.commons.servlet.RequestListenerException;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.Date;
import java.util.Iterator;
import java.util.Properties;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* HttpDispatcher is an HttpServlet which dispatches Http request to the Http
* request listeners registered in its Http dispatcher context.
*
* @see HttpDispatcherContext
*
* @author Hugo Y. K. Lam
*
*/
public class HttpDispatcher extends HttpServlet {
private ServletConfig servletConfig;
private HttpDispatcherContext dispatcherContext;
/**
* The request attribute key of the servlet config.
*/
public static final String CONFIG_KEY = "SERVLET_CONFIG";
/**
* Initializes the servlet.
*
* @param config the ServletConfig.
* @throws ServletException if error occurred in initialization.
*/
public void init(ServletConfig config) throws ServletException {
super.init(config);
servletConfig = config;
String contextId = servletConfig.getInitParameter("context-id");
dispatcherContext = HttpDispatcherContext.getContext(contextId);
if (dispatcherContext == null) {
dispatcherContext = HttpDispatcherContext.getDefaultContext();
}
Sys.main.log.info(servletConfig.getServletName()
+ " initialized successfully");
}
/**
* Destroys the servlet.
*/
public void destroy() {
super.destroy();
dispatcherContext.unregisterAll();
Sys.main.log.info(servletConfig.getServletName()
+ " destroyed successfully");
}
/**
* Processes requests for both HTTP <code>GET</code> and <code>POST</code>
* methods.
*
* @param request servlet request.
* @param response servlet response.
*/
protected void processRequest(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
dispatcherContext.acquire();
try {
/*
* Generate an HTTP request event and look for its listener
*/
String pathInfo = dispatcherContext.getPathInfo(request);
HttpRequestEvent event = new HttpRequestEvent(pathInfo, request, response);
HttpRequestListener listener = dispatcherContext.getListener(pathInfo);
/*
* Send an HTTP NOT FOUND error if no listener found for the event.
*/
if (listener == null) {
Sys.main.log.debug(event + " has no listener.");
response.sendError(HttpURLConnection.HTTP_NOT_FOUND, pathInfo);
return;
}
/*
* Process the event if there is a corresponding listener.
*/
else {
String path = null;
try {
setDefaults(request, response);
/*
* Fire a request accepted event and invokes the listener to
* process it if it should be accepted.
*/
if (fireRequestAcceptedEvent(event)) {
Sys.main.log.debug(event + " is being processed by "
+ listener);
/*
* Process the request and time it.
*/
long start = new Date().getTime();
if (listener.doStartRequest(request, response)) {
path = listener.processRequest(request, response);
listener.doEndRequest(request, response);
}
long end = new Date().getTime();
Sys.main.log.debug(event
+ " had been processed successfully for "
+ (end - start) + " ms");
fireRequestProcessedEvent(event);
}
/*
* Send an HTTP BAD REQUEST error if the request has been
* rejected.
*/
else {
Sys.main.log.debug(event + " is rejected.");
if (!response.isCommitted()) {
response.sendError(
HttpURLConnection.HTTP_BAD_REQUEST,
pathInfo);
}
return;
}
}
/*
* Send an HTTP INTERNAL ERROR error if there is any unhandled
* or unexpected server error.
*/
catch (Throwable e) {
Sys.main.log.error("Error in processing HTTP request", e);
String errMsg = (e instanceof RequestListenerException ? e
.getMessage() : e.getClass().getName() + ": "
+ e.getMessage());
response.sendError(HttpURLConnection.HTTP_INTERNAL_ERROR,
errMsg);
return;
}
/*
* Forward the request to the path, if any, returned by the
* listener after processing the request.
*/
if (path != null) {
if (path.startsWith("&")) {
String returnCode = path.substring(1);
Properties params = listener.getParameters();
path = params == null? null : params.getProperty(
"return-code:" + returnCode);
if (path == null) {
return;
}
}
Sys.main.log.debug(event + " is being forwarded to '" + path + "'");
RequestDispatcher dispatcher = getServletContext()
.getRequestDispatcher(path);
dispatcher.forward(request, response);
}
}
}
finally {
dispatcherContext.release();
}
}
/**
* Sets the defaults for this HttpDispatcher. The servlet config will be
* stored as a request attribute and the encodings of the request and
* response will be set to what defined in the dispatcher context.
*
* @param request the servlet request.
* @param response the servlet response.
*/
private void setDefaults(HttpServletRequest request,
HttpServletResponse response) {
try {
request.setAttribute(CONFIG_KEY, servletConfig);
if (dispatcherContext.getResponseEncoding() != null) {
String enc = dispatcherContext.getResponseEncoding();
if (enc.indexOf(';')!=-1) {
response.setContentType(enc);
}
else {
response.setCharacterEncoding(enc);
}
}
if (dispatcherContext.getRequestEncoding() != null) {
request.setCharacterEncoding(dispatcherContext.getRequestEncoding());
}
}
catch (Exception e) {
Sys.main.log.error("Unable to set servlet default encoding", e);
}
}
/**
* Invoked before a request is processed by the listener registered at the
* request context path which is relative to this servlet's context path.
*
* @param event the HTTP request event.
* @return true if the request should be accepted and processed by the
* registered listener.
*/
protected boolean fireRequestAcceptedEvent(HttpRequestEvent event) {
Iterator filters = dispatcherContext.getRequestFilters().iterator();
while (filters.hasNext()) {
HttpRequestFilter filter = (HttpRequestFilter) filters.next();
if (!filter.requestAccepted(event)) {
return false;
}
}
return true;
}
/**
* Invoked when a request has been processed by the listener registered at
* the request context path which is relative to this servlet's context
* path.
*
* @param event the HTTP request event.
*/
protected void fireRequestProcessedEvent(HttpRequestEvent event) {
Iterator filters = dispatcherContext.getRequestFilters().iterator();
while (filters.hasNext()) {
HttpRequestFilter filter = (HttpRequestFilter) filters.next();
filter.requestProcessed(event);
}
}
/**
* Handles the HTTP <code>GET</code> method.
*
* @param request the servlet request.
* @param response the servlet response.
*/
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
processRequest(request, response);
}
/**
* Handles the HTTP <code>POST</code> method.
*
* @param request the servlet request.
* @param response the servlet response.
*/
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
processRequest(request, response);
}
/**
* Gets a short description of this servlet.
*
* @return a short description of this servlet.
*/
public String getServletInfo() {
return "An HTTP Request Dispatcher";
}
}