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

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