Webwork2のVelocity
以前Webwork2で、VelocityToolが使えないと書きました。
今以前の日記を読み返したら理由を書いてなかった・・・
WebworkでVelocityToolServletを使用することは可能なのですが、
その場合、$stack、$webwork、$ognlが使えません。
VelocityToolが使えてWebworkの変数も使えるようにはできないのか?
そこで、いろいろサイトを巡り参考にして作ってみました。
一部id:khiさんのWikiを参考にさせていただきました。感謝です。
以下にソースをはっておきます。字が小さくて申し訳ないです。
不具合等あれば教えて下さい。
まず、WebWorkVelocityViewServlet
これはWebworkのJIRAにあったものをそのまま頂いて、少し手を加えました。
web.xmlにはVelocityViewServletと同じように、
velocity.propertiesとtoolbox.xmlをパラメータとして渡してあげてください。ここで定義したToolは後で出てくるResultでも使えます。
このまま使うと勝手にHTMLエスケープされて表示されます。
そこでエスケープしてはいけないところをUnescapeToolで対応します。
package hogehoge.velocity; import java.io.FileNotFoundException; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.io.Writer; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.jsp.JspFactory; import javax.servlet.jsp.PageContext; import org.apache.commons.collections.ExtendedProperties; import org.apache.commons.lang.StringEscapeUtils; import org.apache.velocity.Template; import org.apache.velocity.app.event.EventCartridge; import org.apache.velocity.app.event.ReferenceInsertionEventHandler; import org.apache.velocity.context.Context; import org.apache.velocity.exception.MethodInvocationException; import org.apache.velocity.exception.ParseErrorException; import org.apache.velocity.exception.ResourceNotFoundException; import org.apache.velocity.runtime.RuntimeSingleton; import org.apache.velocity.servlet.VelocityServlet; import org.apache.velocity.tools.view.context.ChainedContext; import org.apache.velocity.tools.view.servlet.VelocityViewServlet; import com.opensymphony.webwork.ServletActionContext; import com.opensymphony.webwork.config.Configuration; import com.opensymphony.webwork.views.velocity.VelocityManager; import com.opensymphony.xwork.ActionContext; /** * @created 24-Sep-2004 * @author Nick Curry */ public class WebWorkVelocityViewServlet extends VelocityViewServlet { //~ Instance fields // //////////////////////////////////////////////////////// private VelocityManager velocityManager; protected EventCartridge eventCartrigde; //~ Constructors // /////////////////////////////////////////////////////////// public WebWorkVelocityViewServlet() { velocityManager = VelocityManager.getInstance(); } //~ Methods // //////////////////////////////////////////////////////////////// public void init(ServletConfig servletConfig) throws ServletException { super.init(servletConfig); // initialize our VelocityManager velocityManager.init(servletConfig.getServletContext()); servletConfig.getServletContext().setAttribute("webwork.servlet", this); initEventCartridge(servletConfig); } protected Context createContext(HttpServletRequest request, HttpServletResponse response) { ChainedContext ctx = new ChainedContext(velocityManager.createContext( ActionContext.getContext().getValueStack(), request, response), request, response, getServletContext()); /* if we have a toolbox manager, get a toolbox from it */ if (toolboxManager != null) { ctx.setToolbox(toolboxManager.getToolboxContext(ctx)); } eventCartrigde.attachToContext(ctx); return ctx; } protected Template handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Context context) throws Exception { String servletPath = (String) httpServletRequest .getAttribute("javax.servlet.include.servlet_path"); if (servletPath == null) { servletPath = httpServletRequest.getServletPath(); } return getTemplate(servletPath, getEncoding()); } /** * This method extends the VelocityServlet's loadConfiguration method by * performing the following actions: * <ul> * <li>invokes VelocityServlet.loadConfiguration to create a properties * object</li> * <li>alters the RESOURCE_LOADER to include a class loader</li> * <li>configures the class loader using the WebWorkResourceLoader</li> * </ul> * * @param servletConfig * @throws IOException * @throws FileNotFoundException * @see org.apache.velocity.servlet.VelocityServlet#loadConfiguration */ protected ExtendedProperties loadConfiguration(ServletConfig servletConfig) throws IOException, FileNotFoundException { return ExtendedProperties.convertProperties(velocityManager .loadConfiguration(servletConfig.getServletContext())); } /** * create a PageContext and render the template to PageContext.getOut() * * @see VelocityServlet#mergeTemplate(Template, Context, * HttpServletResponse) for additional documentation */ protected void mergeTemplate(Template template, Context context, HttpServletResponse response) throws ResourceNotFoundException, ParseErrorException, MethodInvocationException, IOException, UnsupportedEncodingException, Exception { // save the old PageContext PageContext oldPageContext = ServletActionContext.getPageContext(); // create a new PageContext JspFactory jspFactory = JspFactory.getDefaultFactory(); HttpServletRequest request = (HttpServletRequest) context .get(VelocityManager.REQUEST); PageContext pageContext = jspFactory.getPageContext(this, request, response, null, true, 8192, true); // put the new PageContext into ActionContext ActionContext actionContext = ActionContext.getContext(); actionContext.put(ServletActionContext.PAGE_CONTEXT, pageContext); try { Writer writer = pageContext.getOut(); template.merge(context, writer); writer.flush(); } finally { // perform cleanup jspFactory.releasePageContext(pageContext); actionContext .put(ServletActionContext.PAGE_CONTEXT, oldPageContext); } } private String getEncoding() { // todo look into converting this to using XWork/WebWork2 encoding rules try { return Configuration.getString("webwork.i18n.encoding"); } catch (IllegalArgumentException e) { return RuntimeSingleton.getString(RuntimeSingleton.OUTPUT_ENCODING, DEFAULT_OUTPUT_ENCODING); } } protected void initEventCartridge(ServletConfig config) throws ServletException { eventCartrigde = new EventCartridge(); eventCartrigde.addEventHandler(new ReferenceInsertionEventHandler() { public Object referenceInsert(String reference, Object value) { //System.out.println("value:" + value + ":reference:"+reference); if (value == null || value instanceof Unescape) { return value; } else { //System.out.println(StringUtils.escape(value.toString())); //ここは好きなメソッドを使ってください。 return StringEscapeUtils.escapeHtml(value.toString()); } } }); } }
次にこの中で使っているUnescapeのソース
package hogehoge.velocity; public class Unescape { private Object object; protected Unescape(Object object){ this.object = object; } public String toString(){ return object == null ? null:object.toString(); } }
それのToolバージョン
package hogehoge.velocity; public class UnescapeTool { public static Unescape unescape(Object object){ return object == null ? null : new Unescape(object); } }
でもって、WebworkのResult部分のVelocityResult(Tool版)
xwork-default.xmlを書き換えるか、resutl typeを新しく定義してやってください。
/* * Copyright (c) 2002-2003 by OpenSymphony * All rights reserved. */ package hogehoge.velocity; import java.io.Writer; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.jsp.JspFactory; import javax.servlet.jsp.PageContext; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.velocity.Template; import org.apache.velocity.app.VelocityEngine; import org.apache.velocity.context.Context; import com.opensymphony.webwork.ServletActionContext; import com.opensymphony.webwork.config.Configuration; import com.opensymphony.webwork.dispatcher.WebWorkResultSupport; import com.opensymphony.webwork.views.velocity.VelocityManager; import com.opensymphony.xwork.ActionContext; import com.opensymphony.xwork.ActionInvocation; import com.opensymphony.xwork.util.OgnlValueStack; /** * Using the Servlet container's {@link JspFactory}, this result mocks a JSP execution environment * and then displays a Velocity template that will be streamed directly to the servlet output. <p> * <p/> * This result follows the same rules from {@link WebWorkResultSupport}. * * @author <a href="mailto:matt@indigoegg.com">Matt Ho</a> */ public class VelocityResult extends WebWorkResultSupport { //~ Static fields/initializers ///////////////////////////////////////////// private static final Log log = LogFactory.getLog(VelocityResult.class); //~ Methods //////////////////////////////////////////////////////////////// /** * Creates a Velocity context from the action, loads a Velocity template and executes the * template. Output is written to the servlet output stream. * * @param finalLocation the location of the Velocity template * @param invocation an encapsulation of the action execution state. * @throws Exception if an error occurs when creating the Velocity context, loading or executing * the template or writing output to the servlet response stream. */ public void doExecute(String finalLocation, ActionInvocation invocation) throws Exception { OgnlValueStack stack = ActionContext.getContext().getValueStack(); HttpServletRequest request = ServletActionContext.getRequest(); HttpServletResponse response = ServletActionContext.getResponse(); JspFactory jspFactory = null; ServletContext servletContext = ServletActionContext.getServletContext(); //Servlet servlet = (Servlet) servletContext.getAttribute("webwork.servlet"); WebWorkVelocityViewServlet servlet = (WebWorkVelocityViewServlet) servletContext.getAttribute("webwork.servlet"); boolean usedJspFactory = false; PageContext pageContext = (PageContext) ActionContext.getContext().get(ServletActionContext.PAGE_CONTEXT); if (pageContext == null) { jspFactory = JspFactory.getDefaultFactory(); pageContext = jspFactory.getPageContext(servlet, request, response, null, true, 8192, true); ActionContext.getContext().put(ServletActionContext.PAGE_CONTEXT, pageContext); usedJspFactory = true; } try { VelocityManager velocityManager = VelocityManager.getInstance(); Template t = getTemplate(stack, velocityManager.getVelocityEngine(), invocation, finalLocation); //Context context = createContext(velocityManager, stack, request, response, finalLocation); Context context = servlet.createContext(request, response); Writer writer = pageContext.getOut(); if (usedJspFactory) { String encoding = getEncoding(finalLocation); String contentType = getContentType(finalLocation); if (encoding != null) { contentType = contentType + ";charset=" + encoding; } response.setContentType(contentType); } // t.merge(context, writer); servlet.mergeTemplate(t,context,response); if (usedJspFactory) { writer.flush(); } } catch (Exception e) { log.error("Unable to render Velocity Template, '" + finalLocation + "'", e); throw e; } finally { if (usedJspFactory) { jspFactory.releasePageContext(pageContext); } } return; } /** * Retrieve the content type for this template. * <p/> * People can override this method if they want to provide specific content types for specific templates (eg text/xml). * * @return The content type associated with this template (default "text/html") */ protected String getContentType(String templateLocation) { return "text/html"; } /** * Retrieve the encoding for this template. * <p/> * People can override this method if they want to provide specific encodings for specific templates. * * @return The encoding associated with this template (defaults to the value of 'webwork.i18n.encoding' property) */ protected String getEncoding(String templateLocation) { return (String) Configuration.get("webwork.i18n.encoding"); } /** * Given a value stack, a Velocity engine, and an action invocation, this method returns the appropriate * Velocity template to render. * * @param stack the value stack to resolve the location again (when parse equals true) * @param velocity the velocity engine to process the request against * @param invocation an encapsulation of the action execution state. * @param location the location of the template * @return the template to render * @throws Exception when the requested template could not be found */ protected Template getTemplate(OgnlValueStack stack, VelocityEngine velocity, ActionInvocation invocation, String location) throws Exception { if (!location.startsWith("/")) { location = invocation.getProxy().getNamespace() + "/" + location; } Template template = velocity.getTemplate(location); return template; } /** * Creates the VelocityContext that we'll use to render this page. * * @param velocityManager a reference to the velocityManager to use * @param stack the value stack to resolve the location against (when parse equals true) * @param location the name of the template that is being used * @return the a minted Velocity context. */ protected Context createContext(VelocityManager velocityManager, OgnlValueStack stack, HttpServletRequest request, HttpServletResponse response, String location) { return velocityManager.createContext(stack, request, response); } }