View Javadoc

1   /*
2    * $Id: PortletVelocityResult.java 454565 2006-10-10 00:02:56Z jmitchell $
3    *
4    * Copyright 2006 The Apache Software Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
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   * &lt;!-- START SNIPPET: example --&gt;
83   *  &lt;result name=&quot;success&quot; type=&quot;velocity&quot;&gt;
84   *    &lt;param name=&quot;location&quot;&gt;foo.vm&lt;/param&gt;
85   *  &lt;/result&gt;
86   *  &lt;!-- END SNIPPET: example --&gt;
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     /* (non-Javadoc)
106      * @see org.apache.struts2.dispatcher.StrutsResultSupport#doExecute(java.lang.String, com.opensymphony.xwork2.ActionInvocation)
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         // View is rendered outside an action...uh oh...
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             // always flush the writer (we used to only flush it if this was a
195             // jspWriter, but someone asked
196             // to do it all the time (WW-829). Since Velocity support is being
197             // deprecated, we'll oblige :)
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 }