View Javadoc

1   /**
2    * Copyright (C) 2005-2009 Alfresco Software Limited.
3    *
4    * This file is part of the Spring Surf Extension project.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *  http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.springframework.extensions.webscripts.processor;
20  
21  import java.io.FileNotFoundException;
22  import java.io.IOException;
23  import java.io.StringReader;
24  import java.io.Writer;
25  
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  import org.springframework.extensions.webscripts.WebScriptException;
29  
30  import freemarker.cache.MruCacheStorage;
31  import freemarker.template.Configuration;
32  import freemarker.template.Template;
33  import freemarker.template.TemplateExceptionHandler;
34  
35  /**
36   * FTL Template Processor for Alfresco Web Framework
37   *  
38   * @author davidc
39   * @author kevinr
40   */
41  public class FTLTemplateProcessor extends AbstractTemplateProcessor
42  {
43      private static final Log logger = LogFactory.getLog(FTLTemplateProcessor.class);
44  
45      /** Default template input encoding */
46      private String defaultEncoding;
47      
48      /** FreeMarker config for templates */
49      private Configuration templateConfig;
50      
51      /** FreeMarker config for string based generated template */
52      private Configuration stringConfig;
53          
54      /** Time in seconds between FreeMarker checking for new template instances */
55      private int updateDelay = 0;
56      
57      /** Size of the FreeMarker in-memory template cache */
58      private int cacheSize = 256;
59  
60      /**
61       * @param defaultEncoding
62       */
63      public void setDefaultEncoding(String defaultEncoding)
64      {
65          this.defaultEncoding = defaultEncoding;
66      }
67          
68      /* (non-Javadoc)
69       * @see org.alfresco.web.scripts.TemplateProcessor#getDefaultEncoding()
70       */
71      public String getDefaultEncoding()
72      {
73          return this.defaultEncoding;
74      }
75      
76      /**
77       * @param updateDelay the time in seconds between checks on the modified date for cached templates
78       */
79      public void setUpdateDelay(int updateDelay)
80      {
81          this.updateDelay = updateDelay;
82      }
83      
84      /**
85       * @param cacheSize the size of the MRU template cache, default is 256
86       */
87      public void setCacheSize(int cacheSize)
88      {
89          if (cacheSize >= 0)
90          {
91              this.cacheSize = cacheSize;
92          }
93      }
94      
95      /* (non-Javadoc)
96       * @see org.alfresco.web.scripts.processor.AbstractTemplateProcessor#init()
97       */
98      public void init()
99      {
100         super.init();
101         
102         this.initConfig();        
103     }
104     
105     /* (non-Javadoc)
106      * @see org.alfresco.processor.Processor#getExtension()
107      */
108     public String getExtension()
109     {
110         return "ftl";
111     }
112 
113     /* (non-Javadoc)
114      * @see org.alfresco.processor.Processor#getName()
115      */
116     public String getName()
117     {
118         return "freemarker";
119     }
120 
121     /* (non-Javadoc)
122      * @see org.alfresco.web.scripts.TemplateProcessor#process(java.lang.String, java.lang.Object, java.io.Writer)
123      */
124     public void process(String template, Object model, Writer out)
125     {
126         if (template == null || template.length() == 0)
127         {
128             throw new IllegalArgumentException("Template name is mandatory.");
129         }
130         if (model == null)
131         {
132             throw new IllegalArgumentException("Model is mandatory.");
133         }
134         if (out == null)
135         {
136             throw new IllegalArgumentException("Output Writer is mandatory.");
137         }
138         
139         try
140         {
141             long startTime = 0;
142             if (logger.isDebugEnabled())
143             {
144                 logger.debug("Executing template: " + template);
145                 startTime = System.nanoTime();
146             }
147             
148             addProcessorModelExtensions(model);
149             
150             Template t = templateConfig.getTemplate(template);
151             if (t != null)
152             {
153                 try
154                 {
155                     // perform the template processing against supplied data model
156                     t.process(model, out);
157                 }
158                 catch (Throwable err)
159                 {
160                     throw new WebScriptException("Failed to process template " + template, err);
161                 }
162             }
163             else
164             {
165                 throw new WebScriptException("Cannot find template " + template);
166             }
167             
168             if (logger.isDebugEnabled())
169             {
170                 long endTime = System.nanoTime();
171                 logger.debug("Time to execute template: " + (endTime - startTime)/1000000f + "ms");
172             }
173         }
174         catch (IOException ioerr)
175         {
176             throw new WebScriptException("Failed to process template " + template, ioerr);
177         }
178     }
179 
180     /* (non-Javadoc)
181      * @see org.alfresco.web.scripts.TemplateProcessor#processString(java.lang.String, java.lang.Object, java.io.Writer)
182      */
183     public void processString(String template, Object model, Writer out)
184     {
185         if (template == null || template.length() == 0)
186         {
187             throw new IllegalArgumentException("Template is mandatory.");
188         }
189         if (model == null)
190         {
191             throw new IllegalArgumentException("Model is mandatory.");
192         }
193         if (out == null)
194         {
195             throw new IllegalArgumentException("Output Writer is mandatory.");
196         }
197         
198         long startTime = 0;
199         if (logger.isDebugEnabled())
200         {
201             logger.debug("Executing template: " + template);
202             startTime = System.nanoTime();
203         }
204         
205         addProcessorModelExtensions(model);
206         
207         try
208         {
209             Template t = new Template("name", new StringReader(template), stringConfig);
210             t.process(model, out);
211             
212             if (logger.isDebugEnabled())
213             {
214                 long endTime = System.nanoTime();
215                 logger.debug("Time to execute template: " + (endTime - startTime)/1000000f + "ms");
216             }
217         }
218         catch (Throwable err)
219         {
220             throw new WebScriptException("Failed to process template " + template, err);
221         }
222     }
223 
224     /* (non-Javadoc)
225      * @see org.alfresco.web.scripts.TemplateProcessor#reset()
226      */
227     public void reset()
228     {
229         this.init();
230         
231         if (templateConfig != null)
232         {
233             templateConfig.clearTemplateCache();
234         }
235     }
236 
237     /* (non-Javadoc)
238      * @see org.alfresco.web.scripts.TemplateProcessor#hasTemplate(java.lang.String)
239      */
240     public boolean hasTemplate(String templatePath)
241     {
242         boolean hasTemplate = false;
243         try
244         {
245             Template template = templateConfig.getTemplate(templatePath);
246             hasTemplate = (template != null);
247         }
248         catch(FileNotFoundException e)
249         {
250             // NOTE: return false as template is not found
251         }
252         catch(IOException e)
253         {
254             throw new WebScriptException("Failed to retrieve template " + templatePath, e);
255         }
256         return hasTemplate;
257     }
258     
259     /**
260      * Initialise FreeMarker Configuration
261      */
262     protected void initConfig()
263     {
264         // construct template config
265         Configuration config = new Configuration();
266         config.setCacheStorage(new MruCacheStorage(cacheSize, cacheSize << 1));
267         config.setTemplateUpdateDelay(updateDelay);
268         config.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
269         config.setLocalizedLookup(false);
270         config.setOutputEncoding("UTF-8");
271         if (defaultEncoding != null)
272         {
273             config.setDefaultEncoding(defaultEncoding);
274         }
275         
276         if (getTemplateLoader() != null)
277         {
278             config.setTemplateLoader(getTemplateLoader());
279         }
280                 
281         templateConfig = config;
282         
283         // construct string config
284         stringConfig = new Configuration();
285         stringConfig.setCacheStorage(new MruCacheStorage(2, 0));
286         stringConfig.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
287         stringConfig.setOutputEncoding("UTF-8");
288         if (defaultEncoding != null)
289         {
290             stringConfig.setDefaultEncoding(defaultEncoding);
291         }
292     }
293 }