001: /*
002: * Copyright (c) 2002-2003 by OpenSymphony
003: * All rights reserved.
004: */
005: package com.opensymphony.webwork.dispatcher;
006:
007: import com.opensymphony.webwork.ServletActionContext;
008: import com.opensymphony.webwork.WebWorkConstants;
009: import com.opensymphony.webwork.config.Configuration;
010: import com.opensymphony.webwork.views.JspSupportServlet;
011: import com.opensymphony.webwork.views.velocity.VelocityManager;
012: import com.opensymphony.xwork.ActionContext;
013: import com.opensymphony.xwork.ActionInvocation;
014: import com.opensymphony.xwork.util.OgnlValueStack;
015: import org.apache.commons.logging.Log;
016: import org.apache.commons.logging.LogFactory;
017: import org.apache.velocity.Template;
018: import org.apache.velocity.app.VelocityEngine;
019: import org.apache.velocity.context.Context;
020:
021: import javax.servlet.Servlet;
022: import javax.servlet.ServletContext;
023: import javax.servlet.http.HttpServletRequest;
024: import javax.servlet.http.HttpServletResponse;
025: import javax.servlet.jsp.JspFactory;
026: import javax.servlet.jsp.PageContext;
027: import java.io.OutputStreamWriter;
028: import java.io.Writer;
029:
030: /**
031: * <!-- START SNIPPET: description -->
032: *
033: * Using the Servlet container's {@link JspFactory}, this result mocks a JSP
034: * execution environment and then displays a Velocity template that will be
035: * streamed directly to the servlet output.
036: *
037: * <!-- END SNIPPET: description -->
038: * <p/>
039: * <b>This result type takes the following parameters:</b>
040: *
041: * <!-- START SNIPPET: params -->
042: *
043: * <ul>
044: *
045: * <li><b>location (default)</b> - the location of the template to process.</li>
046: *
047: * <li><b>parse</b> - true by default. If set to false, the location param will
048: * not be parsed for Ognl expressions.</li>
049: *
050: * </ul>
051: * <p>
052: * This result follows the same rules from {@link WebWorkResultSupport}.
053: * </p>
054: *
055: * <!-- END SNIPPET: params -->
056: *
057: * <b>Example:</b>
058: *
059: * <pre><!-- START SNIPPET: example -->
060: * <result name="success" type="velocity">
061: * <param name="location">foo.vm</param>
062: * </result>
063: * <!-- END SNIPPET: example --></pre>
064: *
065: * @author <a href="mailto:matt@indigoegg.com">Matt Ho</a>
066: */
067: public class VelocityResult extends WebWorkResultSupport {
068:
069: private static final Log log = LogFactory
070: .getLog(VelocityResult.class);
071:
072: /**
073: * Creates a Velocity context from the action, loads a Velocity template and executes the
074: * template. Output is written to the servlet output stream.
075: *
076: * @param finalLocation the location of the Velocity template
077: * @param invocation an encapsulation of the action execution state.
078: * @throws Exception if an error occurs when creating the Velocity context, loading or executing
079: * the template or writing output to the servlet response stream.
080: */
081: public void doExecute(String finalLocation,
082: ActionInvocation invocation) throws Exception {
083: OgnlValueStack stack = ActionContext.getContext()
084: .getValueStack();
085:
086: HttpServletRequest request = ServletActionContext.getRequest();
087: HttpServletResponse response = ServletActionContext
088: .getResponse();
089: JspFactory jspFactory = null;
090: ServletContext servletContext = ServletActionContext
091: .getServletContext();
092: Servlet servlet = JspSupportServlet.jspSupportServlet;
093:
094: VelocityManager.getInstance().init(servletContext);
095:
096: boolean usedJspFactory = false;
097: PageContext pageContext = (PageContext) ActionContext
098: .getContext().get(ServletActionContext.PAGE_CONTEXT);
099:
100: if (pageContext == null && servlet != null) {
101: jspFactory = JspFactory.getDefaultFactory();
102: pageContext = jspFactory.getPageContext(servlet, request,
103: response, null, true, 8192, true);
104: ActionContext.getContext().put(
105: ServletActionContext.PAGE_CONTEXT, pageContext);
106: usedJspFactory = true;
107: }
108:
109: try {
110: String encoding = getEncoding(finalLocation);
111: String contentType = getContentType(finalLocation);
112:
113: if (encoding != null) {
114: contentType = contentType + ";charset=" + encoding;
115: }
116:
117: VelocityManager velocityManager = VelocityManager
118: .getInstance();
119: Template t = getTemplate(stack, velocityManager
120: .getVelocityEngine(), invocation, finalLocation,
121: encoding);
122:
123: Context context = createContext(velocityManager, stack,
124: request, response, finalLocation);
125: Writer writer = new OutputStreamWriter(response
126: .getOutputStream(), encoding);
127:
128: response.setContentType(contentType);
129:
130: t.merge(context, writer);
131:
132: // always flush the writer (we used to only flush it if this was a jspWriter, but someone asked
133: // to do it all the time (WW-829). Since Velocity support is being deprecated, we'll oblige :)
134: writer.flush();
135: } catch (Exception e) {
136: log.error("Unable to render Velocity Template, '"
137: + finalLocation + "'", e);
138: throw e;
139: } finally {
140: if (usedJspFactory) {
141: jspFactory.releasePageContext(pageContext);
142: }
143: }
144:
145: return;
146: }
147:
148: /**
149: * Retrieve the content type for this template.
150: * <p/>
151: * People can override this method if they want to provide specific content types for specific templates (eg text/xml).
152: *
153: * @return The content type associated with this template (default "text/html")
154: */
155: protected String getContentType(String templateLocation) {
156: return "text/html";
157: }
158:
159: /**
160: * Retrieve the encoding for this template.
161: * <p/>
162: * People can override this method if they want to provide specific encodings for specific templates.
163: *
164: * @return The encoding associated with this template (defaults to the value of 'webwork.i18n.encoding' property)
165: */
166: protected String getEncoding(String templateLocation) {
167: String encoding = (String) Configuration
168: .get(WebWorkConstants.WEBWORK_I18N_ENCODING);
169: if (encoding == null) {
170: encoding = System.getProperty("file.encoding");
171: }
172: if (encoding == null) {
173: encoding = "UTF-8";
174: }
175: return encoding;
176: }
177:
178: /**
179: * Given a value stack, a Velocity engine, and an action invocation, this method returns the appropriate
180: * Velocity template to render.
181: *
182: * @param stack the value stack to resolve the location again (when parse equals true)
183: * @param velocity the velocity engine to process the request against
184: * @param invocation an encapsulation of the action execution state.
185: * @param location the location of the template
186: * @param encoding the charset encoding of the template
187: * @return the template to render
188: * @throws Exception when the requested template could not be found
189: */
190: protected Template getTemplate(OgnlValueStack stack,
191: VelocityEngine velocity, ActionInvocation invocation,
192: String location, String encoding) throws Exception {
193: if (!location.startsWith("/")) {
194: location = invocation.getProxy().getNamespace() + "/"
195: + location;
196: }
197:
198: Template template = velocity.getTemplate(location, encoding);
199:
200: return template;
201: }
202:
203: /**
204: * Creates the VelocityContext that we'll use to render this page.
205: *
206: * @param velocityManager a reference to the velocityManager to use
207: * @param stack the value stack to resolve the location against (when parse equals true)
208: * @param location the name of the template that is being used
209: * @return the a minted Velocity context.
210: */
211: protected Context createContext(VelocityManager velocityManager,
212: OgnlValueStack stack, HttpServletRequest request,
213: HttpServletResponse response, String location) {
214: return velocityManager.createContext(stack, request, response);
215: }
216: }
|