View Javadoc

1   /*
2    * $Id: PortletVelocityResult.java 670542 2008-06-23 12:31:51Z hermanns $
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  
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   * &lt;!-- START SNIPPET: example --&gt;
84   *  &lt;result name=&quot;success&quot; type=&quot;velocity&quot;&gt;
85   *    &lt;param name=&quot;location&quot;&gt;foo.vm&lt;/param&gt;
86   *  &lt;/result&gt;
87   *  &lt;!-- END SNIPPET: example --&gt;
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     /* (non-Javadoc)
118      * @see org.apache.struts2.dispatcher.StrutsResultSupport#doExecute(java.lang.String, com.opensymphony.xwork2.ActionInvocation)
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         // View is rendered outside an action...uh oh...
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             // always flush the writer (we used to only flush it if this was a
207             // jspWriter, but someone asked
208             // to do it all the time (WW-829). Since Velocity support is being
209             // deprecated, we'll oblige :)
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 }