1 package org.apache.turbine.services.velocity;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 import java.io.ByteArrayOutputStream;
25 import java.io.IOException;
26 import java.io.OutputStream;
27 import java.io.OutputStreamWriter;
28 import java.io.Writer;
29 import java.util.Iterator;
30 import java.util.List;
31
32 import org.apache.commons.collections.ExtendedProperties;
33 import org.apache.commons.configuration.Configuration;
34 import org.apache.commons.lang.StringUtils;
35 import org.apache.commons.logging.Log;
36 import org.apache.commons.logging.LogFactory;
37 import org.apache.turbine.Turbine;
38 import org.apache.turbine.pipeline.PipelineData;
39 import org.apache.turbine.services.InitializationException;
40 import org.apache.turbine.services.TurbineServices;
41 import org.apache.turbine.services.pull.PullService;
42 import org.apache.turbine.services.template.BaseTemplateEngineService;
43 import org.apache.turbine.util.RunData;
44 import org.apache.turbine.util.TurbineException;
45 import org.apache.velocity.VelocityContext;
46 import org.apache.velocity.app.VelocityEngine;
47 import org.apache.velocity.app.event.EventCartridge;
48 import org.apache.velocity.app.event.MethodExceptionEventHandler;
49 import org.apache.velocity.context.Context;
50 import org.apache.velocity.runtime.RuntimeConstants;
51 import org.apache.velocity.runtime.log.CommonsLogLogChute;
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81 public class TurbineVelocityService
82 extends BaseTemplateEngineService
83 implements VelocityService,
84 MethodExceptionEventHandler
85 {
86
87 private static final String RESOURCE_LOADER_PATH = ".resource.loader.path";
88
89
90 private static final String DEFAULT_CHAR_SET = "ISO-8859-1";
91
92
93 private static final String JAR_PREFIX = "jar:";
94
95
96 private static final String ABSOLUTE_PREFIX = "file://";
97
98
99 private static final Log log = LogFactory.getLog(TurbineVelocityService.class);
100
101
102 private String defaultInputEncoding;
103
104
105 private String defaultOutputEncoding;
106
107
108 private boolean pullModelActive = false;
109
110
111 private boolean catchErrors = true;
112
113
114 private VelocityEngine velocity = null;
115
116
117 private PullService pullService = null;
118
119
120
121
122
123
124
125
126
127
128 @Override
129 public void init()
130 throws InitializationException
131 {
132 try
133 {
134 initVelocity();
135
136
137
138
139 if (TurbineServices.getInstance().isRegistered(PullService.SERVICE_NAME))
140 {
141 pullModelActive = true;
142 pullService = (PullService)TurbineServices.getInstance().getService(PullService.SERVICE_NAME);
143
144 log.debug("Activated Pull Tools");
145 }
146
147
148 registerConfiguration(VelocityService.VELOCITY_EXTENSION);
149
150 defaultInputEncoding = getConfiguration().getString("input.encoding", DEFAULT_CHAR_SET);
151 defaultOutputEncoding = getConfiguration().getString("output.encoding", defaultInputEncoding);
152
153 setInit(true);
154 }
155 catch (Exception e)
156 {
157 throw new InitializationException(
158 "Failed to initialize TurbineVelocityService", e);
159 }
160 }
161
162
163
164
165
166
167 @Override
168 public Context getContext()
169 {
170 Context globalContext =
171 pullModelActive ? pullService.getGlobalContext() : null;
172
173 Context ctx = new VelocityContext(globalContext);
174 return ctx;
175 }
176
177
178
179
180
181
182 @Override
183 public Context getNewContext()
184 {
185 Context ctx = new VelocityContext();
186
187
188
189 EventCartridge ec = new EventCartridge();
190 ec.addEventHandler(this);
191 ec.attachToContext(ctx);
192 return ctx;
193 }
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208 @Override
209 @SuppressWarnings("rawtypes")
210 public Object methodException(Class clazz, String method, Exception e)
211 throws Exception
212 {
213 log.error("Class " + clazz.getName() + "." + method + " threw Exception", e);
214
215 if (!catchErrors)
216 {
217 throw e;
218 }
219
220 return "[Turbine caught an Error here. Look into the turbine.log for further information]";
221 }
222
223
224
225
226
227
228
229
230
231 @Override
232 public Context getContext(PipelineData pipelineData)
233 {
234
235 RunData data = (RunData)pipelineData;
236
237
238 Context context = (Context)
239 data.getTemplateInfo().getTemplateContext(VelocityService.CONTEXT);
240
241 if (context == null)
242 {
243 context = getContext();
244 context.put(VelocityService.RUNDATA_KEY, data);
245
246 context.put(VelocityService.PIPELINEDATA_KEY, pipelineData);
247
248 if (pullModelActive)
249 {
250
251
252
253
254 pullService.populateContext(context, pipelineData);
255 }
256
257 data.getTemplateInfo().setTemplateContext(
258 VelocityService.CONTEXT, context);
259 }
260 return context;
261 }
262
263
264
265
266
267
268
269
270
271
272
273
274 @Override
275 public String handleRequest(Context context, String filename)
276 throws TurbineException
277 {
278 String results = null;
279 ByteArrayOutputStream bytes = null;
280 OutputStreamWriter writer = null;
281 String charset = getOutputCharSet(context);
282
283 try
284 {
285 bytes = new ByteArrayOutputStream();
286
287 writer = new OutputStreamWriter(bytes, charset);
288
289 executeRequest(context, filename, writer);
290 writer.flush();
291 results = bytes.toString(charset);
292 }
293 catch (Exception e)
294 {
295 renderingError(filename, e);
296 }
297 finally
298 {
299 try
300 {
301 if (bytes != null)
302 {
303 bytes.close();
304 }
305 }
306 catch (IOException ignored)
307 {
308
309 }
310 }
311 return results;
312 }
313
314
315
316
317
318
319
320
321
322
323
324
325
326 @Override
327 public void handleRequest(Context context, String filename,
328 OutputStream output)
329 throws TurbineException
330 {
331 String charset = getOutputCharSet(context);
332 OutputStreamWriter writer = null;
333
334 try
335 {
336 writer = new OutputStreamWriter(output, charset);
337 executeRequest(context, filename, writer);
338 }
339 catch (Exception e)
340 {
341 renderingError(filename, e);
342 }
343 finally
344 {
345 try
346 {
347 if (writer != null)
348 {
349 writer.flush();
350 }
351 }
352 catch (Exception ignored)
353 {
354
355 }
356 }
357 }
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372 @Override
373 public void handleRequest(Context context, String filename, Writer writer)
374 throws TurbineException
375 {
376 try
377 {
378 executeRequest(context, filename, writer);
379 }
380 catch (Exception e)
381 {
382 renderingError(filename, e);
383 }
384 finally
385 {
386 try
387 {
388 if (writer != null)
389 {
390 writer.flush();
391 }
392 }
393 catch (Exception ignored)
394 {
395
396 }
397 }
398 }
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413 private void executeRequest(Context context, String filename,
414 Writer writer)
415 throws Exception
416 {
417 String encoding = getTemplateEncoding(context);
418
419 if (encoding == null)
420 {
421 encoding = defaultOutputEncoding;
422 }
423
424 velocity.mergeTemplate(filename, encoding, context, writer);
425 }
426
427
428
429
430
431
432
433 private String getOutputCharSet(Context context)
434 {
435 String charset = null;
436
437 Object data = context.get(VelocityService.RUNDATA_KEY);
438 if ((data != null) && (data instanceof RunData))
439 {
440 charset = ((RunData) data).getCharSet();
441 }
442
443 return (StringUtils.isEmpty(charset)) ? defaultOutputEncoding : charset;
444 }
445
446
447
448
449
450
451
452 private String getTemplateEncoding(Context context)
453 {
454 String encoding = null;
455
456 Object data = context.get(VelocityService.RUNDATA_KEY);
457 if ((data != null) && (data instanceof RunData))
458 {
459 encoding = ((RunData) data).getTemplateEncoding();
460 }
461
462 return encoding != null ? encoding : defaultInputEncoding;
463 }
464
465
466
467
468
469
470
471
472
473
474 private static final void renderingError(String filename, Exception e)
475 throws TurbineException
476 {
477 String err = "Error rendering Velocity template: " + filename;
478 log.error(err, e);
479 throw new TurbineException(err, e);
480 }
481
482
483
484
485
486
487
488 private synchronized void initVelocity()
489 throws Exception
490 {
491
492 Configuration conf = getConfiguration();
493
494 catchErrors = conf.getBoolean(CATCH_ERRORS_KEY, CATCH_ERRORS_DEFAULT);
495
496 conf.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS,
497 CommonsLogLogChute.class.getName());
498 conf.setProperty(CommonsLogLogChute.LOGCHUTE_COMMONS_LOG_NAME,
499 "velocity");
500
501 velocity = new VelocityEngine();
502 velocity.setExtendedProperties(createVelocityProperties(conf));
503 velocity.init();
504 }
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519 public ExtendedProperties createVelocityProperties(Configuration conf)
520 throws Exception
521 {
522
523
524
525 ExtendedProperties veloConfig = new ExtendedProperties();
526
527
528
529
530
531 for (Iterator<String> i = conf.getKeys(); i.hasNext();)
532 {
533 String key = i.next();
534 if (!key.endsWith(RESOURCE_LOADER_PATH))
535 {
536 Object value = conf.getProperty(key);
537 if (value instanceof List<?>) {
538 for (Iterator<?> itr = ((List<?>)value).iterator(); itr.hasNext();)
539 {
540 veloConfig.addProperty(key, itr.next());
541 }
542 }
543 else
544 {
545 veloConfig.addProperty(key, value);
546 }
547 continue;
548 }
549
550 List<Object> paths = conf.getList(key, null);
551 if (paths == null)
552 {
553
554
555 continue;
556 }
557
558
559
560
561
562
563
564
565 for (Object p : paths)
566 {
567 String path = (String)p;
568 log.debug("Translating " + path);
569
570 if (path.startsWith(JAR_PREFIX))
571 {
572
573 if (path.substring(4).startsWith(ABSOLUTE_PREFIX))
574 {
575
576 int jarSepIndex = path.indexOf("!/");
577
578
579 path = (jarSepIndex < 0)
580 ? Turbine.getRealPath(path.substring(11))
581
582 : (Turbine.getRealPath(path.substring(11, jarSepIndex)) + path.substring(jarSepIndex));
583
584 log.debug("Result (absolute jar path): " + path);
585 }
586 }
587 else if(path.startsWith(ABSOLUTE_PREFIX))
588 {
589
590 path = Turbine.getRealPath(path.substring(7));
591
592 log.debug("Result (absolute URL Path): " + path);
593 }
594
595 else if(path.indexOf("://") < 0)
596 {
597 path = Turbine.getRealPath(path);
598
599 log.debug("Result (normal fs reference): " + path);
600 }
601
602 log.debug("Adding " + key + " -> " + path);
603
604 veloConfig.addProperty(key, path);
605 }
606 }
607 return veloConfig;
608 }
609
610
611
612
613
614
615
616
617
618 @Override
619 public boolean templateExists(String template)
620 {
621 return velocity.resourceExists(template);
622 }
623
624
625
626
627
628
629
630 @Override
631 public void requestFinished(Context context)
632 {
633 if (pullModelActive)
634 {
635 pullService.releaseTools(context);
636 }
637 }
638 }