/* vim: set ts=2 et sw=2 cindent fo=qroca: */ package com.globant.katari.core.web; import com.opensymphony.module.sitemesh.Page; import com.opensymphony.module.sitemesh.parser.HTMLPageParser; import org.apache.commons.lang.Validate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import javax.servlet.RequestDispatcher; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; /** Renders a weblet and makes the result available as a string. * * A weblet is a fragemnt of html that modules provide to include in some part * of a page, generally in the decorating border. * * This class asks the container to include the request, intercepts the output * and gets the body of the response. It only supports html output. * * @author juan.pereyra@globant.com */ public class WebletRenderer { /** The class logger. */ private static Logger log = LoggerFactory.getLogger(WebletRenderer.class); /** The servlet context. * * It is never null. */ private final ServletContext servletContext; /** Standard constructor. * * @param theServletContext The servlet context. It cannot be null. */ public WebletRenderer(final ServletContext theServletContext) { Validate.notNull(theServletContext, "The servlet context cannot be null."); servletContext = theServletContext; } /** A wrapper for the servlet request that changes the context path presented * to the module so that the module 'thinks' it is mapped directly in web xml * configuration file. */ private static class ModuleContextRequestWrapper extends HttpServletRequestWrapper { /** The name of the module. * * It is never null. */ private String moduleName; /** Creates a ModuleContextRequestWrapper. * * @param request The servlet request. It cannot be null. * * @param theModuleName The name of the module. It cannot be null. */ public ModuleContextRequestWrapper(final HttpServletRequest request, final String theModuleName) { super(request); Validate.notNull(request, "The request cannot be null"); Validate.notNull(theModuleName, "The module name cannot be null"); moduleName = theModuleName; } /** The context path that must be presented to the weblet. * * It corresponds to the module path. * * @return a string with the context path, never null. */ @Override public String getContextPath() { String contextPath = super.getContextPath(); if (contextPath.equals("/")) { contextPath = ""; } return contextPath + "/module/" + moduleName; } /** The http method. * * @return the string "GET". */ @Override public java.lang.String getMethod() { return "GET"; } } /** Renders the weblet to a string. * * @param moduleName The name of the module containing the weblet. It cannot * be null. * * @param webletName The name of the weblet. It cannot be null. * * @param instance An optional weblet instance data. The weblet uses the * instance as it pleases, for example, to draw different things according to * the instance name. It is ignored if it is null or the empty string. * * @param request The servlet request. It cannot be null. * * @param response The servlet response. It cannot be null. * * @return a string with content of the body tag extracted from the generated * weblet output. * * @throws IOException in case of an io error. * * @throws ServletException in case of an unexpected error. */ public String renderWebletResponse(final String moduleName, final String webletName, final Object instance, final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { Validate.notNull(moduleName, "The module name cannot be null"); Validate.notNull(webletName, "The weblet name cannot be null"); Validate.notNull(request, "The request cannot be null"); Validate.notNull(response, "The response cannot be null"); if (log.isTraceEnabled()) { log.trace("Entering renderWebletResponse('" + moduleName + "', '" + webletName + "', '" + instance + "', ...)"); } WebletResponseWrapper wrappedResponse; wrappedResponse = new WebletResponseWrapper(response); String path = "/module/" + moduleName + "/weblet/" + webletName + ".do"; // We store the instance value in the request as an attribute. We save the // old value of the instance, just in case. Object oldInstance = null; if (instance != null && !"".equals(instance)) { oldInstance = request.getAttribute("instance"); request.setAttribute("instance", instance); } RequestDispatcher requestDispatcher; requestDispatcher = servletContext.getRequestDispatcher(path); Object springMacroRequestContext = request.getAttribute("springMacroRequestContext"); request.removeAttribute("springMacroRequestContext"); requestDispatcher.include(new ModuleContextRequestWrapper(request, moduleName), wrappedResponse); if (springMacroRequestContext != null) { request.setAttribute("springMacroRequestContext", springMacroRequestContext); } String htmlPage = wrappedResponse.getResponseAsString(); if (oldInstance != null) { request.setAttribute("instance", oldInstance); } String body = extractBody(htmlPage); if (log.isTraceEnabled()) { log.trace("Leaving renderWebletResponse with '" + body + "'"); } return body; } /** Extracts the body from an html string. * * @param content the html content. It cannot be null. * * @return the content between body tags. * * @throws IOException in case of an io error. */ private String extractBody(final String content) throws IOException { Validate.notNull(content, "The content cannot be null"); HTMLPageParser parser = new HTMLPageParser(); Page page = parser.parse(content.toCharArray()); return page.getBody(); } }