View Javadoc

1   /*
2    * Copyright (c) 2005-2007 Creative Sphere Limited.
3    * All rights reserved. This program and the accompanying materials
4    * are made available under the terms of the Eclipse Public License v1.0
5    * which accompanies this distribution, and is available at
6    * http://www.eclipse.org/legal/epl-v10.html
7    *
8    * Contributors:
9    *
10   *   Creative Sphere - initial API and implementation
11   *
12   */
13  package org.abstracthorizon.danube.freemarker;
14  
15  import org.abstracthorizon.danube.connection.Connection;
16  import org.abstracthorizon.danube.connection.ConnectionException;
17  import org.abstracthorizon.danube.mvc.ModelAndView;
18  import org.abstracthorizon.danube.mvc.View;
19  import org.abstracthorizon.danube.support.RuntimeIOException;
20  import org.abstracthorizon.danube.support.URLUtils;
21  
22  import java.io.File;
23  import java.io.FileReader;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.io.InputStreamReader;
27  import java.io.Reader;
28  import java.io.Writer;
29  import java.net.URL;
30  import java.net.URLConnection;
31  
32  import org.slf4j.Logger;
33  import org.slf4j.LoggerFactory;
34  
35  import freemarker.cache.TemplateLoader;
36  import freemarker.template.Configuration;
37  import freemarker.template.DefaultObjectWrapper;
38  import freemarker.template.Template;
39  import freemarker.template.TemplateException;
40  import freemarker.template.TemplateExceptionHandler;
41  
42  /**
43   * <p>This is implementation of {@link org.abstracthorizon.danube.mvc.View}
44   * that uses Velocity template engine. Model map is used as map of parameters for
45   * Velocity.
46   * </p>
47   *
48   * @author Daniel Sendula
49   */
50  public class FreeMarkerViewAdapter implements View {
51  
52      /** Logger */
53      protected Logger logger = LoggerFactory.getLogger(getClass());
54  
55      /** Default pages suffix is &quot;.page&quot; */
56      public static final String DEFAULT_SUFFIX = ".page";
57  
58      /** Path where templates are stored */
59      protected File templatesPath;
60  
61      /** URL where templates are stored */
62      protected URL templatesURL;
63  
64      /** Suffix to be used for templates */
65      protected String suffix = DEFAULT_SUFFIX;
66  
67      /** Configuration */
68      protected Configuration configuration;
69  
70      /**
71       * Constructor.
72       */
73      public FreeMarkerViewAdapter() {
74      }
75  
76      /**
77       * This metod initialises Velocity engine.
78       * @throws RuntimeIOException
79       */
80      public void init() throws RuntimeIOException {
81  
82          configuration = new Configuration();
83          configuration.setTemplateLoader(createTemplateLoader());
84          DefaultObjectWrapper defaultObjectWrapper = new DefaultObjectWrapper();
85          configuration.setObjectWrapper(defaultObjectWrapper);
86          configuration.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);
87      }
88  
89      protected TemplateLoader createTemplateLoader() throws RuntimeIOException {
90          return new LocalTemplateLoader();
91      }
92  
93      /**
94       * This method renders output
95       * @param connection http connection
96       * @param modelAndView view name to be used as template's name and model map to be used as attributes
97       * @throws ConnectionException
98       */
99      public void render(Connection connection, ModelAndView modelAndView) throws ConnectionException {
100 
101         try {
102             Template template = configuration.getTemplate(makeTemplateName(modelAndView.getView()));
103             Writer writer = (Writer)connection.adapt(Writer.class);
104 
105             template.process(modelAndView.getModel(), writer);
106         } catch (TemplateException templateException) {
107             throw new ConnectionException(templateException);
108         } catch (IOException e) {
109             throw new RuntimeIOException(e);
110         }
111 
112         //out.flush();
113     }
114 
115     /**
116      * Returns path where templates are stored
117      * @return Returns the templatePath.
118      */
119     public File getTemplatesPath() {
120         return templatesPath;
121     }
122 
123     /**
124      * Sets path where templates are stored. Note: only a file or URL need to be set, not both.
125      * @param templatesPath path where templates are stored.
126      */
127     public void setTemplatesPath(File templatesPath) {
128         this.templatesPath = templatesPath;
129         if (configuration != null) {
130             configuration.setTemplateLoader(createTemplateLoader());
131         }
132     }
133 
134     /**
135      * Returns URL where templaes are to be read from
136      * @return URL where templaes are to be read from
137      */
138     public URL getTemplatesURL() {
139         return templatesURL;
140     }
141 
142     /**
143      * Sets URL templates are to be read from. Note: only a file or URL need to be set, not both.
144      * @param templatesURL URL templates are to be read from
145      */
146     public void setTemplatesURL(URL templatesURL) {
147         this.templatesURL = templatesURL;
148         if (templatesURL.getProtocol().equals("file")) {
149             templatesPath = new File(templatesURL.getFile());
150         }
151     }
152 
153     /**
154      * Creates name of the template
155      * @param view name of the view
156      * @return returns template's name
157      */
158     protected String makeTemplateName(String view) {
159         if (suffix == null) {
160             return view;
161         } else {
162             return view + suffix;
163         }
164     }
165 
166     /**
167      * Returns suffix of the templates
168      * @return suffix
169      */
170     public String getSuffix() {
171         return suffix;
172     }
173 
174     /**
175      * Sets suffix of the templates
176      * @param suffix suffix
177      */
178     public void setSuffix(String suffix) {
179         if (suffix.startsWith(".")) {
180             this.suffix = suffix;
181         } else {
182             this.suffix = "." + suffix;
183         }
184     }
185 
186     /**
187      * Local template loader that uses templates path or templates URL (which ever is supplied).
188      * Templates path has advantage against URL.
189      */
190     public class LocalTemplateLoader implements TemplateLoader {
191 
192         /** Constructor */
193         public LocalTemplateLoader() {
194         }
195 
196         /**
197          * This method does nothing
198          * @param tmp template resource
199          * @throws IOException
200          */
201         public void closeTemplateSource(Object tmp) throws IOException {
202         }
203 
204         /**
205          * This method returns a file or an URL if file is not available and URL's input stream
206          * can be accessed (it tries to read ONE byte of it).
207          * @param path path to resource
208          * @return file, URL or <code>null</code>
209          * @throws IOException
210          */
211         public Object findTemplateSource(String path) throws IOException {
212             if (templatesPath != null) {
213                 File file = new File(templatesPath, path);
214                 if (file.exists()) {
215                     return file;
216                 }
217             }
218             if (templatesURL != null) {
219                 URL newURL = URLUtils.addPath(templatesURL, path);
220                 try {
221                     InputStream peek = newURL.openStream();
222                     peek.read();
223                     peek.close();
224                     return newURL;
225                 } catch (IOException ignore) {
226                 }
227             }
228 
229             return null;
230         }
231 
232         /**
233          * This method returns file's last modified attribute if file is
234          * supplied. If not it tries to invoke last modified on an URL.
235          * If that fails too then it returns -1
236          * @param tmp resource which has to be a file or URL.
237          * @return file's or url's last modified or -1
238          */
239         public long getLastModified(Object tmp) {
240             if (tmp instanceof File) {
241                 return ((File)tmp).lastModified();
242             } else if (tmp instanceof URL) {
243                 try {
244                     URLConnection connection = ((URL)tmp).openConnection();
245                     return connection.getLastModified();
246                 } catch (IOException ignore) {
247                 }
248             }
249 
250             return -1;
251         }
252 
253         /**
254          * Returns an reader from file or URL or <code>null</code> if that fails
255          * @param tmp file or URL
256          * @param encoding encoding
257          * @return file reader or input stream reader of URL's input stream or <code>null</code>
258          */
259         public Reader getReader(Object tmp, String encoding) throws IOException {
260             if (tmp instanceof File) {
261                 File file = (File)tmp;
262                 return new FileReader(file);
263             } else if (tmp instanceof URL) {
264                 URL url = (URL)tmp;
265                 return new InputStreamReader(url.openStream());
266             } else {
267                 return null;
268             }
269         }
270 
271     }
272 }