1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 * <result name="success" type="velocity">
77 * <param name="location">foo.vm</param>
78 * </result>
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
146
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 }