1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.struts2.dispatcher;
22
23 import java.io.OutputStreamWriter;
24 import java.io.Writer;
25
26 import javax.servlet.Servlet;
27 import javax.servlet.ServletContext;
28 import javax.servlet.http.HttpServletRequest;
29 import javax.servlet.http.HttpServletResponse;
30 import javax.servlet.jsp.JspFactory;
31 import javax.servlet.jsp.PageContext;
32
33 import org.apache.commons.logging.Log;
34 import org.apache.commons.logging.LogFactory;
35 import org.apache.struts2.ServletActionContext;
36 import org.apache.struts2.StrutsConstants;
37 import org.apache.struts2.views.JspSupportServlet;
38 import org.apache.struts2.views.velocity.VelocityManager;
39 import org.apache.velocity.Template;
40 import org.apache.velocity.app.VelocityEngine;
41 import org.apache.velocity.context.Context;
42
43 import com.opensymphony.xwork2.ActionContext;
44 import com.opensymphony.xwork2.ActionInvocation;
45 import com.opensymphony.xwork2.inject.Inject;
46 import com.opensymphony.xwork2.util.ValueStack;
47
48
49 /***
50 * <!-- START SNIPPET: description -->
51 *
52 * Using the Servlet container's {@link JspFactory}, this result mocks a JSP
53 * execution environment and then displays a Velocity template that will be
54 * streamed directly to the servlet output.
55 *
56 * <!-- END SNIPPET: description -->
57 * <p/>
58 * <b>This result type takes the following parameters:</b>
59 *
60 * <!-- START SNIPPET: params -->
61 *
62 * <ul>
63 *
64 * <li><b>location (default)</b> - the location of the template to process.</li>
65 *
66 * <li><b>parse</b> - true by default. If set to false, the location param will
67 * not be parsed for Ognl expressions.</li>
68 *
69 * </ul>
70 * <p>
71 * This result follows the same rules from {@link StrutsResultSupport}.
72 * </p>
73 *
74 * <!-- END SNIPPET: params -->
75 *
76 * <b>Example:</b>
77 *
78 * <pre><!-- START SNIPPET: example -->
79 * <result name="success" type="velocity">
80 * <param name="location">foo.vm</param>
81 * </result>
82 * <!-- END SNIPPET: example --></pre>
83 *
84 */
85 public class VelocityResult extends StrutsResultSupport {
86
87 private static final long serialVersionUID = 7268830767762559424L;
88
89 private static final Log log = LogFactory.getLog(VelocityResult.class);
90
91 private String defaultEncoding;
92 private VelocityManager velocityManager;
93
94 public VelocityResult() {
95 super();
96 }
97
98 public VelocityResult(String location) {
99 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, ActionInvocation invocation) throws Exception {
122 ValueStack stack = ActionContext.getContext().getValueStack();
123
124 HttpServletRequest request = ServletActionContext.getRequest();
125 HttpServletResponse response = ServletActionContext.getResponse();
126 JspFactory jspFactory = null;
127 ServletContext servletContext = ServletActionContext.getServletContext();
128 Servlet servlet = JspSupportServlet.jspSupportServlet;
129
130 velocityManager.init(servletContext);
131
132 boolean usedJspFactory = false;
133 PageContext pageContext = (PageContext) ActionContext.getContext().get(ServletActionContext.PAGE_CONTEXT);
134
135 if (pageContext == null && servlet != null) {
136 jspFactory = JspFactory.getDefaultFactory();
137 pageContext = jspFactory.getPageContext(servlet, request, response, null, true, 8192, true);
138 ActionContext.getContext().put(ServletActionContext.PAGE_CONTEXT, pageContext);
139 usedJspFactory = true;
140 }
141
142 try {
143 String encoding = getEncoding(finalLocation);
144 String contentType = getContentType(finalLocation);
145
146 if (encoding != null) {
147 contentType = contentType + ";charset=" + encoding;
148 }
149
150 Template t = getTemplate(stack, velocityManager.getVelocityEngine(), invocation, finalLocation, encoding);
151
152 Context context = createContext(velocityManager, stack, request, response, finalLocation);
153 Writer writer = new OutputStreamWriter(response.getOutputStream(), encoding);
154
155
156 response.setContentType(contentType);
157
158 t.merge(context, writer);
159
160
161
162 writer.flush();
163 } catch (Exception e) {
164 log.error("Unable to render Velocity Template, '" + finalLocation + "'", e);
165 throw e;
166 } finally {
167 if (usedJspFactory) {
168 jspFactory.releasePageContext(pageContext);
169 }
170 }
171
172 return;
173 }
174
175 /***
176 * Retrieve the content type for this template.
177 * <p/>
178 * People can override this method if they want to provide specific content types for specific templates (eg text/xml).
179 *
180 * @return The content type associated with this template (default "text/html")
181 */
182 protected String getContentType(String templateLocation) {
183 return "text/html";
184 }
185
186 /***
187 * Retrieve the encoding for this template.
188 * <p/>
189 * People can override this method if they want to provide specific encodings for specific templates.
190 *
191 * @return The encoding associated with this template (defaults to the value of 'struts.i18n.encoding' property)
192 */
193 protected String getEncoding(String templateLocation) {
194 String encoding = defaultEncoding;
195 if (encoding == null) {
196 encoding = System.getProperty("file.encoding");
197 }
198 if (encoding == null) {
199 encoding = "UTF-8";
200 }
201 return encoding;
202 }
203
204 /***
205 * Given a value stack, a Velocity engine, and an action invocation, this method returns the appropriate
206 * Velocity template to render.
207 *
208 * @param stack the value stack to resolve the location again (when parse equals true)
209 * @param velocity the velocity engine to process the request against
210 * @param invocation an encapsulation of the action execution state.
211 * @param location the location of the template
212 * @param encoding the charset encoding of the template
213 * @return the template to render
214 * @throws Exception when the requested template could not be found
215 */
216 protected Template getTemplate(ValueStack stack, VelocityEngine velocity, ActionInvocation invocation, String location, String encoding) throws Exception {
217 if (!location.startsWith("/")) {
218 location = invocation.getProxy().getNamespace() + "/" + location;
219 }
220
221 Template template = velocity.getTemplate(location, encoding);
222
223 return template;
224 }
225
226 /***
227 * Creates the VelocityContext that we'll use to render this page.
228 *
229 * @param velocityManager a reference to the velocityManager to use
230 * @param stack the value stack to resolve the location against (when parse equals true)
231 * @param location the name of the template that is being used
232 * @return the a minted Velocity context.
233 */
234 protected Context createContext(VelocityManager velocityManager, ValueStack stack, HttpServletRequest request, HttpServletResponse response, String location) {
235 return velocityManager.createContext(stack, request, response);
236 }
237 }