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