View Javadoc

1   /*
2    * $Id: VelocityResult.java 454565 2006-10-10 00:02:56Z jmitchell $
3    *
4    * Copyright 2006 The Apache Software Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.struts2.dispatcher;
19  
20  import java.io.OutputStreamWriter;
21  import java.io.Writer;
22  
23  import javax.servlet.Servlet;
24  import javax.servlet.ServletContext;
25  import javax.servlet.http.HttpServletRequest;
26  import javax.servlet.http.HttpServletResponse;
27  import javax.servlet.jsp.JspFactory;
28  import javax.servlet.jsp.PageContext;
29  
30  import org.apache.commons.logging.Log;
31  import org.apache.commons.logging.LogFactory;
32  import org.apache.struts2.ServletActionContext;
33  import org.apache.struts2.StrutsConstants;
34  import org.apache.struts2.config.Settings;
35  import org.apache.struts2.views.JspSupportServlet;
36  import org.apache.struts2.views.velocity.VelocityManager;
37  import org.apache.velocity.Template;
38  import org.apache.velocity.app.VelocityEngine;
39  import org.apache.velocity.context.Context;
40  
41  import com.opensymphony.xwork2.ActionContext;
42  import com.opensymphony.xwork2.ActionInvocation;
43  import com.opensymphony.xwork2.util.ValueStack;
44  
45  
46  /***
47   * <!-- START SNIPPET: description -->
48   *
49   * Using the Servlet container's {@link JspFactory}, this result mocks a JSP
50   * execution environment and then displays a Velocity template that will be
51   * streamed directly to the servlet output.
52   *
53   * <!-- END SNIPPET: description -->
54   * <p/>
55   * <b>This result type takes the following parameters:</b>
56   *
57   * <!-- START SNIPPET: params -->
58   *
59   * <ul>
60   *
61   * <li><b>location (default)</b> - the location of the template to process.</li>
62   *
63   * <li><b>parse</b> - true by default. If set to false, the location param will 
64   * not be parsed for Ognl expressions.</li>
65   *
66   * </ul>
67   * <p>
68   * This result follows the same rules from {@link StrutsResultSupport}.
69   * </p>
70   *
71   * <!-- END SNIPPET: params -->
72   *
73   * <b>Example:</b>
74   *
75   * <pre><!-- START SNIPPET: example -->
76   * &lt;result name="success" type="velocity"&gt;
77   *   &lt;param name="location"&gt;foo.vm&lt;/param&gt;
78   * &lt;/result&gt;
79   * <!-- END SNIPPET: example --></pre>
80   *
81   */
82  public class VelocityResult extends StrutsResultSupport {
83  
84  	private static final long serialVersionUID = 7268830767762559424L;
85  	
86  	private static final Log log = LogFactory.getLog(VelocityResult.class);
87  
88  	public VelocityResult() {
89  		super();
90  	}
91  	
92  	public VelocityResult(String location) {
93  		super(location);
94  	}
95  
96      /***
97       * Creates a Velocity context from the action, loads a Velocity template and executes the
98       * template. Output is written to the servlet output stream.
99       *
100      * @param finalLocation the location of the Velocity template
101      * @param invocation    an encapsulation of the action execution state.
102      * @throws Exception if an error occurs when creating the Velocity context, loading or executing
103      *                   the template or writing output to the servlet response stream.
104      */
105     public void doExecute(String finalLocation, ActionInvocation invocation) throws Exception {
106         ValueStack stack = ActionContext.getContext().getValueStack();
107 
108         HttpServletRequest request = ServletActionContext.getRequest();
109         HttpServletResponse response = ServletActionContext.getResponse();
110         JspFactory jspFactory = null;
111         ServletContext servletContext = ServletActionContext.getServletContext();
112         Servlet servlet = JspSupportServlet.jspSupportServlet;
113 
114         VelocityManager.getInstance().init(servletContext);
115 
116         boolean usedJspFactory = false;
117         PageContext pageContext = (PageContext) ActionContext.getContext().get(ServletActionContext.PAGE_CONTEXT);
118 
119         if (pageContext == null && servlet != null) {
120             jspFactory = JspFactory.getDefaultFactory();
121             pageContext = jspFactory.getPageContext(servlet, request, response, null, true, 8192, true);
122             ActionContext.getContext().put(ServletActionContext.PAGE_CONTEXT, pageContext);
123             usedJspFactory = true;
124         }
125 
126         try {
127             String encoding = getEncoding(finalLocation);
128             String contentType = getContentType(finalLocation);
129 
130             if (encoding != null) {
131                 contentType = contentType + ";charset=" + encoding;
132             }
133 
134             VelocityManager velocityManager = VelocityManager.getInstance();
135             Template t = getTemplate(stack, velocityManager.getVelocityEngine(), invocation, finalLocation, encoding);
136 
137             Context context = createContext(velocityManager, stack, request, response, finalLocation);
138             Writer writer = new OutputStreamWriter(response.getOutputStream(), encoding);
139 
140 
141             response.setContentType(contentType);
142 
143             t.merge(context, writer);
144 
145             // always flush the writer (we used to only flush it if this was a jspWriter, but someone asked
146             // to do it all the time (WW-829). Since Velocity support is being deprecated, we'll oblige :)
147             writer.flush();
148         } catch (Exception e) {
149             log.error("Unable to render Velocity Template, '" + finalLocation + "'", e);
150             throw e;
151         } finally {
152             if (usedJspFactory) {
153                 jspFactory.releasePageContext(pageContext);
154             }
155         }
156 
157         return;
158     }
159 
160     /***
161      * Retrieve the content type for this template.
162      * <p/>
163      * People can override this method if they want to provide specific content types for specific templates (eg text/xml).
164      *
165      * @return The content type associated with this template (default "text/html")
166      */
167     protected String getContentType(String templateLocation) {
168         return "text/html";
169     }
170 
171     /***
172      * Retrieve the encoding for this template.
173      * <p/>
174      * People can override this method if they want to provide specific encodings for specific templates.
175      *
176      * @return The encoding associated with this template (defaults to the value of 'struts.i18n.encoding' property)
177      */
178     protected String getEncoding(String templateLocation) {
179         String encoding = (String) Settings.get(StrutsConstants.STRUTS_I18N_ENCODING);
180         if (encoding == null) {
181             encoding = System.getProperty("file.encoding");
182         }
183         if (encoding == null) {
184             encoding = "UTF-8";
185         }
186         return encoding;
187     }
188 
189     /***
190      * Given a value stack, a Velocity engine, and an action invocation, this method returns the appropriate
191      * Velocity template to render.
192      *
193      * @param stack      the value stack to resolve the location again (when parse equals true)
194      * @param velocity   the velocity engine to process the request against
195      * @param invocation an encapsulation of the action execution state.
196      * @param location   the location of the template
197      * @param encoding   the charset encoding of the template
198      * @return the template to render
199      * @throws Exception when the requested template could not be found
200      */
201     protected Template getTemplate(ValueStack stack, VelocityEngine velocity, ActionInvocation invocation, String location, String encoding) throws Exception {
202         if (!location.startsWith("/")) {
203             location = invocation.getProxy().getNamespace() + "/" + location;
204         }
205 
206         Template template = velocity.getTemplate(location, encoding);
207 
208         return template;
209     }
210 
211     /***
212      * Creates the VelocityContext that we'll use to render this page.
213      *
214      * @param velocityManager a reference to the velocityManager to use
215      * @param stack           the value stack to resolve the location against (when parse equals true)
216      * @param location        the name of the template that is being used
217      * @return the a minted Velocity context.
218      */
219     protected Context createContext(VelocityManager velocityManager, ValueStack stack, HttpServletRequest request, HttpServletResponse response, String location) {
220         return velocityManager.createContext(stack, request, response);
221     }
222 }