1 package org.andromda.templateengines.velocity;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.io.StringWriter;
7 import java.io.Writer;
8 import java.net.URL;
9 import java.util.ArrayList;
10 import java.util.Collection;
11 import java.util.HashMap;
12 import java.util.List;
13 import java.util.Map;
14 import java.util.Properties;
15 import org.andromda.core.common.AndroMDALogger;
16 import org.andromda.core.common.Constants;
17 import org.andromda.core.common.ExceptionUtils;
18 import org.andromda.core.common.Merger;
19 import org.andromda.core.common.ResourceUtils;
20 import org.andromda.core.common.ResourceWriter;
21 import org.andromda.core.templateengine.TemplateEngine;
22 import org.andromda.core.templateengine.TemplateEngineException;
23 import org.apache.commons.collections.ExtendedProperties;
24 import org.apache.commons.io.FileUtils;
25 import org.apache.commons.lang.StringUtils;
26 import org.apache.log4j.FileAppender;
27 import org.apache.log4j.Logger;
28 import org.apache.log4j.PatternLayout;
29 import org.apache.velocity.Template;
30 import org.apache.velocity.VelocityContext;
31 import org.apache.velocity.app.VelocityEngine;
32 import org.apache.velocity.runtime.RuntimeConstants;
33 import org.apache.velocity.runtime.log.Log4JLogChute;
34 import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
35 import org.apache.velocity.runtime.resource.loader.FileResourceLoader;
36 import org.apache.velocity.tools.generic.EscapeTool;
37
38
39
40
41
42
43
44
45 public class VelocityTemplateEngine
46 implements TemplateEngine
47 {
48
49
50
51 protected static Logger logger = null;
52
53
54
55
56 protected FileAppender appender = null;
57
58
59
60
61 private static final String PROPERTIES_DIR = "META-INF/";
62
63
64
65
66 private static final String PROPERTIES_SUFFIX = "-velocity.properties";
67
68
69
70
71 private static final String TEMPORARY_TEMPLATE_LOCATION = Constants.TEMPORARY_DIRECTORY + "velocity/merged";
72
73
74
75
76 private String mergeLocation;
77
78
79
80
81 private String namespace;
82
83
84
85
86 private VelocityEngine velocityEngine;
87
88
89
90 private VelocityContext velocityContext;
91
92
93
94 private final List<String> macroLibraries = new ArrayList<String>();
95
96
97
98
99
100 private final Map<String, Template> discoveredTemplates = new HashMap<String, Template>();
101
102
103
104
105 private final Collection<File> mergedTemplateFiles = new ArrayList<File>();
106
107
108
109
110
111
112
113 public void initialize(final String namespace)
114 throws Exception
115 {
116 this.namespace = namespace;
117 this.initLogger(namespace);
118
119 ExtendedProperties engineProperties = new ExtendedProperties();
120
121
122
123
124
125
126 engineProperties.setProperty(VelocityEngine.RESOURCE_LOADER, "file,classpath");
127
128 engineProperties.setProperty(
129 "file." + VelocityEngine.RESOURCE_LOADER + ".class",
130 FileResourceLoader.class.getName());
131
132 engineProperties.setProperty(
133 "classpath." + VelocityEngine.RESOURCE_LOADER + ".class",
134 ClasspathResourceLoader.class.getName());
135
136
137 engineProperties.setProperty( RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS,
138 "org.apache.velocity.runtime.log.Log4JLogChute" );
139 engineProperties.setProperty(Log4JLogChute.RUNTIME_LOG_LOG4J_LOGGER, logger.getName());
140
141
142 for (String macroLibrary : getMacroLibraries())
143 {
144 engineProperties.addProperty(
145 VelocityEngine.VM_LIBRARY,
146 macroLibrary);
147 }
148
149 this.velocityEngine = new VelocityEngine();
150 this.velocityEngine.setExtendedProperties(engineProperties);
151
152 if (this.mergeLocation != null)
153 {
154
155 velocityEngine.addProperty(VelocityEngine.FILE_RESOURCE_LOADER_PATH, this.mergeLocation);
156 }
157
158
159
160 if (Merger.instance().requiresMerge(this.namespace))
161 {
162 velocityEngine.addProperty(
163 VelocityEngine.FILE_RESOURCE_LOADER_PATH,
164 this.getMergedTemplatesLocation());
165 }
166
167 this.addProperties(namespace);
168 this.velocityEngine.init();
169 }
170
171
172
173
174
175
176 private void addProperties(String pluginName)
177 throws IOException
178 {
179
180 URL propertiesUri =
181 ResourceUtils.getResource(PROPERTIES_DIR + StringUtils.trimToEmpty(pluginName) + PROPERTIES_SUFFIX);
182
183 if (propertiesUri != null)
184 {
185 if (logger.isDebugEnabled())
186 {
187 logger.debug("loading properties from --> '" + propertiesUri + '\'');
188 }
189
190 Properties properties = new Properties();
191 properties.load(propertiesUri.openStream());
192
193 for (Map.Entry entry : properties.entrySet())
194 {
195 final String property = (String) entry.getKey();
196 final String value = (String)entry.getValue();
197 if (logger.isDebugEnabled())
198 {
199 logger.debug("setting property '" + property + "' with --> '" + value + '\'');
200 }
201 this.velocityEngine.setProperty(property, value);
202 }
203 }
204 }
205
206
207
208
209
210 public void processTemplate(
211 final String templateFile,
212 final Map<String, Object> templateObjects,
213 final Writer output)
214 throws Exception
215 {
216 final String methodName = "VelocityTemplateEngine.processTemplate";
217
218 if (logger.isDebugEnabled())
219 {
220 logger.debug(
221 "performing " + methodName + " with templateFile '" + templateFile + "' and templateObjects '" +
222 templateObjects + '\'');
223 }
224 ExceptionUtils.checkEmpty("templateFile", templateFile);
225 ExceptionUtils.checkNull("output", output);
226 this.velocityContext = new VelocityContext();
227 this.loadVelocityContext(templateObjects);
228
229 Template template = this.discoveredTemplates.get(templateFile);
230 if (template == null)
231 {
232 template = this.velocityEngine.getTemplate(templateFile);
233
234
235 final Merger merger = Merger.instance();
236 if (merger.requiresMerge(this.namespace))
237 {
238 final String mergedTemplateLocation = this.getMergedTemplateLocation(templateFile);
239 final InputStream resource = template.getResourceLoader().getResourceStream(templateFile);
240 ResourceWriter.instance().writeStringToFile(
241 merger.getMergedString(resource, this.namespace),
242 mergedTemplateLocation);
243 template = this.velocityEngine.getTemplate(templateFile);
244 this.mergedTemplateFiles.add(new File(mergedTemplateLocation));
245 resource.close();
246 }
247 this.discoveredTemplates.put(templateFile, template);
248 }
249 template.merge(velocityContext, output);
250 }
251
252
253
254
255
256
257
258 private void loadVelocityContext(final Map<String, Object> templateObjects)
259 {
260 if (templateObjects != null)
261 {
262
263 for (Map.Entry<String, Object> entry : templateObjects.entrySet())
264 {
265 this.velocityContext.put(entry.getKey(), entry.getValue());
266 }
267 }
268
269 this.velocityContext.put("esc", new EscapeTool());
270 }
271
272
273
274
275
276
277
278 private String getMergedTemplateLocation(String templatePath)
279 {
280 return this.getMergedTemplatesLocation() + '/' + templatePath;
281 }
282
283
284
285
286
287
288
289
290 private String getMergedTemplatesLocation()
291 {
292 return TEMPORARY_TEMPLATE_LOCATION + '/' + this.namespace;
293 }
294
295
296
297
298 private static final String LOG_TAG = "logtag";
299
300
301
302
303 public String getEvaluatedExpression(final String expression, final Map<String, Object> templateObjects)
304 {
305 String evaluatedExpression = null;
306 if (StringUtils.isNotBlank(expression))
307 {
308
309 if (this.velocityContext == null)
310 {
311 this.velocityContext = new VelocityContext();
312 this.loadVelocityContext(templateObjects);
313 }
314
315 try
316 {
317 final StringWriter writer = new StringWriter();
318 this.velocityEngine.evaluate(this.velocityContext, writer, LOG_TAG, expression);
319 evaluatedExpression = writer.toString();
320 }
321 catch (final Throwable throwable)
322 {
323 throw new TemplateEngineException(throwable);
324 }
325 }
326 return evaluatedExpression;
327 }
328
329
330
331
332 public List<String> getMacroLibraries()
333 {
334 return this.macroLibraries;
335 }
336
337
338
339
340 public void addMacroLibrary(String libraryName)
341 {
342 this.macroLibraries.add(libraryName);
343 }
344
345
346
347
348 public void setMergeLocation(String mergeLocation)
349 {
350 this.mergeLocation = mergeLocation;
351 }
352
353
354
355
356 public void shutdown()
357 {
358
359
360 FileUtils.deleteQuietly(new File(TEMPORARY_TEMPLATE_LOCATION));
361 this.discoveredTemplates.clear();
362 this.velocityEngine = null;
363 if(null!=logger && null!=appender)
364 {
365 logger.removeAppender(appender);
366 }
367 }
368
369
370
371
372
373
374
375 private void initLogger(final String pluginName)
376 throws IOException
377 {
378 logger = AndroMDALogger.getNamespaceLogger(pluginName);
379 logger.setAdditivity(false);
380
381 appender =
382 new FileAppender(
383 new PatternLayout("%-5p %d - %m%n"),
384 AndroMDALogger.getNamespaceLogFileName(pluginName),
385 true);
386 logger.addAppender(appender);
387 }
388 }