KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > turbine > services > velocity > TurbineVelocityService


1 package org.apache.turbine.services.velocity;
2
3 /*
4  * Copyright 2001-2004 The Apache Software Foundation.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License")
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */

18
19 import java.io.ByteArrayOutputStream JavaDoc;
20 import java.io.IOException JavaDoc;
21 import java.io.OutputStream JavaDoc;
22 import java.io.OutputStreamWriter JavaDoc;
23 import java.io.Writer JavaDoc;
24
25 import java.util.Iterator JavaDoc;
26 import java.util.List JavaDoc;
27 import java.util.Vector JavaDoc;
28
29 import javax.servlet.ServletConfig JavaDoc;
30
31 import org.apache.commons.collections.ExtendedProperties;
32 import org.apache.commons.configuration.Configuration;
33 import org.apache.commons.lang.StringUtils;
34 import org.apache.commons.logging.Log;
35 import org.apache.commons.logging.LogFactory;
36
37 import org.apache.velocity.VelocityContext;
38 import org.apache.velocity.app.Velocity;
39 import org.apache.velocity.app.event.EventCartridge;
40 import org.apache.velocity.app.event.MethodExceptionEventHandler;
41 import org.apache.velocity.context.Context;
42 import org.apache.velocity.runtime.log.SimpleLog4JLogSystem;
43
44 import org.apache.turbine.Turbine;
45 import org.apache.turbine.services.InitializationException;
46 import org.apache.turbine.services.pull.PullService;
47 import org.apache.turbine.services.pull.TurbinePull;
48 import org.apache.turbine.services.template.BaseTemplateEngineService;
49 import org.apache.turbine.util.RunData;
50 import org.apache.turbine.util.TurbineException;
51
52 /**
53  * This is a Service that can process Velocity templates from within a
54  * Turbine Screen. It is used in conjunction with the templating service
55  * as a Templating Engine for templates ending in "vm". It registers
56  * itself as translation engine with the template service and gets
57  * accessed from there. After configuring it in your properties, it
58  * should never be necessary to call methods from this service directly.
59  *
60  * Here's an example of how you might use it from a
61  * screen:<br>
62  *
63  * <code>
64  * Context context = TurbineVelocity.getContext(data);<br>
65  * context.put("message", "Hello from Turbine!");<br>
66  * String results = TurbineVelocity.handleRequest(context,"helloWorld.vm");<br>
67  * data.getPage().getBody().addElement(results);<br>
68  * </code>
69  *
70  * @author <a HREF="mailto:mbryson@mont.mindspring.com">Dave Bryson</a>
71  * @author <a HREF="mailto:krzewski@e-point.pl">Rafal Krzewski</a>
72  * @author <a HREF="mailto:jvanzyl@periapt.com">Jason van Zyl</a>
73  * @author <a HREF="mailto:sean@informage.ent">Sean Legassick</a>
74  * @author <a HREF="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
75  * @author <a HREF="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
76  * @version $Id: TurbineVelocityService.java,v 1.27.2.4 2004/08/22 23:25:35 henning Exp $
77  */

78 public class TurbineVelocityService
79         extends BaseTemplateEngineService
80         implements VelocityService,
81                    MethodExceptionEventHandler
82 {
83     /** The generic resource loader path property in velocity.*/
84     private static final String JavaDoc RESOURCE_LOADER_PATH = ".resource.loader.path";
85
86     /** Default character set to use if not specified in the RunData object. */
87     private static final String JavaDoc DEFAULT_CHAR_SET = "ISO-8859-1";
88
89     /** The prefix used for URIs which are of type <code>jar</code>. */
90     private static final String JavaDoc JAR_PREFIX = "jar:";
91
92     /** The prefix used for URIs which are of type <code>absolute</code>. */
93     private static final String JavaDoc ABSOLUTE_PREFIX = "file://";
94
95     /** Logging */
96     private static Log log = LogFactory.getLog(TurbineVelocityService.class);
97
98     /** Is the pullModelActive? */
99     private boolean pullModelActive = false;
100
101     /** Shall we catch Velocity Errors and report them in the log file? */
102     private boolean catchErrors = true;
103
104     /** Internal Reference to the pull Service */
105     private PullService pullService = null;
106
107
108     /**
109      * Load all configured components and initialize them. This is
110      * a zero parameter variant which queries the Turbine Servlet
111      * for its config.
112      *
113      * @throws InitializationException Something went wrong in the init
114      * stage
115      */

116     public void init()
117             throws InitializationException
118     {
119         try
120         {
121             initVelocity();
122
123             // We can only load the Pull Model ToolBox
124
// if the Pull service has been listed in the TR.props
125
// and the service has successfully been initialized.
126
if (TurbinePull.isRegistered())
127             {
128                 pullModelActive = true;
129
130                 pullService = TurbinePull.getService();
131
132                 log.debug("Activated Pull Tools");
133             }
134
135             // Register with the template service.
136
registerConfiguration(VelocityService.VELOCITY_EXTENSION);
137
138             setInit(true);
139         }
140         catch (Exception JavaDoc e)
141         {
142             throw new InitializationException(
143                 "Failed to initialize TurbineVelocityService", e);
144         }
145     }
146
147
148     /**
149      * Inits the service using servlet parameters to obtain path to the
150      * configuration file.
151      *
152      * @param config The ServletConfiguration from Turbine
153      *
154      * @throws InitializationException Something went wrong when starting up.
155      * @deprecated use init() instead.
156      */

157     public void init(ServletConfig JavaDoc config)
158             throws InitializationException
159     {
160         init();
161     }
162
163
164     /**
165      * Create a Context object that also contains the globalContext.
166      *
167      * @return A Context object.
168      */

169     public Context getContext()
170     {
171         Context globalContext =
172                 pullModelActive ? pullService.getGlobalContext() : null;
173
174         Context ctx = new VelocityContext(globalContext);
175         return ctx;
176     }
177
178     /**
179      * This method returns a new, empty Context object.
180      *
181      * @return A Context Object.
182      */

183     public Context getNewContext()
184     {
185         Context ctx = new VelocityContext();
186
187         // Attach an Event Cartridge to it, so we get exceptions
188
// while invoking methods from the Velocity Screens
189
EventCartridge ec = new EventCartridge();
190         ec.addEventHandler(this);
191         ec.attachToContext(ctx);
192         return ctx;
193     }
194
195     /**
196      * MethodException Event Cartridge handler
197      * for Velocity.
198      *
199      * It logs an execption thrown by the velocity processing
200      * on error level into the log file
201      *
202      * @param clazz The class that threw the exception
203      * @param method The Method name that threw the exception
204      * @param e The exception that would've been thrown
205      * @return A valid value to be used as Return value
206      * @throws Exception We threw the exception further up
207      */

208     public Object JavaDoc methodException(Class JavaDoc clazz, String JavaDoc method, Exception JavaDoc e)
209             throws Exception JavaDoc
210     {
211         log.error("Class " + clazz.getName() + "." + method + " threw Exception", e);
212
213         if (!catchErrors)
214         {
215             throw e;
216         }
217
218         return "[Turbine caught an Error here. Look into the turbine.log for further information]";
219     }
220
221     /**
222      * Create a Context from the RunData object. Adds a pointer to
223      * the RunData object to the VelocityContext so that RunData
224      * is available in the templates.
225      *
226      * @param data The Turbine RunData object.
227      * @return A clone of the WebContext needed by Velocity.
228      */

229     public Context getContext(RunData data)
230     {
231         // Attempt to get it from the data first. If it doesn't
232
// exist, create it and then stuff it into the data.
233
Context context = (Context)
234             data.getTemplateInfo().getTemplateContext(VelocityService.CONTEXT);
235
236         if (context == null)
237         {
238             context = getContext();
239             context.put(VelocityService.RUNDATA_KEY, data);
240
241             if (pullModelActive)
242             {
243                 // Populate the toolbox with request scope, session scope
244
// and persistent scope tools (global tools are already in
245
// the toolBoxContent which has been wrapped to construct
246
// this request-specific context).
247
pullService.populateContext(context, data);
248             }
249
250             data.getTemplateInfo().setTemplateContext(
251                 VelocityService.CONTEXT, context);
252         }
253         return context;
254     }
255
256     /**
257      * Process the request and fill in the template with the values
258      * you set in the Context.
259      *
260      * @param context The populated context.
261      * @param filename The file name of the template.
262      * @return The process template as a String.
263      *
264      * @throws TurbineException Any exception trown while processing will be
265      * wrapped into a TurbineException and rethrown.
266      */

267     public String JavaDoc handleRequest(Context context, String JavaDoc filename)
268         throws TurbineException
269     {
270         String JavaDoc results = null;
271         ByteArrayOutputStream JavaDoc bytes = null;
272         OutputStreamWriter JavaDoc writer = null;
273         String JavaDoc charset = getCharSet(context);
274
275         try
276         {
277             bytes = new ByteArrayOutputStream JavaDoc();
278
279             writer = new OutputStreamWriter JavaDoc(bytes, charset);
280
281             executeRequest(context, filename, writer);
282             writer.flush();
283             results = bytes.toString(charset);
284         }
285         catch (Exception JavaDoc e)
286         {
287             renderingError(filename, e);
288         }
289         finally
290         {
291             try
292             {
293                 if (bytes != null)
294                 {
295                     bytes.close();
296                 }
297             }
298             catch (IOException JavaDoc ignored)
299             {
300                 // do nothing.
301
}
302         }
303         return results;
304     }
305
306     /**
307      * Process the request and fill in the template with the values
308      * you set in the Context.
309      *
310      * @param context A Context.
311      * @param filename A String with the filename of the template.
312      * @param output A OutputStream where we will write the process template as
313      * a String.
314      *
315      * @throws TurbineException Any exception trown while processing will be
316      * wrapped into a TurbineException and rethrown.
317      */

318     public void handleRequest(Context context, String JavaDoc filename,
319                               OutputStream JavaDoc output)
320             throws TurbineException
321     {
322         String JavaDoc charset = getCharSet(context);
323         OutputStreamWriter JavaDoc writer = null;
324
325         try
326         {
327             writer = new OutputStreamWriter JavaDoc(output, charset);
328             executeRequest(context, filename, writer);
329         }
330         catch (Exception JavaDoc e)
331         {
332             renderingError(filename, e);
333         }
334         finally
335         {
336             try
337             {
338                 if (writer != null)
339                 {
340                     writer.flush();
341                 }
342             }
343             catch (Exception JavaDoc ignored)
344             {
345                 // do nothing.
346
}
347         }
348     }
349
350
351     /**
352      * Process the request and fill in the template with the values
353      * you set in the Context.
354      *
355      * @param context A Context.
356      * @param filename A String with the filename of the template.
357      * @param writer A Writer where we will write the process template as
358      * a String.
359      *
360      * @throws TurbineException Any exception trown while processing will be
361      * wrapped into a TurbineException and rethrown.
362      */

363     public void handleRequest(Context context, String JavaDoc filename, Writer JavaDoc writer)
364             throws TurbineException
365     {
366         try
367         {
368             executeRequest(context, filename, writer);
369         }
370         catch (Exception JavaDoc e)
371         {
372             renderingError(filename, e);
373         }
374         finally
375         {
376             try
377             {
378                 if (writer != null)
379                 {
380                     writer.flush();
381                 }
382             }
383             catch (Exception JavaDoc ignored)
384             {
385                 // do nothing.
386
}
387         }
388     }
389
390
391     /**
392      * Process the request and fill in the template with the values
393      * you set in the Context. Apply the character and template
394      * encodings from RunData to the result.
395      *
396      * @param context A Context.
397      * @param filename A String with the filename of the template.
398      * @param writer A OutputStream where we will write the process template as
399      * a String.
400      *
401      * @throws Exception A problem occured.
402      */

403     private void executeRequest(Context context, String JavaDoc filename,
404                                 Writer JavaDoc writer)
405             throws Exception JavaDoc
406     {
407         String JavaDoc encoding = getEncoding(context);
408
409         if (encoding != null)
410         {
411             Velocity.mergeTemplate(filename, encoding, context, writer);
412         }
413         else
414         {
415             Velocity.mergeTemplate(filename, context, writer);
416         }
417     }
418
419     /**
420      * Retrieve the required charset from the Turbine RunData in the context
421      *
422      * @param context A Context.
423      * @return The character set applied to the resulting String.
424      */

425     private String JavaDoc getCharSet(Context context)
426     {
427         String JavaDoc charset = null;
428
429         Object JavaDoc data = context.get(VelocityService.RUNDATA_KEY);
430         if ((data != null) && (data instanceof RunData))
431         {
432             charset = ((RunData) data).getCharSet();
433         }
434
435         return (StringUtils.isEmpty(charset)) ? DEFAULT_CHAR_SET : charset;
436     }
437
438     /**
439      * Retrieve the required encoding from the Turbine RunData in the context
440      *
441      * @param context A Context.
442      * @return The encoding applied to the resulting String.
443      */

444     private String JavaDoc getEncoding(Context context)
445     {
446         String JavaDoc encoding = null;
447
448         Object JavaDoc data = context.get(VelocityService.RUNDATA_KEY);
449         if ((data != null) && (data instanceof RunData))
450         {
451             encoding = ((RunData) data).getTemplateEncoding();
452         }
453
454         return encoding;
455     }
456
457     /**
458      * Macro to handle rendering errors.
459      *
460      * @param filename The file name of the unrenderable template.
461      * @param e The error.
462      *
463      * @exception TurbineException Thrown every time. Adds additional
464      * information to <code>e</code>.
465      */

466     private static final void renderingError(String JavaDoc filename, Exception JavaDoc e)
467             throws TurbineException
468     {
469         String JavaDoc err = "Error rendering Velocity template: " + filename;
470         log.error(err, e);
471         throw new TurbineException(err, e);
472     }
473
474     /**
475      * Setup the velocity runtime by using a subset of the
476      * Turbine configuration which relates to velocity.
477      *
478      * @exception Exception An Error occured.
479      */

480     private synchronized void initVelocity()
481         throws Exception JavaDoc
482     {
483         // Get the configuration for this service.
484
Configuration conf = getConfiguration();
485
486         catchErrors = conf.getBoolean(CATCH_ERRORS_KEY, CATCH_ERRORS_DEFAULT);
487         
488         conf.setProperty(Velocity.RUNTIME_LOG_LOGSYSTEM_CLASS,
489                 SimpleLog4JLogSystem.class.getName());
490         conf.setProperty(Velocity.RUNTIME_LOG_LOGSYSTEM
491                 + ".log4j.category", "velocity");
492         
493         Velocity.setExtendedProperties(createVelocityProperties(conf));
494         Velocity.init();
495     }
496
497
498     /**
499      * This method generates the Extended Properties object necessary
500      * for the initialization of Velocity. It also converts the various
501      * resource loader pathes into webapp relative pathes. It also
502      *
503      * @param conf The Velocity Service configuration
504      *
505      * @return An ExtendedProperties Object for Velocity
506      *
507      * @throws Exception If a problem occured while converting the properties.
508      */

509
510     public ExtendedProperties createVelocityProperties(Configuration conf)
511             throws Exception JavaDoc
512     {
513         // This bugger is public, because we want to run some Unit tests
514
// on it.
515

516         ExtendedProperties veloConfig = new ExtendedProperties();
517
518         // Fix up all the template resource loader pathes to be
519
// webapp relative. Copy all other keys verbatim into the
520
// veloConfiguration.
521

522         for (Iterator JavaDoc i = conf.getKeys(); i.hasNext();)
523         {
524             String JavaDoc key = (String JavaDoc) i.next();
525             if (!key.endsWith(RESOURCE_LOADER_PATH))
526             {
527                 Object JavaDoc value = conf.getProperty(key);
528
529                 // Since 1.0-pre-something, Commons Collections suddently
530
// no longer returns a vector for multiple value-keys but a
531
// List object. Velocity will choke if we add this object because
532
// org.apache.commons.collections.ExtendedProperties expect a
533
// Vector object. Ah, the joys of incompatible class changes,
534
// unwritten assumptions and general Java JAR Hell... =8-O
535
if (value instanceof List JavaDoc)
536                 {
537                     List JavaDoc srcValue = (List JavaDoc) value;
538                     Vector JavaDoc targetValue = new Vector JavaDoc(srcValue.size());
539
540                     for (Iterator JavaDoc it = srcValue.iterator(); it.hasNext(); )
541                     {
542                         targetValue.add(it.next());
543                     }
544
545                     veloConfig.addProperty(key, targetValue);
546                 }
547                 else
548                 {
549                     veloConfig.addProperty(key, value);
550                 }
551
552                 continue; // for()
553
}
554
555             List JavaDoc paths = conf.getList(key, null);
556             if (paths == null)
557             {
558                 // We don't copy this into VeloProperties, because
559
// null value is unhealthy for the ExtendedProperties object...
560
continue; // for()
561
}
562
563             Velocity.clearProperty(key);
564
565             // Translate the supplied pathes given here.
566
// the following three different kinds of
567
// pathes must be translated to be webapp-relative
568
//
569
// jar:file://path-component!/entry-component
570
// file://path-component
571
// path/component
572

573             for (Iterator JavaDoc j = paths.iterator(); j.hasNext();)
574             {
575                 String JavaDoc path = (String JavaDoc) j.next();
576
577                 log.debug("Translating " + path);
578
579                 if (path.startsWith(JAR_PREFIX))
580                 {
581                     // skip jar: -> 4 chars
582
if (path.substring(4).startsWith(ABSOLUTE_PREFIX))
583                     {
584                         // We must convert up to the jar path separator
585
int jarSepIndex = path.indexOf("!/");
586
587                         // jar:file:// -> skip 11 chars
588
path = (jarSepIndex < 0)
589                             ? Turbine.getRealPath(path.substring(11))
590                         // Add the path after the jar path separator again to the new url.
591
: (Turbine.getRealPath(path.substring(11, jarSepIndex)) + path.substring(jarSepIndex));
592
593                         log.debug("Result (absolute jar path): " + path);
594                     }
595                 }
596                 else if(path.startsWith(ABSOLUTE_PREFIX))
597                 {
598                     // skip file:// -> 7 chars
599
path = Turbine.getRealPath(path.substring(7));
600
601                     log.debug("Result (absolute URL Path): " + path);
602                 }
603                 // Test if this might be some sort of URL that we haven't encountered yet.
604
else if(path.indexOf("://") < 0)
605                 {
606                     path = Turbine.getRealPath(path);
607
608                     log.debug("Result (normal fs reference): " + path);
609                 }
610
611                 log.debug("Adding " + key + " -> " + path);
612                 // Re-Add this property to the configuration object
613
veloConfig.addProperty(key, path);
614             }
615         }
616         return veloConfig;
617     }
618
619     /**
620      * Find out if a given template exists. Velocity
621      * will do its own searching to determine whether
622      * a template exists or not.
623      *
624      * @param template String template to search for
625      * @return True if the template can be loaded by Velocity
626      */

627     public boolean templateExists(String JavaDoc template)
628     {
629         return Velocity.templateExists(template);
630     }
631
632     /**
633      * Performs post-request actions (releases context
634      * tools back to the object pool).
635      *
636      * @param context a Velocity Context
637      */

638     public void requestFinished(Context context)
639     {
640         if (pullModelActive)
641         {
642             pullService.releaseTools(context);
643         }
644     }
645 }
646
Popular Tags