package org.rendersnake.ext.guice;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
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.HtmlCanvas;
import org.rendersnake.PageContext;
import org.rendersnake.Renderable;
import org.rendersnake.ext.servlet.PostHandler;
import org.rendersnake.ext.servlet.RequestHeadersMap;
import org.rendersnake.ext.servlet.RequestParametersMap;
import org.rendersnake.ext.servlet.SessionAttributesMap;
import org.rendersnake.ext.spring.HtmlCanvasFactory;
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,PostHandler> handlerMap = new HashMap<String,PostHandler>();
/**
* Dispatch an incoming request to a Renderable component
* by looking for a named component that matches the path.
*/
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String uri = request.getPathInfo();
Renderable component = this.dispatchMap.get(uri);
if (component == null) {
response.sendError(404);
} else {
HtmlCanvas canvas = HtmlCanvasFactory.createCanvas(request,response,response.getWriter());
PageContext context = canvas.getPageContext();
String q = request.getQueryString();
context.withObject(PageContext.REQUEST_PATH, request.getPathTranslated());
context.withObject(PageContext.REQUEST_URIQ, request.getRequestURI() + (q == null ? "" : "?" + q));
context.withObject(PageContext.SESSION, new SessionAttributesMap(request));
context.withObject(PageContext.REQUEST_PARAMETERS, new RequestParametersMap(request));
context.withObject(PageContext.REQUEST_HEADERS, new RequestHeadersMap(request));
canvas.render(component);
}
}
/**
* Dispatch an incoming request to a PostHandler component
* by looking for a named component that matches the path.
*/
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String uri = req.getPathInfo();
PostHandler handler = this.handlerMap.get(uri);
if (handler == null) {
resp.sendError(404);
} else {
handler.handlePost(req, resp);
}
}
/**
* 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 (PostHandler.class.isAssignableFrom(klass)) {
PostHandler h = (PostHandler)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);
}
}