package org.rendersnake.ext.guice;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Logger;
import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.rendersnake.PageContext;
import org.rendersnake.Renderable;
import org.rendersnake.internal.HtmlServletCanvas;
import org.rendersnake.servlet.FormHandler;
import org.rendersnake.servlet.FormResponseAction;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Singleton;
/**
* GuiceComponentServlet is used to serve RenderSnake components.
* Each such component must be annotation with @Named in order to pickup here.
*
* Usage
* <pre>
new ServletModule(){
protected void configureServlets() {
serve("/web/*").with(GuiceComponentServlet.class);
// list all page classes here
bind(HomePage.class);
}
}
* </pre>
*
*
* @author emicklei
*/
@Singleton
public class GuiceComponentServlet extends HttpServlet {
private static final long serialVersionUID = -5260542693799682553L;
private static final Logger LOG = Logger.getLogger("org.rendersnake.ext.guice");
/**
* Injector needed for initialization of the mapping.
*/
@Inject
private Injector injector;
/**
* Cached map of uri paths to Renderable components.
*/
private Map<String,Renderable> dispatchMap = new HashMap<String,Renderable>();
/**
* Cached map of uri paths to FormHandler components.
*/
private Map<String,FormHandler> handlerMap = new HashMap<String,FormHandler>();
/**
* Dispatch an incoming request to a Renderable component
* by looking for a named component that matches the path.
*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String uri = "";
String info = req.getPathInfo();
if (null != info && info.length() > 0) {
uri = info.substring(1);
}
Renderable component = this.dispatchMap.get(uri);
if (component == null) {
resp.sendError(404);
} else {
HtmlServletCanvas canvas = new HtmlServletCanvas(req, resp, resp.getWriter());
this.copyParametersIntoPageContext(req, canvas);
canvas.render(component);
}
}
private void copyParametersIntoPageContext(HttpServletRequest req, HtmlServletCanvas canvas) {
// copy query parameters into pageContext
Enumeration<String> names = req.getParameterNames();
PageContext ctx = canvas.getPageContext();
while (names.hasMoreElements()) {
String key = names.nextElement();
Object value = req.getParameter(key);
ctx.set(key,value);
}
}
/**
* Dispatch an incoming request to a Renderable component
* by looking for a named component that matches the path.
*/
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String uri = "";
String info = req.getPathInfo();
if (null != info && info.length() > 0) {
uri = info.substring(1);
}
FormHandler handler = this.handlerMap.get(uri);
if (handler == null) {
resp.sendError(404);
} else {
FormResponseAction action = handler.handle(req, resp);
if (action.redirectToUrl != null) {
resp.sendRedirect(action.redirectToUrl);
return;
} else if (action.component != null){
HtmlServletCanvas html = new HtmlServletCanvas(req, resp, resp.getWriter());
this.copyParametersIntoPageContext(req, html);
action.component.renderOn(html);
} else {
resp.setStatus(200);
}
}
}
/**
* Initializes the mapping between path info and Renderable components.
* It is implemented by inspecting all bindings of the injector.
*/
@Override
public void init(ServletConfig config) throws ServletException {
for (Key<?> key : injector.getBindings().keySet()) {
Type type = key.getTypeLiteral().getType();
if (type instanceof Class<?>) {
Class<?> klass = (Class<?>) type;
if (Renderable.class.isAssignableFrom(klass)) {
Renderable p = (Renderable)injector.getInstance(klass);
Named annotation = klass.getAnnotation(Named.class);
if (annotation != null) {
dispatchMap.put(annotation.value(), p);
LOG.info("Binding component:"+klass+" to:" + annotation);
} else {
LOG.severe("Missing @Named annotation in component:"+klass);
}
}
if (FormHandler.class.isAssignableFrom(klass)) {
FormHandler h = (FormHandler)injector.getInstance(klass);
Named annotation = klass.getAnnotation(Named.class);
if (annotation != null) {
handlerMap.put(annotation.value(), h);
LOG.info("Binding component:"+klass+" to:" + annotation);
} else {
LOG.severe("Missing @Named annotation in component:"+klass);
}
}
}
}
super.init(config);
}
}