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

Save This Page
Home » struts-2.1.8.1-src » org.apache » struts2 » views » freemarker » [javadoc | source]