Save This Page
Home » struts-2.1.8.1-src » org.apache » struts2 » components » template » [javadoc | source]
    1   /*
    2    * $Id: FreemarkerTemplateEngine.java 802949 2009-08-10 22:40:51Z 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.components.template;
   23   
   24   import java.io.IOException;
   25   import java.io.Writer;
   26   import java.util;
   27   import java.util.concurrent.ConcurrentHashMap;
   28   import java.util.concurrent.CopyOnWriteArraySet;
   29   
   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.StrutsConstants;
   36   import org.apache.struts2.views.freemarker.FreemarkerManager;
   37   
   38   import com.opensymphony.xwork2.ActionContext;
   39   import com.opensymphony.xwork2.ActionInvocation;
   40   import com.opensymphony.xwork2.inject.Inject;
   41   import com.opensymphony.xwork2.util.ClassLoaderUtil;
   42   import com.opensymphony.xwork2.util.ValueStack;
   43   import com.opensymphony.xwork2.util.logging.Logger;
   44   import com.opensymphony.xwork2.util.logging.LoggerFactory;
   45   
   46   import freemarker.template.Configuration;
   47   import freemarker.template.SimpleHash;
   48   import freemarker.core.ParseException;
   49   
   50   /**
   51    * Freemarker based template engine.
   52    */
   53   public class FreemarkerTemplateEngine extends BaseTemplateEngine {
   54       static Class bodyContent = null;
   55       protected FreemarkerManager freemarkerManager;
   56   
   57       protected final Map<String, freemarker.template.Template> templates = new ConcurrentHashMap<String, freemarker.template.Template>();
   58       protected final Set<String> missingTemplates = new CopyOnWriteArraySet<String>();
   59       protected boolean freemarkerCaching = false;
   60   
   61       static {
   62           try {
   63               bodyContent = ClassLoaderUtil.loadClass("javax.servlet.jsp.tagext.BodyContent",
   64                       FreemarkerTemplateEngine.class);
   65           } catch (ClassNotFoundException e) {
   66               // this is OK -- this just means JSP isn't even being used here, which is perfectly fine.
   67               // we need this class in environments that use JSP to know when to wrap the writer
   68               // and ignore flush() calls. In JSP, it is illegal for a BodyContent writer to be flushed(),
   69               // so we have to take caution here.
   70           }
   71       }
   72   
   73       private static final Logger LOG = LoggerFactory.getLogger(FreemarkerTemplateEngine.class);
   74   
   75       @Inject
   76       public void setFreemarkerManager(FreemarkerManager mgr) {
   77           this.freemarkerManager = mgr;
   78       }
   79       
   80       public void renderTemplate(TemplateRenderingContext templateContext) throws Exception {
   81       	// get the various items required from the stack
   82           ValueStack stack = templateContext.getStack();
   83           Map context = stack.getContext();
   84           ServletContext servletContext = (ServletContext) context.get(ServletActionContext.SERVLET_CONTEXT);
   85           HttpServletRequest req = (HttpServletRequest) context.get(ServletActionContext.HTTP_REQUEST);
   86           HttpServletResponse res = (HttpServletResponse) context.get(ServletActionContext.HTTP_RESPONSE);
   87   
   88           // prepare freemarker
   89           Configuration config = freemarkerManager.getConfiguration(servletContext);
   90   
   91           // get the list of templates we can use
   92           List<Template> templates = templateContext.getTemplate().getPossibleTemplates(this);
   93   
   94           // find the right template
   95           freemarker.template.Template template = null;
   96           String templateName = null;
   97           Exception exception = null;
   98           for (Template t : templates) {
   99               templateName = getFinalTemplateName(t);
  100               if (freemarkerCaching) {
  101                   if (!isTemplateMissing(templateName)) {
  102                       try {
  103                           template = findInCache(templateName);  // look in cache first
  104                           if (template == null) {
  105                               // try to load, and if it works, stop at the first one
  106                               template = config.getTemplate(templateName);
  107                               addToCache(templateName, template);
  108                           }
  109                           break;
  110                       } catch (IOException e) {
  111                           addToMissingTemplateCache(templateName);
  112                           if (exception == null) {
  113                               exception = e;
  114                           }
  115                       }
  116                   }
  117               } else {
  118                   try {
  119                       // try to load, and if it works, stop at the first one
  120                       template = config.getTemplate(templateName);
  121                       break;
  122                   } catch (ParseException e) {
  123                       // template was found but was invalid - always report this.
  124                       exception = e;
  125                       break;
  126                   } catch (IOException e) {
  127                       // FileNotFoundException is anticipated - report the first IOException if no template found
  128                       if (exception == null) {
  129                           exception = e;
  130                       }
  131                   }
  132               }
  133           }
  134   
  135           if (template == null) {
  136               if (LOG.isErrorEnabled()) {
  137                   LOG.error("Could not load the FreeMarker template named '" + templateContext.getTemplate().getName() +"':");
  138                   for (Template t : templates) {
  139                       LOG.error("Attempted: " + getFinalTemplateName(t));
  140                   }
  141                   LOG.error("The TemplateLoader provided by the FreeMarker Configuration was a: "+config.getTemplateLoader().getClass().getName());
  142               }
  143               if (exception != null) {
  144                   throw exception;
  145               } else {
  146                   return;
  147               }
  148           }
  149   
  150           if (LOG.isDebugEnabled()) {
  151               LOG.debug("Rendering template " + templateName);
  152           }
  153   
  154           ActionInvocation ai = ActionContext.getContext().getActionInvocation();
  155   
  156           Object action = (ai == null) ? null : ai.getAction();
  157           SimpleHash model = freemarkerManager.buildTemplateModel(stack, action, servletContext, req, res, config.getObjectWrapper());
  158   
  159           model.put("tag", templateContext.getTag());
  160           model.put("themeProperties", getThemeProps(templateContext.getTemplate()));
  161   
  162           // the BodyContent JSP writer doesn't like it when FM flushes automatically --
  163           // so let's just not do it (it will be flushed eventually anyway)
  164           Writer writer = templateContext.getWriter();
  165           final Writer wrapped = writer;
  166           writer = new Writer() {
  167               public void write(char cbuf[], int off, int len) throws IOException {
  168                   wrapped.write(cbuf, off, len);
  169               }
  170   
  171               public void flush() throws IOException {
  172                   // nothing!
  173               }
  174   
  175               public void close() throws IOException {
  176                   wrapped.close();
  177               }
  178           };
  179   
  180           try {
  181               stack.push(templateContext.getTag());
  182               template.process(model, writer);
  183           } finally {
  184               stack.pop();
  185           }
  186       }
  187   
  188       protected String getSuffix() {
  189           return "ftl";
  190       }
  191   
  192       protected void addToMissingTemplateCache(String templateName) {
  193           missingTemplates.add(templateName);
  194       }
  195       
  196       protected boolean isTemplateMissing(String templateName) {
  197           return missingTemplates.contains(templateName);
  198       }
  199   
  200       protected void addToCache(String templateName,
  201           freemarker.template.Template template) {
  202           templates.put(templateName, template);
  203       }
  204       
  205       protected freemarker.template.Template findInCache(String templateName) {
  206           return templates.get(templateName);
  207       }
  208       
  209       /**
  210        * Enables or disables Struts caching of Freemarker templates. By default disabled.
  211        * Set struts.freemarker.templatesCache=true to enable cache
  212        * @param cacheTemplates "true" if the template engine should cache freemarker template
  213        * internally
  214        */
  215       @Inject(StrutsConstants.STRUTS_FREEMARKER_TEMPLATES_CACHE)
  216       public void setCacheTemplates(String cacheTemplates) {
  217           freemarkerCaching = "true".equals(cacheTemplates);
  218       }
  219   
  220       /**
  221        * Clear the templates and missing templates cache
  222        */
  223       public void clearTemplatesCache() {
  224           templates.clear();
  225           missingTemplates.clear();
  226       }
  227   }

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