Save This Page
Home » struts-2.1.8.1-src » org.apache » struts2 » views » freemarker » [javadoc | source]
    1   /*
    2    * $Id: FreemarkerManager.java 719672 2008-11-21 18:57:55Z 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.File;
   25   import java.io.IOException;
   26   import java.io.InputStream;
   27   import java.util.Collections;
   28   import java.util.HashMap;
   29   import java.util.Map;
   30   import java.util.Properties;
   31   import java.util.Set;
   32   
   33   import javax.servlet.GenericServlet;
   34   import javax.servlet.ServletContext;
   35   import javax.servlet.http.HttpServletRequest;
   36   import javax.servlet.http.HttpServletResponse;
   37   import javax.servlet.http.HttpSession;
   38   
   39   import org.apache.struts2.StrutsConstants;
   40   import org.apache.struts2.views.JspSupportServlet;
   41   import org.apache.struts2.views.TagLibrary;
   42   import org.apache.struts2.views.util.ContextUtil;
   43   
   44   import com.opensymphony.xwork2.inject.Container;
   45   import com.opensymphony.xwork2.inject.Inject;
   46   import com.opensymphony.xwork2.util.FileManager;
   47   import com.opensymphony.xwork2.util.ValueStack;
   48   import com.opensymphony.xwork2.util.logging.Logger;
   49   import com.opensymphony.xwork2.util.logging.LoggerFactory;
   50   
   51   import freemarker.cache.FileTemplateLoader;
   52   import freemarker.cache.MultiTemplateLoader;
   53   import freemarker.cache.TemplateLoader;
   54   import freemarker.cache.WebappTemplateLoader;
   55   import freemarker.ext.beans.BeansWrapper;
   56   import freemarker.ext.jsp.TaglibFactory;
   57   import freemarker.ext.servlet.HttpRequestHashModel;
   58   import freemarker.ext.servlet.HttpRequestParametersHashModel;
   59   import freemarker.ext.servlet.HttpSessionHashModel;
   60   import freemarker.ext.servlet.ServletContextHashModel;
   61   import freemarker.template.ObjectWrapper;
   62   import freemarker.template.SimpleHash;
   63   import freemarker.template.TemplateException;
   64   import freemarker.template.TemplateExceptionHandler;
   65   import freemarker.template.TemplateModel;
   66   
   67   
   68   /**
   69    * Static Configuration Manager for the FreemarkerResult's configuration
   70    *
   71    * <p/>
   72    *
   73    * Possible extension points are :-
   74    * <ul>
   75    *   <li>createConfiguration method</li>
   76    *   <li>loadSettings method</li>
   77    *   <li>getTemplateLoader method</li>
   78    *   <li>populateContext method</li>
   79    * </ul>
   80    *
   81    * <p/>
   82    * <b> createConfiguration method </b><br/>
   83    * Create a freemarker Configuration.
   84    * <p/>
   85    *
   86    * <b> loadSettings method </b><br/>
   87    * Load freemarker settings, default to freemarker.properties (if found in classpath)
   88    * <p/>
   89    *
   90    * <b> getTemplateLoader method</b><br/>
   91    * create a freemarker TemplateLoader that loads freemarker template in the following order :-
   92    * <ol>
   93    *   <li>path defined in ServletContext init parameter named 'templatePath' or 'TemplatePath' (must be an absolute path)</li>
   94    *   <li>webapp classpath</li>
   95    *   <li>struts's static folder (under [STRUT2_SOURCE]/org/apache/struts2/static/</li>
   96    * </ol>
   97    * <p/>
   98    *
   99    * <b> populateContext method</b><br/>
  100    * populate the created model.
  101    *
  102    */
  103   public class FreemarkerManager {
  104   
  105       private static final Logger LOG = LoggerFactory.getLogger(FreemarkerManager.class);
  106       public static final String CONFIG_SERVLET_CONTEXT_KEY = "freemarker.Configuration";
  107       public static final String KEY_EXCEPTION = "exception";
  108   
  109       // coppied from freemarker servlet - since they are private
  110       protected static final String ATTR_APPLICATION_MODEL = ".freemarker.Application";
  111       protected static final String ATTR_JSP_TAGLIBS_MODEL = ".freemarker.JspTaglibs";
  112       protected static final String ATTR_REQUEST_MODEL = ".freemarker.Request";
  113       protected static final String ATTR_REQUEST_PARAMETERS_MODEL = ".freemarker.RequestParameters";
  114   
  115       // coppied from freemarker servlet - so that there is no dependency on it
  116       public static final String KEY_APPLICATION = "Application";
  117       public static final String KEY_REQUEST_MODEL = "Request";
  118       public static final String KEY_SESSION_MODEL = "Session";
  119       public static final String KEY_JSP_TAGLIBS = "JspTaglibs";
  120       public static final String KEY_REQUEST_PARAMETER_MODEL = "Parameters";
  121       
  122       protected String encoding;
  123       protected boolean altMapWrapper;
  124       protected boolean cacheBeanWrapper;
  125       protected int mruMaxStrongSize;
  126       protected Map<String,TagLibrary> tagLibraries;
  127       
  128       @Inject(StrutsConstants.STRUTS_I18N_ENCODING)
  129       public void setEncoding(String encoding) {
  130           this.encoding = encoding;
  131       }
  132       
  133       @Inject(StrutsConstants.STRUTS_FREEMARKER_WRAPPER_ALT_MAP)
  134       public void setWrapperAltMap(String val) {
  135           altMapWrapper = "true".equals(val);
  136       }
  137       
  138       @Inject(StrutsConstants.STRUTS_FREEMARKER_BEANWRAPPER_CACHE)
  139       public void setCacheBeanWrapper(String val) {
  140           cacheBeanWrapper = "true".equals(val);
  141       }
  142       
  143       @Inject(StrutsConstants.STRUTS_FREEMARKER_MRU_MAX_STRONG_SIZE)
  144       public void setMruMaxStrongSize(String size) {
  145           mruMaxStrongSize = Integer.parseInt(size);
  146       }
  147       
  148       @Inject
  149       public void setContainer(Container container) {
  150           Map<String,TagLibrary> map = new HashMap<String,TagLibrary>();
  151           Set<String> prefixes = container.getInstanceNames(TagLibrary.class);
  152           for (String prefix : prefixes) {
  153               map.put(prefix, container.getInstance(TagLibrary.class, prefix));
  154           }
  155           this.tagLibraries = Collections.unmodifiableMap(map);
  156       }
  157   
  158       public synchronized freemarker.template.Configuration getConfiguration(ServletContext servletContext) throws TemplateException {
  159           freemarker.template.Configuration config = (freemarker.template.Configuration) servletContext.getAttribute(CONFIG_SERVLET_CONTEXT_KEY);
  160   
  161           if (config == null) {
  162               config = createConfiguration(servletContext);
  163   
  164               // store this configuration in the servlet context
  165               servletContext.setAttribute(CONFIG_SERVLET_CONTEXT_KEY, config);
  166           }
  167           
  168           config.setWhitespaceStripping(true);
  169   
  170           return config;
  171       }
  172   
  173       protected ScopesHashModel buildScopesHashModel(ServletContext servletContext, HttpServletRequest request, HttpServletResponse response, ObjectWrapper wrapper, ValueStack stack) {
  174           ScopesHashModel model = new ScopesHashModel(wrapper, servletContext, request, stack);
  175   
  176           // Create hash model wrapper for servlet context (the application)
  177           // only need one thread to do this once, per servlet context
  178           synchronized (servletContext) {
  179               ServletContextHashModel servletContextModel = (ServletContextHashModel) servletContext.getAttribute(ATTR_APPLICATION_MODEL);
  180   
  181               if (servletContextModel == null) {
  182   
  183                   GenericServlet servlet = JspSupportServlet.jspSupportServlet;
  184                   // TODO if the jsp support  servlet isn't load-on-startup then it won't exist
  185                   // if it hasn't been accessed, and a JSP page is accessed
  186                   if (servlet != null) {
  187                       servletContextModel = new ServletContextHashModel(servlet, wrapper);
  188                       servletContext.setAttribute(ATTR_APPLICATION_MODEL, servletContextModel);
  189                       TaglibFactory taglibs = new TaglibFactory(servletContext);
  190                       servletContext.setAttribute(ATTR_JSP_TAGLIBS_MODEL, taglibs);
  191                   }
  192   
  193               }
  194   
  195               model.put(KEY_APPLICATION, servletContextModel);
  196               model.put(KEY_JSP_TAGLIBS, (TemplateModel) servletContext.getAttribute(ATTR_JSP_TAGLIBS_MODEL));
  197           }
  198   
  199           // Create hash model wrapper for session
  200           HttpSession session = request.getSession(false);
  201           if (session != null) {
  202               model.put(KEY_SESSION_MODEL, new HttpSessionHashModel(session, wrapper));
  203           } else {
  204               // no session means no attributes ???
  205               //            model.put(KEY_SESSION_MODEL, new SimpleHash());
  206           }
  207   
  208           // Create hash model wrapper for the request attributes
  209           HttpRequestHashModel requestModel = (HttpRequestHashModel) request.getAttribute(ATTR_REQUEST_MODEL);
  210   
  211           if ((requestModel == null) || (requestModel.getRequest() != request)) {
  212               requestModel = new HttpRequestHashModel(request, response, wrapper);
  213               request.setAttribute(ATTR_REQUEST_MODEL, requestModel);
  214           }
  215   
  216           model.put(KEY_REQUEST_MODEL, requestModel);
  217   
  218   
  219           // Create hash model wrapper for request parameters
  220           HttpRequestParametersHashModel reqParametersModel = (HttpRequestParametersHashModel) request.getAttribute(ATTR_REQUEST_PARAMETERS_MODEL);
  221           if (reqParametersModel == null || requestModel.getRequest() != request) {
  222               reqParametersModel = new HttpRequestParametersHashModel(request);
  223               request.setAttribute(ATTR_REQUEST_PARAMETERS_MODEL, reqParametersModel);
  224           }
  225           model.put(KEY_REQUEST_PARAMETER_MODEL, reqParametersModel);
  226   
  227           return model;
  228       }
  229   
  230       protected void populateContext(ScopesHashModel model, ValueStack stack, Object action, HttpServletRequest request, HttpServletResponse response) {
  231           // put the same objects into the context that the velocity result uses
  232           Map standard = ContextUtil.getStandardContext(stack, request, response);
  233           model.putAll(standard);
  234   
  235           // support for JSP exception pages, exposing the servlet or JSP exception
  236           Throwable exception = (Throwable) request.getAttribute("javax.servlet.error.exception");
  237   
  238           if (exception == null) {
  239               exception = (Throwable) request.getAttribute("javax.servlet.error.JspException");
  240           }
  241   
  242           if (exception != null) {
  243               model.put(KEY_EXCEPTION, exception);
  244           }
  245       }
  246   
  247       protected BeansWrapper getObjectWrapper() {
  248           StrutsBeanWrapper wrapper = new StrutsBeanWrapper(altMapWrapper);
  249           wrapper.setUseCache(cacheBeanWrapper);
  250           return wrapper;
  251       }
  252   
  253       /**
  254        * The default template loader is a MultiTemplateLoader which includes
  255        * a ClassTemplateLoader and a WebappTemplateLoader (and a FileTemplateLoader depending on
  256        * the init-parameter 'TemplatePath').
  257        * <p/>
  258        * The ClassTemplateLoader will resolve fully qualified template includes
  259        * that begin with a slash. for example /com/company/template/common.ftl
  260        * <p/>
  261        * The WebappTemplateLoader attempts to resolve templates relative to the web root folder
  262        */
  263       protected TemplateLoader getTemplateLoader(ServletContext servletContext) {
  264           // construct a FileTemplateLoader for the init-param 'TemplatePath'
  265           FileTemplateLoader templatePathLoader = null;
  266   
  267           String templatePath = servletContext.getInitParameter("TemplatePath");
  268           if (templatePath == null) {
  269               templatePath = servletContext.getInitParameter("templatePath");
  270           }
  271   
  272           if (templatePath != null) {
  273               try {
  274                   templatePathLoader = new FileTemplateLoader(new File(templatePath));
  275               } catch (IOException e) {
  276                   LOG.error("Invalid template path specified: " + e.getMessage(), e);
  277               }
  278           }
  279   
  280           // presume that most apps will require the class and webapp template loader
  281           // if people wish to
  282           return templatePathLoader != null ?
  283                   new MultiTemplateLoader(new TemplateLoader[]{
  284                           templatePathLoader,
  285                           new WebappTemplateLoader(servletContext),
  286                           new StrutsClassTemplateLoader()
  287                   })
  288                   : new MultiTemplateLoader(new TemplateLoader[]{
  289                   new WebappTemplateLoader(servletContext),
  290                   new StrutsClassTemplateLoader()
  291           });
  292       }
  293   
  294       /**
  295        * Create the instance of the freemarker Configuration object.
  296        * <p/>
  297        * this implementation
  298        * <ul>
  299        * <li>obtains the default configuration from Configuration.getDefaultConfiguration()
  300        * <li>sets up template loading from a ClassTemplateLoader and a WebappTemplateLoader
  301        * <li>sets up the object wrapper to be the BeansWrapper
  302        * <li>loads settings from the classpath file /freemarker.properties
  303        * </ul>
  304        *
  305        * @param servletContext
  306        */
  307       protected freemarker.template.Configuration createConfiguration(ServletContext servletContext) throws TemplateException {
  308           freemarker.template.Configuration configuration = new freemarker.template.Configuration();
  309   
  310           configuration.setTemplateLoader(getTemplateLoader(servletContext));
  311   
  312           configuration.setTemplateExceptionHandler(TemplateExceptionHandler.HTML_DEBUG_HANDLER);
  313   
  314           configuration.setObjectWrapper(getObjectWrapper());
  315           
  316           if( mruMaxStrongSize > 0 ) {
  317               configuration.setSetting(freemarker.template.Configuration.CACHE_STORAGE_KEY, "strong:" + mruMaxStrongSize);
  318           }
  319   
  320           if (encoding != null) {
  321               configuration.setDefaultEncoding(encoding);
  322           }
  323   
  324           loadSettings(servletContext, configuration);
  325   
  326           return configuration;
  327       }
  328   
  329       /**
  330        * Load the settings from the /freemarker.properties file on the classpath
  331        *
  332        * @see freemarker.template.Configuration#setSettings for the definition of valid settings
  333        */
  334       protected void loadSettings(ServletContext servletContext, freemarker.template.Configuration configuration) {
  335           InputStream in = null;
  336   
  337           try {
  338               in = FileManager.loadFile("freemarker.properties", FreemarkerManager.class);
  339   
  340               if (in != null) {
  341                   Properties p = new Properties();
  342                   p.load(in);
  343                   configuration.setSettings(p);
  344               }
  345           } catch (IOException e) {
  346               LOG.error("Error while loading freemarker settings from /freemarker.properties", e);
  347           } catch (TemplateException e) {
  348               LOG.error("Error while loading freemarker settings from /freemarker.properties", e);
  349           } finally {
  350               if (in != null) {
  351                   try {
  352                       in.close();
  353                   } catch(IOException io) {
  354                       LOG.warn("Unable to close input stream", io);
  355                   }
  356               }
  357           }
  358       }
  359   
  360       public SimpleHash buildTemplateModel(ValueStack stack, Object action, ServletContext servletContext, HttpServletRequest request, HttpServletResponse response, ObjectWrapper wrapper) {
  361           ScopesHashModel model = buildScopesHashModel(servletContext, request, response, wrapper, stack);
  362           populateContext(model, stack, action, request, response);
  363           if (tagLibraries != null) {
  364               for (String prefix : tagLibraries.keySet()) {
  365                   model.put(prefix, tagLibraries.get(prefix).getFreemarkerModels(stack, request, response));
  366               }
  367           }
  368           return model;
  369       }
  370   }

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