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 }