View Javadoc

1   /*
2    * Copyright (C) 2003-2004 Christian Siefkes <christian@siefkes.net>.
3    * Development of this software is supported by the German Research Society,
4    * Berlin-Brandenburg Graduate School in Distributed Information Systems
5    * (DFG grant no. GRK 316).
6    *
7    * This library is free software; you can redistribute it and/or
8    * modify it under the terms of the GNU Lesser General Public
9    * License as published by the Free Software Foundation; either
10   * version 2.1 of the License, or (at your option) any later version.
11   *
12   * This library is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15   * Lesser General Public License for more details.
16   *
17   * You should have received a copy of the GNU Lesser General Public
18   * License along with this library; if not, visit
19   * http://www.gnu.org/licenses/lgpl.html or write to the Free Software
20   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
21   */
22  package de.fu_berlin.ties.util;
23  
24  import java.io.File;
25  import java.io.FileWriter;
26  import java.io.IOException;
27  import java.io.OutputStreamWriter;
28  import java.io.Writer;
29  import java.util.ArrayList;
30  import java.util.HashMap;
31  import java.util.Iterator;
32  import java.util.List;
33  import java.util.Map;
34  import java.util.Properties;
35  import java.util.SortedSet;
36  
37  import org.apache.commons.lang.StringEscapeUtils;
38  import org.apache.velocity.VelocityContext;
39  import org.apache.velocity.app.Velocity;
40  import org.apache.velocity.context.Context;
41  import org.apache.velocity.exception.VelocityException;
42  import org.apache.velocity.runtime.RuntimeConstants;
43  
44  import de.fu_berlin.ties.TiesConfiguration;
45  import de.fu_berlin.ties.io.IOUtils;
46  
47  /***
48   * A static class that provides a convenience interface to a Velocity
49   * singletons. No instances of this class can be created, only the static
50   * members should be used.
51   *
52   * @author Christian Siefkes
53   * @version $Revision: 1.17 $, $Date: 2004/11/08 11:57:48 $, $Author: siefkes $
54   */
55  public final class VelocityService {
56  
57      /***
58       * The changes to the Velocity configuration made by default.
59       */
60      private static final Properties DEFAULT_PROPERTIES;
61  
62      /***
63       * The template directory.
64       */
65      public static final String TEMPLATE_DIR = "velocity" + File.separator;
66  
67      /***
68       * The extension of template files.
69       */
70      public static final String TEMPLATE_EXT = ".vm";
71  
72      /***
73       * Name of the default template used to print configuration parameters.
74       */
75      public static final String CONFIG_TEMPLATE = completeTemplateName("config");
76  
77      /***
78       * Name of the default template used to print goals.
79       */
80      public static final String GOAL_TEMPLATE = completeTemplateName("goals");
81  
82      /***
83       * Name of the main template used for Anakia XML format.
84       */
85      public static final String ANAKIA_TEMPLATE = completeTemplateName("xdoc");
86  
87      /***
88       * Static initializer populates the default properties and configures the
89       * Velocity singleton.
90       */
91      static {
92          // init and populate properties to change
93          DEFAULT_PROPERTIES = new Properties();
94          // load resources from both filesystem (default) and classpath
95          DEFAULT_PROPERTIES.setProperty(
96              RuntimeConstants.RESOURCE_LOADER, "file,classpath");
97          // configure file loader to use template caching (check every 2min)
98          DEFAULT_PROPERTIES.setProperty(
99              RuntimeConstants.FILE_RESOURCE_LOADER_CACHE, "true");
100         DEFAULT_PROPERTIES.setProperty(
101             "file.resource.loader.modificationCheckInterval", "120");
102         // specify classpath loader
103         DEFAULT_PROPERTIES.setProperty("classpath.resource.loader.class",
104             "org.apache.velocity.runtime.resource.loader."
105                 + "ClasspathResourceLoader");
106         DEFAULT_PROPERTIES.setProperty("classpath.resource.loader.description",
107             "Velocity Classpath Resource Loader");
108 
109         try {
110             /* use shared logger
111             Velocity.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM,
112                 Log.TIES);
113             Velocity.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS,
114                 "org.apache.velocity.runtime.log.AvalonLogSystem");
115             Velocity.setProperty("runtime.log.logsystem.avalon.logger",
116                 Log.TIES);
117             */
118             // configure with properties
119             Velocity.init(DEFAULT_PROPERTIES);
120         } catch (Exception e) {
121             Util.LOG.error(
122                 "VelocityService: could not initialize the Velocity singleton",
123                 e);
124         }
125     }
126 
127     /***
128      * Completes the short form of a template name, by prepending the
129      * {@link #TEMPLATE_DIR} and appending the {@link #TEMPLATE_EXT}.
130      *
131      * @param shortName the short form of the template name (without directory
132      * and extension)
133      * @return the full form of the template name, which is assumed to be
134      * in the default directory and have the default extension
135      */
136     public static String completeTemplateName(final String shortName) {
137         return TEMPLATE_DIR + shortName + TEMPLATE_EXT;
138     }
139 
140     /***
141      * Returns properties listing the changes to the Velocity configuration
142      * made by default.
143      *
144      * @return a {@link java.util.Properties} object containing the changed
145      * parameters
146      */
147     public static Properties getDefaultProperties() {
148         return DEFAULT_PROPERTIES;
149     }
150 
151     /***
152       * Main methods: prints the list of config parameters and
153       * {@linkplain #printGoals(TiesConfiguration, Writer) goals} from the
154       * {@link TiesConfiguration#CONF standard configuration} to specified
155       * files (or standard out). The generated files are in Anakia XML format
156       * and uses the platform's default character set.
157       *
158       * @param args the command-line arguments: should contain the filename
159       * for writing the config parameters as first element and the filename
160       * for writing the goals as second element; if no filenames are given, the
161       * corresponding output is written to standard out
162       */
163      public static void main(final String[] args) {
164          Writer confWriter = null;
165          Writer goalWriter = null;
166 
167          try {
168              // write config parameters
169              confWriter = (args.length > 0)
170                  ? new FileWriter(args[0]) // use first file name
171                  : new OutputStreamWriter(System.out); // write to stdout
172              final Map<String, Object> confContextObjects =
173                  new HashMap<String, Object>();
174              confContextObjects.put("contents", CONFIG_TEMPLATE);
175              confContextObjects.put("title", "TIE Configuration Parameters");
176              confContextObjects.put("keywords", "TIE, configuration, "
177                  + "configuration parameters, properties, default values");
178              printConfigProperties(TiesConfiguration.CONF, ANAKIA_TEMPLATE,
179                 IOUtils.STANDARD_UNICODE_CHARSET, confContextObjects,
180                     confWriter);
181 
182              // write goal processors
183              goalWriter = (args.length > 1)
184                  ? new FileWriter(args[1]) // use second file name
185                  : new OutputStreamWriter(System.out); // write to stdout
186              final Map<String, Object> goalContextObjects =
187                  new HashMap<String, Object>();
188              goalContextObjects.put("contents", GOAL_TEMPLATE);
189              goalContextObjects.put("title", "TIE Processing Goals");
190              goalContextObjects.put("keywords", "TIE, goals, "
191                  + "processors, processing goals, goal processors");
192              printGoals(TiesConfiguration.CONF, ANAKIA_TEMPLATE,
193                 IOUtils.STANDARD_UNICODE_CHARSET, goalContextObjects,
194                 goalWriter);
195          } catch (Exception e) {
196              e.printStackTrace();
197          } finally {
198              // close writers
199              IOUtils.tryToClose(confWriter);
200              IOUtils.tryToClose(goalWriter);
201          }
202      }
203 
204     /***
205      * Prints all properties contained in a configuration, using the
206      * specified template for rendering. In addition to the user-specified
207      * context objects (if any), the template has access to a collection
208      * <code>$allParameters</code> of all config parameters.
209      *
210      * @param config the configuration to print
211      * @param templateName name of template to be used in merge, either in
212      * short form (without directory and extension, when default directory and
213      * extension are used) or in complete form
214      * @param charset the character set used in the template
215      * @param contextObjects a map of objects that are made available in the
216      * context of the template; might be <code>null</code>
217      * @param writer the writer to write the output to; the writer is flushed
218      * but not closed by this method
219      * @throws IOException if an I/O error occurred
220      * @throws VelocityException if rendering failed
221      */
222     public static void printConfigProperties(final TiesConfiguration config,
223             final String templateName, final String charset,
224             final Map<String, Object> contextObjects, final Writer writer)
225             throws IOException, VelocityException {
226         // populate from user-specified map, if not null
227         final Map<String, Object> contextMap = (contextObjects == null)
228             ? new HashMap<String, Object>()
229             : new HashMap<String, Object>(contextObjects);
230 
231         // create and store descriptors for all key, incl. non-set keys
232         final SortedSet keySet = config.sortedKeys(true);
233         final Iterator keys = keySet.iterator();
234         final List<TiesConfiguration.EntryDescriptor> allParams =
235             new ArrayList<TiesConfiguration.EntryDescriptor>(keySet.size());
236         String key;
237         TiesConfiguration.EntryDescriptor descriptor;
238 
239         while (keys.hasNext()) {
240             key = (String) keys.next();
241             descriptor = config.getDescriptor(key);
242 
243             if (descriptor != null) {
244                 allParams.add(descriptor);
245             } else {
246                 Util.LOG.warn("Missing description for key " + key);
247             }
248         }
249 
250         contextMap.put("allParameters", allParams);
251         VelocityService.renderTemplate(templateName, charset, contextMap,
252             writer);
253     }
254 
255     /***
256      * Prints all properties contained in a configuration, using the
257      * default template for printing them. This templates generates an
258      * (X)HTML fragment listing all configuration parameters with their
259      * types and default values in a table.
260      *
261      * @param config the configuration to print
262      * @param writer the writer to write the output to; the writer is flushed
263      * but not closed by this method
264      * @throws IOException if an I/O error occurred
265      * @throws VelocityException if rendering failed
266      */
267     public static void printConfigProperties(final TiesConfiguration config,
268             final Writer writer) throws IOException, VelocityException {
269         printConfigProperties(config, CONFIG_TEMPLATE,
270             IOUtils.STANDARD_UNICODE_CHARSET, null, writer);
271     }
272 
273     /***
274      * Prints the goals contained in a configuration, using the default
275      * template for printing them. In addition to the user-specified
276      * context objects (if any), the template has access to a collection
277      * <code>$allParameters</code> of all goals.
278      *
279      * @param config the configuration to print
280      * @param templateName name of template to be used in merge, either in
281      * short form (without directory and extension, when default directory and
282      * extension are used) or in complete form
283      * @param charset the character set used in the template
284      * @param contextObjects a map of objects that are made available in the
285      * context of the template; might be <code>null</code>
286      * @param writer the writer to write the output to; the writer is flushed
287      * but not closed by this method
288      * @throws IOException if an I/O error occurred
289      * @throws VelocityException if rendering failed
290      */
291     public static void printGoals(final TiesConfiguration config,
292             final String templateName, final String charset,
293             final Map<String, Object> contextObjects, final Writer writer)
294             throws IOException, VelocityException {
295         final TiesConfiguration goalSubset = (TiesConfiguration)
296             config.subset(TiesConfiguration.CONFIG_GOAL_PREFIX);
297         printConfigProperties(goalSubset, templateName,
298             charset, contextObjects, writer);
299     }
300 
301     /***
302      * Prints the goals contained in a configuration, using the default
303      * template for printing them. This templates generates an (X)HTML fragment
304      * listing all goals with their output extension (if given) and any further
305      * arguments in a table.
306      *
307      * @param config the configuration to print
308      * @param writer the writer to write the output to; the writer is flushed
309      * but not closed by this method
310      * @throws IOException if an I/O error occurred
311      * @throws VelocityException if rendering failed
312      */
313     public static void printGoals(final TiesConfiguration config,
314             final Writer writer) throws IOException, VelocityException {
315         printGoals(config, GOAL_TEMPLATE, IOUtils.STANDARD_UNICODE_CHARSET,
316             null, writer);
317     }
318 
319     /***
320      * Renders a template using the given context objects. The template must
321      * be in the standard Unicode character set (UFT-8).
322      *
323      * @param templateName name of template to be used in merge, either in
324      * short form (without directory and extension, when default directory and
325      * extension are used) or in complete form
326      * @param contextObjects a map of objects that are made available in the
327      * context of the template; mappings from "escapeTool" to a
328      * {@link StringEscapeUtils} and from "util" to a {@link Util} are added to
329      * allow XML entity escaping, String conversion etc.
330      * @param writer writer to write template into; the writer is flushed
331      * but not closed by this method
332      * @throws IOException if an I/O error occurred
333      * @throws VelocityException if rendering failed
334      */
335     public static void renderTemplate(final String templateName,
336             final Map contextObjects, final Writer writer)
337             throws IOException, VelocityException {
338         renderTemplate(templateName, IOUtils.STANDARD_UNICODE_CHARSET,
339             contextObjects, writer);
340     }
341 
342     /***
343      * Renders a template using the given context objects.
344      *
345      * @param templateName name of template to be used in merge, either in
346      * short form (without directory and extension, when default directory and
347      * extension are used) or in complete form
348      * @param charset the character set used in template
349      * @param contextObjects a map of objects that are made available in the
350      * context of the template; mappings from "escapeTool" to a
351      * {@link StringEscapeUtils} and from "util" to a {@link Util} are added to
352      * allow XML entity escaping, String conversion etc.
353      * @param writer writer to write template into; the writer is flushed
354      * but not closed by this method
355      * @throws IOException if an I/O error occurred
356      * @throws VelocityException if rendering failed
357      */
358     public static void renderTemplate(final String templateName,
359             final String charset, final Map contextObjects, final Writer writer)
360             throws IOException, VelocityException {
361         final Context context = new VelocityContext(contextObjects);
362 
363         // "embed" specified name within default directory and extension
364         final String embeddedTemplateName = completeTemplateName(templateName);
365 
366         // use the embedded name if it exists, otherwise use the name "as is"
367         final String actualTemplateName =
368             Velocity.resourceExists(embeddedTemplateName)
369                 ? embeddedTemplateName : templateName;
370 
371         // add utilities
372         context.put("escapeTool", new StringEscapeUtils());
373         context.put("util", new Util());
374         boolean result = false;
375 
376         try {
377             result = Velocity.mergeTemplate(actualTemplateName, charset,
378                 context, writer);
379             writer.flush();
380         } catch (IOException ioe) {
381             throw ioe; // can rethow "as is"
382         } catch (VelocityException ve) {
383             throw ve; // can rethow "as is"
384         } catch (RuntimeException re) {
385             throw re; // can rethow "as is"
386         } catch (Exception e) {
387             // repackaging the exception in a more suitable type
388             final VelocityException ve = new VelocityException(
389                 "Exception during rendering: " + e.toString());
390             ve.initCause(e);
391             throw ve;
392         }
393 
394         // something went wrong, but we don't know what
395         if (!result) {
396             throw new VelocityException("Velocity could not render the template"
397                 + "-- check log for errors");
398         }
399     }
400 
401     /***
402      * Private constructor prevents creation of instances.
403      */
404     private VelocityService() {
405         super();
406     }
407 }