View Javadoc

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