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