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