View Javadoc

    1   /*
    2    * $Id: PortletFreemarkerResult.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.views.freemarker;
    22
    23  import java.io.IOException;
    24  import java.io.Writer;
    25  import java.util.Locale;
    26
    27  import javax.portlet.ActionResponse;
    28  import javax.portlet.PortletException;
    29  import javax.portlet.PortletRequestDispatcher;
    30  import javax.servlet.ServletContext;
    31  import javax.servlet.http.HttpServletRequest;
    32  import javax.servlet.http.HttpServletResponse;
    33
    34  import org.apache.struts2.ServletActionContext;
    35  import org.apache.struts2.dispatcher.StrutsResultSupport;
    36  import org.apache.struts2.portlet.PortletActionConstants;
    37  import org.apache.struts2.portlet.context.PortletActionContext;
    38  import org.apache.struts2.views.util.ResourceUtil;
    39
    40  import com.opensymphony.xwork2.ActionInvocation;
    41  import com.opensymphony.xwork2.inject.Inject;
    42  import com.opensymphony.xwork2.util.ValueStack;
    43
    44  import freemarker.template.Configuration;
    45  import freemarker.template.ObjectWrapper;
    46  import freemarker.template.Template;
    47  import freemarker.template.TemplateException;
    48  import freemarker.template.TemplateModel;
    49  import freemarker.template.TemplateModelException;
    50
    51  /***
    52   */
    53  public class PortletFreemarkerResult extends StrutsResultSupport {
    54
    55      private static final long serialVersionUID = -5570612389289887543L;
    56
    57      protected ActionInvocation invocation;
    58
    59      protected Configuration configuration;
    60
    61      protected ObjectWrapper wrapper;
    62      protected FreemarkerManager freemarkerManager;
    63
    64      /*
    65       * Struts results are constructed for each result execeution
    66       *
    67       * the current context is availible to subclasses via these protected fields
    68       */
    69      protected String location;
    70
    71      private String pContentType = "text/html";
    72
    73      public PortletFreemarkerResult() {
    74          super();
    75      }
    76
    77      public PortletFreemarkerResult(String location) {
    78          super(location);
    79      }
    80
    81      @Inject
    82      public void setFreemarkerManager(FreemarkerManager mgr) {
    83          this.freemarkerManager = mgr;
    84      }
    85
    86      public void setContentType(String aContentType) {
    87          pContentType = aContentType;
    88      }
    89
    90      /***
    91       * allow parameterization of the contentType the default being text/html
    92       */
    93      public String getContentType() {
    94          return pContentType;
    95      }
    96
    97      /***
    98       * Execute this result, using the specified template location. <p/>The
    99       * template location has already been interoplated for any variable
    100      * substitutions <p/>this method obtains the freemarker configuration and
    101      * the object wrapper from the provided hooks. It them implements the
    102      * template processing workflow by calling the hooks for preTemplateProcess
    103      * and postTemplateProcess
    104      */
    105     public void doExecute(String location, ActionInvocation invocation)
    106             throws IOException, TemplateException, PortletException {
    107         if (PortletActionContext.isEvent()) {
    108             executeActionResult(location, invocation);
    109         } else if (PortletActionContext.isRender()) {
    110             executeRenderResult(location, invocation);
    111         }
    112     }
    113
    114     /***
    115      * @param location
    116      * @param invocation
    117      */
    118     private void executeActionResult(String location,
    119                                      ActionInvocation invocation) {
    120         ActionResponse res = PortletActionContext.getActionResponse();
    121         // View is rendered outside an action...uh oh...
    122         res.setRenderParameter(PortletActionConstants.ACTION_PARAM, "freemarkerDirect");
    123         res.setRenderParameter("location", location);
    124         res.setRenderParameter(PortletActionConstants.MODE_PARAM, PortletActionContext
    125                 .getRequest().getPortletMode().toString());
    126
    127     }
    128
    129     /***
    130      * @param location
    131      * @param invocation
    132      * @throws TemplateException
    133      * @throws IOException
    134      * @throws TemplateModelException
    135      */
    136     private void executeRenderResult(String location,
    137                                      ActionInvocation invocation) throws TemplateException, IOException,
    138             TemplateModelException, PortletException {
    139         prepareServletActionContext();
    140         this.location = location;
    141         this.invocation = invocation;
    142         this.configuration = getConfiguration();
    143         this.wrapper = getObjectWrapper();
    144
    145         HttpServletRequest req = ServletActionContext.getRequest();
    146
    147         if (!location.startsWith("/")) {
    148             String base = ResourceUtil.getResourceBase(req);
    149             location = base + "/" + location;
    150         }
    151
    152         Template template = configuration.getTemplate(location, deduceLocale());
    153         TemplateModel model = createModel();
    154         // Give subclasses a chance to hook into preprocessing
    155         if (preTemplateProcess(template, model)) {
    156             try {
    157                 // Process the template
    158                 PortletActionContext.getRenderResponse().setContentType(pContentType);
    159                 template.process(model, getWriter());
    160             } finally {
    161                 // Give subclasses a chance to hook into postprocessing
    162                 postTemplateProcess(template, model);
    163             }
    164         }
    165     }
    166
    167     /***
    168      *
    169      */
    170     private void prepareServletActionContext() throws PortletException,
    171             IOException {
    172         PortletRequestDispatcher disp = PortletActionContext.getPortletConfig()
    173                 .getPortletContext().getNamedDispatcher("preparator");
    174         disp.include(PortletActionContext.getRenderRequest(),
    175                 PortletActionContext.getRenderResponse());
    176     }
    177
    178     /***
    179      * This method is called from {@link #doExecute(String, ActionInvocation)}
    180      * to obtain the FreeMarker configuration object that this result will use
    181      * for template loading. This is a hook that allows you to custom-configure
    182      * the configuration object in a subclass, or to fetch it from an IoC
    183      * container. <p/><b>The default implementation obtains the configuration
    184      * from the ConfigurationManager instance. </b>
    185      */
    186     protected Configuration getConfiguration() throws TemplateException {
    187         return freemarkerManager.getConfiguration(
    188                 ServletActionContext.getServletContext());
    189     }
    190
    191     /***
    192      * This method is called from {@link #doExecute(String, ActionInvocation)}
    193      * to obtain the FreeMarker object wrapper object that this result will use
    194      * for adapting objects into template models. This is a hook that allows you
    195      * to custom-configure the wrapper object in a subclass. <p/><b>The default
    196      * implementation returns {@link Configuration#getObjectWrapper()}</b>
    197      */
    198     protected ObjectWrapper getObjectWrapper() {
    199         return configuration.getObjectWrapper();
    200     }
    201
    202     /***
    203      * The default writer writes directly to the response writer.
    204      */
    205     protected Writer getWriter() throws IOException {
    206         return PortletActionContext.getRenderResponse().getWriter();
    207     }
    208
    209     /***
    210      * Build the instance of the ScopesHashModel, including JspTagLib support
    211      * <p/>Objects added to the model are <p/>
    212      * <ul>
    213      * <li>Application - servlet context attributes hash model
    214      * <li>JspTaglibs - jsp tag lib factory model
    215      * <li>Request - request attributes hash model
    216      * <li>Session - session attributes hash model
    217      * <li>request - the HttpServletRequst object for direct access
    218      * <li>response - the HttpServletResponse object for direct access
    219      * <li>stack - the OgnLValueStack instance for direct access
    220      * <li>ognl - the instance of the OgnlTool
    221      * <li>action - the action itself
    222      * <li>exception - optional : the JSP or Servlet exception as per the
    223      * servlet spec (for JSP Exception pages)
    224      * <li>struts - instance of the StrutsUtil class
    225      * </ul>
    226      */
    227     protected TemplateModel createModel() throws TemplateModelException {
    228         ServletContext servletContext = ServletActionContext
    229                 .getServletContext();
    230         HttpServletRequest request = ServletActionContext.getRequest();
    231         HttpServletResponse response = ServletActionContext.getResponse();
    232         ValueStack stack = ServletActionContext.getContext()
    233                 .getValueStack();
    234         return freemarkerManager.buildTemplateModel(stack,
    235                 invocation.getAction(), servletContext, request, response,
    236                 wrapper);
    237     }
    238
    239     /***
    240      * Returns the locale used for the
    241      * {@link Configuration#getTemplate(String, Locale)}call. The base
    242      * implementation simply returns the locale setting of the configuration.
    243      * Override this method to provide different behaviour,
    244      */
    245     protected Locale deduceLocale() {
    246         return configuration.getLocale();
    247     }
    248
    249     /***
    250      * the default implementation of postTemplateProcess applies the contentType
    251      * parameter
    252      */
    253     protected void postTemplateProcess(Template template, TemplateModel data)
    254             throws IOException {
    255     }
    256
    257     /***
    258      * Called before the execution is passed to template.process(). This is a
    259      * generic hook you might use in subclasses to perform a specific action
    260      * before the template is processed. By default does nothing. A typical
    261      * action to perform here is to inject application-specific objects into the
    262      * model root
    263      *
    264      * @return true to process the template, false to suppress template
    265      *         processing.
    266      */
    267     protected boolean preTemplateProcess(Template template, TemplateModel model)
    268             throws IOException {
    269         Object attrContentType = template.getCustomAttribute("content_type");
    270
    271         if (attrContentType != null) {
    272             ServletActionContext.getResponse().setContentType(
    273                     attrContentType.toString());
    274         } else {
    275             String contentType = getContentType();
    276
    277             if (contentType == null) {
    278                 contentType = "text/html";
    279             }
    280
    281             String encoding = template.getEncoding();
    282
    283             if (encoding != null) {
    284                 contentType = contentType + "; charset=" + encoding;
    285             }
    286
    287             ServletActionContext.getResponse().setContentType(contentType);
    288         }
    289
    290         return true;
    291     }
    292 }
    293