001: /*
002: * $Id: VelocityResult.java 474191 2006-11-13 08:30:40Z mrdon $
003: *
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021: package org.apache.struts2.dispatcher;
022:
023: import java.io.OutputStreamWriter;
024: import java.io.Writer;
025:
026: import javax.servlet.Servlet;
027: import javax.servlet.ServletContext;
028: import javax.servlet.http.HttpServletRequest;
029: import javax.servlet.http.HttpServletResponse;
030: import javax.servlet.jsp.JspFactory;
031: import javax.servlet.jsp.PageContext;
032:
033: import org.apache.commons.logging.Log;
034: import org.apache.commons.logging.LogFactory;
035: import org.apache.struts2.ServletActionContext;
036: import org.apache.struts2.StrutsConstants;
037: import org.apache.struts2.views.JspSupportServlet;
038: import org.apache.struts2.views.velocity.VelocityManager;
039: import org.apache.velocity.Template;
040: import org.apache.velocity.app.VelocityEngine;
041: import org.apache.velocity.context.Context;
042:
043: import com.opensymphony.xwork2.ActionContext;
044: import com.opensymphony.xwork2.ActionInvocation;
045: import com.opensymphony.xwork2.inject.Inject;
046: import com.opensymphony.xwork2.util.ValueStack;
047:
048: /**
049: * <!-- START SNIPPET: description -->
050: *
051: * Using the Servlet container's {@link JspFactory}, this result mocks a JSP
052: * execution environment and then displays a Velocity template that will be
053: * streamed directly to the servlet output.
054: *
055: * <!-- END SNIPPET: description -->
056: * <p/>
057: * <b>This result type takes the following parameters:</b>
058: *
059: * <!-- START SNIPPET: params -->
060: *
061: * <ul>
062: *
063: * <li><b>location (default)</b> - the location of the template to process.</li>
064: *
065: * <li><b>parse</b> - true by default. If set to false, the location param will
066: * not be parsed for Ognl expressions.</li>
067: *
068: * </ul>
069: * <p>
070: * This result follows the same rules from {@link StrutsResultSupport}.
071: * </p>
072: *
073: * <!-- END SNIPPET: params -->
074: *
075: * <b>Example:</b>
076: *
077: * <pre><!-- START SNIPPET: example -->
078: * <result name="success" type="velocity">
079: * <param name="location">foo.vm</param>
080: * </result>
081: * <!-- END SNIPPET: example --></pre>
082: *
083: */
084: public class VelocityResult extends StrutsResultSupport {
085:
086: private static final long serialVersionUID = 7268830767762559424L;
087:
088: private static final Log log = LogFactory
089: .getLog(VelocityResult.class);
090:
091: private String defaultEncoding;
092: private VelocityManager velocityManager;
093:
094: public VelocityResult() {
095: super ();
096: }
097:
098: public VelocityResult(String location) {
099: super (location);
100: }
101:
102: @Inject(StrutsConstants.STRUTS_I18N_ENCODING)
103: public void setDefaultEncoding(String val) {
104: defaultEncoding = val;
105: }
106:
107: @Inject
108: public void setVelocityManager(VelocityManager mgr) {
109: this .velocityManager = mgr;
110: }
111:
112: /**
113: * Creates a Velocity context from the action, loads a Velocity template and executes the
114: * template. Output is written to the servlet output stream.
115: *
116: * @param finalLocation the location of the Velocity template
117: * @param invocation an encapsulation of the action execution state.
118: * @throws Exception if an error occurs when creating the Velocity context, loading or executing
119: * the template or writing output to the servlet response stream.
120: */
121: public void doExecute(String finalLocation,
122: ActionInvocation invocation) throws Exception {
123: ValueStack stack = ActionContext.getContext().getValueStack();
124:
125: HttpServletRequest request = ServletActionContext.getRequest();
126: HttpServletResponse response = ServletActionContext
127: .getResponse();
128: JspFactory jspFactory = null;
129: ServletContext servletContext = ServletActionContext
130: .getServletContext();
131: Servlet servlet = JspSupportServlet.jspSupportServlet;
132:
133: velocityManager.init(servletContext);
134:
135: boolean usedJspFactory = false;
136: PageContext pageContext = (PageContext) ActionContext
137: .getContext().get(ServletActionContext.PAGE_CONTEXT);
138:
139: if (pageContext == null && servlet != null) {
140: jspFactory = JspFactory.getDefaultFactory();
141: pageContext = jspFactory.getPageContext(servlet, request,
142: response, null, true, 8192, true);
143: ActionContext.getContext().put(
144: ServletActionContext.PAGE_CONTEXT, pageContext);
145: usedJspFactory = true;
146: }
147:
148: try {
149: String encoding = getEncoding(finalLocation);
150: String contentType = getContentType(finalLocation);
151:
152: if (encoding != null) {
153: contentType = contentType + ";charset=" + encoding;
154: }
155:
156: Template t = getTemplate(stack, velocityManager
157: .getVelocityEngine(), invocation, finalLocation,
158: encoding);
159:
160: Context context = createContext(velocityManager, stack,
161: request, response, finalLocation);
162: Writer writer = new OutputStreamWriter(response
163: .getOutputStream(), encoding);
164:
165: response.setContentType(contentType);
166:
167: t.merge(context, writer);
168:
169: // always flush the writer (we used to only flush it if this was a jspWriter, but someone asked
170: // to do it all the time (WW-829). Since Velocity support is being deprecated, we'll oblige :)
171: writer.flush();
172: } catch (Exception e) {
173: log.error("Unable to render Velocity Template, '"
174: + finalLocation + "'", e);
175: throw e;
176: } finally {
177: if (usedJspFactory) {
178: jspFactory.releasePageContext(pageContext);
179: }
180: }
181:
182: return;
183: }
184:
185: /**
186: * Retrieve the content type for this template.
187: * <p/>
188: * People can override this method if they want to provide specific content types for specific templates (eg text/xml).
189: *
190: * @return The content type associated with this template (default "text/html")
191: */
192: protected String getContentType(String templateLocation) {
193: return "text/html";
194: }
195:
196: /**
197: * Retrieve the encoding for this template.
198: * <p/>
199: * People can override this method if they want to provide specific encodings for specific templates.
200: *
201: * @return The encoding associated with this template (defaults to the value of 'struts.i18n.encoding' property)
202: */
203: protected String getEncoding(String templateLocation) {
204: String encoding = defaultEncoding;
205: if (encoding == null) {
206: encoding = System.getProperty("file.encoding");
207: }
208: if (encoding == null) {
209: encoding = "UTF-8";
210: }
211: return encoding;
212: }
213:
214: /**
215: * Given a value stack, a Velocity engine, and an action invocation, this method returns the appropriate
216: * Velocity template to render.
217: *
218: * @param stack the value stack to resolve the location again (when parse equals true)
219: * @param velocity the velocity engine to process the request against
220: * @param invocation an encapsulation of the action execution state.
221: * @param location the location of the template
222: * @param encoding the charset encoding of the template
223: * @return the template to render
224: * @throws Exception when the requested template could not be found
225: */
226: protected Template getTemplate(ValueStack stack,
227: VelocityEngine velocity, ActionInvocation invocation,
228: String location, String encoding) throws Exception {
229: if (!location.startsWith("/")) {
230: location = invocation.getProxy().getNamespace() + "/"
231: + location;
232: }
233:
234: Template template = velocity.getTemplate(location, encoding);
235:
236: return template;
237: }
238:
239: /**
240: * Creates the VelocityContext that we'll use to render this page.
241: *
242: * @param velocityManager a reference to the velocityManager to use
243: * @param stack the value stack to resolve the location against (when parse equals true)
244: * @param location the name of the template that is being used
245: * @return the a minted Velocity context.
246: */
247: protected Context createContext(VelocityManager velocityManager,
248: ValueStack stack, HttpServletRequest request,
249: HttpServletResponse response, String location) {
250: return velocityManager.createContext(stack, request, response);
251: }
252: }
|