/* vim: set ts=2 et sw=2 cindent fo=qroca: */
package com.globant.katari.core.web;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.Validate;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.context.support.XmlWebApplicationContext;
/** Http servlet that acts as a bridge between Katari and other web frameworks.
*
* Basically, it loads a local application context (like DispatcherServlet
* does) and makes it available for the underlying servlet. All requests are
* delegated to an {@link Servlet} instance.
*
* @author pablo.saavedra
*/
public class KatariWrapperServlet extends HttpServlet {
/**
* Serial version.
*/
private static final long serialVersionUID = 1L;
/**
* The {@link FacesServlet} to delegate to.
*/
private Servlet delegate;
/**
* A local web application context.
*/
private XmlWebApplicationContext appContext;
/**
* The context parameters to override in the servlet context.
*/
private Map<String, String> initParameterOverrides = new HashMap
<String, String>();
/** Creates a new {@link KatariWrapperServlet} wrapping the given
* FacesServlet.
*
* @param theDelegate The faces servlet to wrap, cannot be null.
*/
public KatariWrapperServlet(final Servlet theDelegate) {
Validate.notNull(theDelegate, "The faces servlet cannot be null");
this.delegate = theDelegate;
}
/**
* Services the given request and renders the response.
* <p>
* Calls to this method are simply passed to the inner delegate. However,
* subclasses get a chance to modify the request and response overriding the
* {@link #wrapRequest(HttpServletRequest)} and
* {@link #wrapResponse(HttpServletResponse)} methods.
* @param req
* The HTTP servlet request.
* @param resp
* The HTTP servlet response.
* @throws ServletException
* as part of the servlet contract.
* @throws IOException
* as part of the servlet contract.
* @see #wrapRequest(HttpServletRequest)
* @see #wrapResponse(HttpServletResponse)
* @see #service(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
*/
@Override
protected void service(final HttpServletRequest req,
final HttpServletResponse resp) throws ServletException, IOException {
HttpServletRequest wrappedRequest = wrapRequest(req);
HttpServletResponse wrappedResponse = wrapResponse(resp);
delegate.service(wrappedRequest, wrappedResponse);
}
/** Gives subclasses a chance to wrap the original {@link
* HttpServletResponse}.
*
* This method must never return null.
*
* @param resp
* The response to wrap,.
* @return The wrapped response, or the original one if no wrapping is
* needed, never null.
*/
protected HttpServletResponse wrapResponse(final HttpServletResponse resp) {
return resp;
}
/**
* Gives subclasses a chance to wrap the original {@link HttpServletRequest}.
* This method must never return null.
* @param req
* The request to wrap,.
* @return The wrapped request, or the original one if no wrapping is needed,
* never null.
*/
protected HttpServletRequest wrapRequest(final HttpServletRequest req) {
return req;
}
/**
* Destroys the servlet.
* <p>
* This class does the following, in order:
* <ol>
* <li>Destroys the delegate servlet</li>
* <li>Destroys the local application context</li>
* <li>Calls super.destroy()</li>
* </ol>
*/
@Override
public void destroy() {
delegate.destroy();
appContext.destroy();
super.destroy();
}
/**
* Initializes the servlet.
* <p>
* The initialization process consists of creating a
* {@link WebApplicationContext} by loading the xml configuration file
* located at the path indicated by the <code>contextConfigLocation</code>
* initialization parameter.
* <p>
* The application context will be stored in a {@link ScopedServletContext},
* under {@link WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE}
* key, and all the configured extra init parameters will be loaded into the
* scoped servlet context too.
* <p>
* The new servlet context will be passed into the delegate servlet's init
* method to complete initialization.
* @param config
* The original servlet config, cannot be null.
* @throws ServletException
* in case of an initialization error.
* @see ScopedServletContext
*/
@Override
public void init(final ServletConfig config) throws ServletException {
super.init(config);
String location = config.getInitParameter("contextConfigLocation");
ServletContext ctx = config.getServletContext();
appContext = new XmlWebApplicationContext();
appContext.setConfigLocation(location);
appContext.setServletContext(ctx);
appContext.setParent(WebApplicationContextUtils
.getRequiredWebApplicationContext(ctx));
appContext.refresh();
ScopedServletContext context = new ScopedServletContext(ctx);
context.setAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
appContext);
addInitParameters(context);
ServletConfig configWrapper = new ServletConfigWrapper(config, context);
delegate.init(configWrapper);
}
/**
* Adds the configured init parameters to the given servlet context.
* @param context
* The servlet context to add the parameters to, cannot be null.
* @see #setInitParameterOverrides(Map)
*/
protected void addInitParameters(final ScopedServletContext context) {
for (Entry<String, String> param : initParameterOverrides.entrySet()) {
context.addInitParameter(param.getKey(), param.getValue());
}
}
/**
* Sets the init parameters to override in the servlet context.
* @param overrides
* The map of parameters to add, cannot be null.
*/
public void setInitParameterOverrides(final Map<String, String> overrides) {
Validate.notNull(overrides);
this.initParameterOverrides = overrides;
}
}