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.portlet.result;
22
23 import com.opensymphony.xwork2.ActionContext;
24 import com.opensymphony.xwork2.ActionInvocation;
25 import com.opensymphony.xwork2.inject.Inject;
26 import com.opensymphony.xwork2.util.ValueStack;
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.apache.struts2.ServletActionContext;
30 import org.apache.struts2.StrutsConstants;
31 import org.apache.struts2.dispatcher.StrutsResultSupport;
32 import org.apache.struts2.portlet.PortletActionConstants;
33 import org.apache.struts2.portlet.context.PortletActionContext;
34 import org.apache.struts2.views.JspSupportServlet;
35 import org.apache.struts2.views.velocity.VelocityManager;
36 import org.apache.velocity.Template;
37 import org.apache.velocity.app.VelocityEngine;
38 import org.apache.velocity.context.Context;
39
40 import javax.portlet.ActionResponse;
41 import javax.servlet.Servlet;
42 import javax.servlet.ServletContext;
43 import javax.servlet.http.HttpServletRequest;
44 import javax.servlet.http.HttpServletResponse;
45 import javax.servlet.jsp.JspFactory;
46 import javax.servlet.jsp.PageContext;
47 import java.io.OutputStreamWriter;
48 import java.io.Writer;
49
50 /***
51 * <!-- START SNIPPET: description -->
52 *
53 * Using the Servlet container's {@link JspFactory}, this result mocks a JSP
54 * execution environment and then displays a Velocity template that will be
55 * streamed directly to the servlet output.
56 *
57 * <!-- END SNIPPET: description --> <p/><b>This result type takes the
58 * 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.
65 * </li>
66 *
67 * <li><b>parse </b>- true by default. If set to false, the location param
68 * will not be parsed for Ognl expressions.</li>
69 *
70 * </ul>
71 * <p>
72 * This result follows the same rules from {@link StrutsResultSupport}.
73 * </p>
74 *
75 * <!-- END SNIPPET: params -->
76 *
77 * <b>Example: </b>
78 *
79 * <pre>
80 * <!-- START SNIPPET: example -->
81 * <result name="success" type="velocity">
82 * <param name="location">foo.vm</param>
83 * </result>
84 * <!-- END SNIPPET: example -->
85 * </pre>
86 *
87 */
88 public class PortletVelocityResult extends StrutsResultSupport {
89
90 private static final long serialVersionUID = -8241086555872212274L;
91
92 private static final Log log = LogFactory
93 .getLog(PortletVelocityResult.class);
94
95 private String defaultEncoding;
96 private VelocityManager velocityManager;
97
98 public PortletVelocityResult() {
99 super();
100 }
101
102 public PortletVelocityResult(String location) {
103 super(location);
104 }
105
106 @Inject
107 public void setVelocityManager(VelocityManager mgr) {
108 this.velocityManager = mgr;
109 }
110
111 @Inject(StrutsConstants.STRUTS_I18N_ENCODING)
112 public void setDefaultEncoding(String encoding) {
113 this.defaultEncoding = encoding;
114 }
115
116
117
118
119 public void doExecute(String location, ActionInvocation invocation)
120 throws Exception {
121 if (PortletActionContext.isEvent()) {
122 executeActionResult(location, invocation);
123 } else if (PortletActionContext.isRender()) {
124 executeRenderResult(location, invocation);
125 }
126 }
127
128 /***
129 * Executes the result
130 *
131 * @param location The location string
132 * @param invocation The action invocation
133 */
134 private void executeActionResult(String location,
135 ActionInvocation invocation) {
136 ActionResponse res = PortletActionContext.getActionResponse();
137
138 res.setRenderParameter(PortletActionConstants.ACTION_PARAM,
139 "freemarkerDirect");
140 res.setRenderParameter("location", location);
141 res.setRenderParameter(PortletActionConstants.MODE_PARAM, PortletActionContext
142 .getRequest().getPortletMode().toString());
143
144 }
145
146 /***
147 * Creates a Velocity context from the action, loads a Velocity template and
148 * executes the template. Output is written to the servlet output stream.
149 *
150 * @param finalLocation the location of the Velocity template
151 * @param invocation an encapsulation of the action execution state.
152 * @throws Exception if an error occurs when creating the Velocity context,
153 * loading or executing the template or writing output to the
154 * servlet response stream.
155 */
156 public void executeRenderResult(String finalLocation,
157 ActionInvocation invocation) throws Exception {
158 ValueStack stack = ActionContext.getContext().getValueStack();
159
160 HttpServletRequest request = ServletActionContext.getRequest();
161 HttpServletResponse response = ServletActionContext.getResponse();
162 JspFactory jspFactory = null;
163 ServletContext servletContext = ServletActionContext
164 .getServletContext();
165 Servlet servlet = JspSupportServlet.jspSupportServlet;
166
167 velocityManager.init(servletContext);
168
169 boolean usedJspFactory = false;
170 PageContext pageContext = (PageContext) ActionContext.getContext().get(
171 ServletActionContext.PAGE_CONTEXT);
172
173 if (pageContext == null && servlet != null) {
174 jspFactory = JspFactory.getDefaultFactory();
175 pageContext = jspFactory.getPageContext(servlet, request, response,
176 null, true, 8192, true);
177 ActionContext.getContext().put(ServletActionContext.PAGE_CONTEXT,
178 pageContext);
179 usedJspFactory = true;
180 }
181
182 try {
183 String encoding = getEncoding(finalLocation);
184 String contentType = getContentType(finalLocation);
185
186 if (encoding != null) {
187 contentType = contentType + ";charset=" + encoding;
188 }
189 response.setContentType(contentType);
190 Template t = getTemplate(stack,
191 velocityManager.getVelocityEngine(), invocation,
192 finalLocation, encoding);
193
194 Context context = createContext(velocityManager, stack, request,
195 response, finalLocation);
196 Writer writer = new OutputStreamWriter(response.getOutputStream(),
197 encoding);
198
199 t.merge(context, writer);
200
201
202
203
204
205 writer.flush();
206 } catch (Exception e) {
207 log.error("Unable to render Velocity Template, '" + finalLocation
208 + "'", e);
209 throw e;
210 } finally {
211 if (usedJspFactory) {
212 jspFactory.releasePageContext(pageContext);
213 }
214 }
215
216 return;
217 }
218
219 /***
220 * Retrieve the content type for this template. <p/>People can override
221 * this method if they want to provide specific content types for specific
222 * templates (eg text/xml).
223 *
224 * @return The content type associated with this template (default
225 * "text/html")
226 */
227 protected String getContentType(String templateLocation) {
228 return "text/html";
229 }
230
231 /***
232 * Retrieve the encoding for this template. <p/>People can override this
233 * method if they want to provide specific encodings for specific templates.
234 *
235 * @return The encoding associated with this template (defaults to the value
236 * of 'struts.i18n.encoding' property)
237 */
238 protected String getEncoding(String templateLocation) {
239 String encoding = defaultEncoding;
240 if (encoding == null) {
241 encoding = System.getProperty("file.encoding");
242 }
243 if (encoding == null) {
244 encoding = "UTF-8";
245 }
246 return encoding;
247 }
248
249 /***
250 * Given a value stack, a Velocity engine, and an action invocation, this
251 * method returns the appropriate Velocity template to render.
252 *
253 * @param stack the value stack to resolve the location again (when parse
254 * equals true)
255 * @param velocity the velocity engine to process the request against
256 * @param invocation an encapsulation of the action execution state.
257 * @param location the location of the template
258 * @param encoding the charset encoding of the template
259 * @return the template to render
260 * @throws Exception when the requested template could not be found
261 */
262 protected Template getTemplate(ValueStack stack,
263 VelocityEngine velocity, ActionInvocation invocation,
264 String location, String encoding) throws Exception {
265 if (!location.startsWith("/")) {
266 location = invocation.getProxy().getNamespace() + "/" + location;
267 }
268
269 Template template = velocity.getTemplate(location, encoding);
270
271 return template;
272 }
273
274 /***
275 * Creates the VelocityContext that we'll use to render this page.
276 *
277 * @param velocityManager a reference to the velocityManager to use
278 * @param stack the value stack to resolve the location against (when parse
279 * equals true)
280 * @param location the name of the template that is being used
281 * @return the a minted Velocity context.
282 */
283 protected Context createContext(VelocityManager velocityManager,
284 ValueStack stack, HttpServletRequest request,
285 HttpServletResponse response, String location) {
286 return velocityManager.createContext(stack, request, response);
287 }
288
289 }