View Javadoc

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