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