View Javadoc

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