KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > tigris > scarab > services > email > VelocityEmailService


1 package org.tigris.scarab.services.email;
2
3 /* ====================================================================
4  * The Apache Software License, Version 1.1
5  *
6  * Copyright (c) 2001 The Apache Software Foundation. All rights
7  * reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  * notice, this list of conditions and the following disclaimer in
18  * the documentation and/or other materials provided with the
19  * distribution.
20  *
21  * 3. The end-user documentation included with the redistribution,
22  * if any, must include the following acknowledgment:
23  * "This product includes software developed by the
24  * Apache Software Foundation (http://www.apache.org/)."
25  * Alternately, this acknowledgment may appear in the software itself,
26  * if and wherever such third-party acknowledgments normally appear.
27  *
28  * 4. The names "Apache" and "Apache Software Foundation" and
29  * "Apache Turbine" must not be used to endorse or promote products
30  * derived from this software without prior written permission. For
31  * written permission, please contact apache@apache.org.
32  *
33  * 5. Products derived from this software may not be called "Apache",
34  * "Apache Turbine", nor may "Apache" appear in their name, without
35  * prior written permission of the Apache Software Foundation.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
41  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48  * SUCH DAMAGE.
49  * ====================================================================
50  *
51  * This software consists of voluntary contributions made by many
52  * individuals on behalf of the Apache Software Foundation. For more
53  * information on the Apache Software Foundation, please see
54  * <http://www.apache.org/>.
55  */

56
57 import java.io.ByteArrayOutputStream JavaDoc;
58 import java.io.IOException JavaDoc;
59 import java.io.OutputStream JavaDoc;
60 import java.io.OutputStreamWriter JavaDoc;
61 import java.io.StringWriter JavaDoc;
62 import java.io.Writer JavaDoc;
63 import java.util.ArrayList JavaDoc;
64 import java.util.Iterator JavaDoc;
65 import java.util.List JavaDoc;
66
67 import org.apache.commons.configuration.ConfigurationConverter;
68 import org.apache.fulcrum.InitializationException;
69 import org.apache.fulcrum.ServiceException;
70 import org.apache.fulcrum.template.BaseTemplateEngineService;
71 import org.apache.fulcrum.template.TemplateContext;
72 import org.apache.fulcrum.velocity.ContextAdapter;
73 import org.apache.velocity.VelocityContext;
74 import org.apache.velocity.app.VelocityEngine;
75 import org.apache.velocity.context.Context;
76 import org.apache.velocity.context.InternalEventContext;
77 import org.apache.velocity.exception.MethodInvocationException;
78 import org.tigris.scarab.tools.ScarabLocalizationTool;
79 import org.tigris.scarab.util.ScarabConstants;
80
81 /**
82  * This is a Service that can process Velocity templates from within a
83  * Turbine Screen. Here's an example of how you might use it from a
84  * screen:<br>
85  *
86  * <code><pre>
87  * Context context = new VelocityContext();
88  * context.put("message", "Hello from Turbine!");
89  * String results = TurbineVelocity.handleRequest(context,"HelloWorld.vm");
90  * </pre></code>
91  *
92  * Character sets map codes to glyphs, while encodings map between
93  * chars/bytes and codes.
94  * <i>bytes -> [encoding] -> charset -> [rendering] -> glyphs</i>
95  *
96  * <p>This copy of TurbineVelocityService has been slightly modified
97  * from its original form to support toggling of Scarab's cross-site
98  * scripting filter.</p>
99  *
100  * @author <a HREF="mailto:mbryson@mont.mindspring.com">Dave Bryson</a>
101  * @author <a HREF="mailto:krzewski@e-point.pl">Rafal Krzewski</a>
102  * @author <a HREF="mailto:jvanzyl@periapt.com">Jason van Zyl</a>
103  * @author <a HREF="mailto:sean@informage.ent">Sean Legassick</a>
104  * @author <a HREF="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
105  * @author <a HREF="mailto:jon@latchkey.com">Jon S. Stevens</a>
106  * @author <a HREF="mailto:mpoeschl@marmot.at">Martin Poeschl</a>
107  * @author <a HREF="mailto:james@jamestaylor.org">James Taylor</a>
108  * @version $Id: VelocityEmailService.java 9104 2004-05-10 21:04:51Z dabbous $
109  */

110 public class VelocityEmailService
111     extends BaseTemplateEngineService
112     implements EmailService
113 {
114     /**
115      * The generic resource loader path property in velocity.
116      */

117     private static final String JavaDoc RESOURCE_LOADER_PATH =
118         ".resource.loader.path";
119
120     /**
121      * Default character set to use if not specified by caller.
122      */

123     private static final String JavaDoc DEFAULT_CHAR_SET = "ISO-8859-1";
124
125     /**
126      * The prefix used for URIs which are of type <code>jar</code>.
127      */

128     private static final String JavaDoc JAR_PREFIX = "jar:";
129
130     /**
131      * The prefix used for URIs which are of type <code>absolute</code>.
132      */

133     private static final String JavaDoc ABSOLUTE_PREFIX = "file://";
134
135     /**
136      * The VelocityEngine used by the service to merge templates
137      */

138     private VelocityEngine velocityEngine = new VelocityEngine();
139
140     /**
141      * Performs early initialization of this Turbine service.
142      */

143     public void init()
144         throws InitializationException
145     {
146         try
147         {
148             initVelocity();
149             // Register with the template service.
150
registerConfiguration("ve");
151             setInit(true);
152         }
153         catch (Exception JavaDoc e)
154         {
155             e.printStackTrace();
156             throw new InitializationException(
157                 "Failed to initialize VelocityEmailService", e); //EXCEPTION
158
}
159     }
160
161     /**
162      * @see org.apache.fulcrum.velocity.VelocityService
163      */

164     public String JavaDoc handleRequest(TemplateContext context, String JavaDoc template)
165         throws ServiceException
166     {
167         return handleRequest(new ContextAdapter(context), template);
168     }
169
170     /**
171      * @see org.apache.fulcrum.velocity.VelocityService
172      */

173     public String JavaDoc handleRequest(Context context, String JavaDoc filename)
174         throws ServiceException
175     {
176         return handleRequest(context, filename, (String JavaDoc)null, null);
177     }
178
179     /**
180      * @see org.apache.fulcrum.velocity.VelocityService
181      */

182     public String JavaDoc handleRequest(Context context, String JavaDoc filename,
183                                 String JavaDoc charset, String JavaDoc encoding)
184         throws ServiceException
185     {
186         String JavaDoc results = null;
187         if (charset == null)
188         {
189             StringWriter JavaDoc writer = null;
190             try
191             {
192                 writer = new StringWriter JavaDoc();
193                 handleRequest(context, filename, writer, encoding);
194                 results = writer.toString();
195             }
196             catch (Exception JavaDoc e)
197             {
198                 renderingError(filename, e);
199             }
200             finally
201             {
202                 try
203                 {
204                     if (writer != null)
205                     {
206                         writer.close();
207                     }
208                 }
209                 catch (IOException JavaDoc ignored)
210                 {
211                 }
212             }
213         }
214         else
215         {
216         ByteArrayOutputStream JavaDoc bytes = null;
217         try
218         {
219             bytes = new ByteArrayOutputStream JavaDoc();
220             charset = decodeRequest(context, filename, bytes, charset,
221                                     encoding);
222             results = bytes.toString(charset);
223         }
224         catch (Exception JavaDoc e)
225         {
226             renderingError(filename, e);
227         }
228         finally
229         {
230             try
231             {
232                 if (bytes != null)
233                 {
234                     bytes.close();
235                 }
236             }
237             catch (IOException JavaDoc ignored)
238             {
239             }
240         }
241         }
242         
243         return results;
244     }
245
246     /**
247      * @see org.apache.fulcrum.template.TemplateEngineService
248      */

249     public void handleRequest(TemplateContext context, String JavaDoc template,
250                               OutputStream JavaDoc outputStream)
251         throws ServiceException
252     {
253         handleRequest(new ContextAdapter(context), template, outputStream);
254     }
255
256     /**
257      * @see org.apache.fulcrum.velocity.VelocityService
258      */

259     public void handleRequest(Context context, String JavaDoc filename,
260                               OutputStream JavaDoc output)
261         throws ServiceException
262     {
263         handleRequest(context, filename, output, null, null);
264     }
265
266     /**
267      * @see org.apache.fulcrum.velocity.VelocityService
268      */

269     public void handleRequest(Context context, String JavaDoc filename,
270                               OutputStream JavaDoc output, String JavaDoc charset,
271                               String JavaDoc encoding)
272         throws ServiceException
273     {
274         decodeRequest(context, filename, output, charset, encoding);
275     }
276
277     /**
278      * @see BaseTemplateEngineService#handleRequest(TemplateContext, String, Writer)
279      */

280     public void handleRequest(TemplateContext context,
281                                        String JavaDoc template, Writer JavaDoc writer)
282         throws ServiceException
283     {
284         handleRequest(new ContextAdapter(context), template, writer);
285     }
286
287     /**
288      * @see VelocityEmailService#handleRequest(Context, String, Writer)
289      */

290     public void handleRequest(Context context, String JavaDoc filename,
291                               Writer JavaDoc writer)
292         throws ServiceException
293     {
294         handleRequest(context, filename, writer, null);
295     }
296
297     /**
298      * @see VelocityEmailService#handleRequest(Context, String, Writer, String)
299      */

300     public void handleRequest(Context context, String JavaDoc filename,
301                               Writer JavaDoc writer, String JavaDoc encoding)
302         throws ServiceException
303     {
304         ScarabLocalizationTool l10n = null;
305         try
306         {
307             // If the context is not already an instance of
308
// InternalEventContext, wrap it in a VeclocityContext so that
309
// event cartridges will work. Unfortunately there is no interface
310
// that extends both Context and InternalEventContext, so this
311
// is not as clear as it could be.
312

313             Context eventContext;
314
315             if ( context instanceof InternalEventContext )
316             {
317                 eventContext = context;
318             }
319             else
320             {
321                 eventContext = new VelocityContext( context );
322             }
323
324             if (encoding == null)
325             {
326                 encoding = DEFAULT_CHAR_SET;
327             }
328
329             // Assumption: Emails are always plain text.
330
l10n = (ScarabLocalizationTool)
331                 context.get(ScarabConstants.LOCALIZATION_TOOL);
332             if (l10n != null)
333             {
334                 l10n.setFilterEnabled(false);
335             }
336             // Request scoped encoding first supported by Velocity 1.1.
337
velocityEngine.mergeTemplate(filename, encoding,
338                                          eventContext, writer);
339         }
340         catch (Exception JavaDoc e)
341         {
342             renderingError(filename, e);
343         }
344         finally
345         {
346             if (l10n != null)
347             {
348                 l10n.setFilterEnabled(true);
349             }
350         }
351     }
352
353     /**
354      * Processes the request and fill in the template with the values
355      * you set in the the supplied Context. Applies the specified
356      * character and template encodings.
357      *
358      * @param context A context to use when evaluating the specified
359      * template.
360      * @param filename The file name of the template.
361      * @param output The stream to which we will write the processed
362      * template as a String.
363      * @return The character set applied to the resulting text.
364      *
365      * @throws ServiceException Any exception trown while processing
366      * will be wrapped into a ServiceException and rethrown.
367      */

368     private String JavaDoc decodeRequest(Context context, String JavaDoc filename,
369                                  OutputStream JavaDoc output, String JavaDoc charset,
370                                  String JavaDoc encoding)
371         throws ServiceException
372     {
373         if (charset == null)
374         {
375             charset = DEFAULT_CHAR_SET;
376         }
377
378         OutputStreamWriter JavaDoc writer = null;
379         try
380         {
381             try
382             {
383                 writer = new OutputStreamWriter JavaDoc(output, charset);
384             }
385             catch (Exception JavaDoc e)
386             {
387                 renderingError(filename, e);
388             }
389             handleRequest(context, filename, writer, encoding);
390         }
391         finally
392         {
393             try
394             {
395                 if (writer != null)
396                 {
397                     writer.flush();
398                 }
399             }
400             catch (Exception JavaDoc ignored)
401             {
402             }
403         }
404         return charset;
405     }
406
407     /**
408      * Macro to handle rendering errors.
409      *
410      * @param filename The file name of the unrenderable template.
411      * @param e The error.
412      *
413      * @exception ServiceException Thrown every time. Adds additional
414      * information to <code>e</code>.
415      */

416     private final void renderingError(String JavaDoc filename, Throwable JavaDoc e)
417         throws ServiceException
418     {
419         String JavaDoc err = "Error rendering Velocity template: " + filename;
420         getCategory().error(err + ": " + e.getMessage());
421         // if the Exception is a MethodInvocationException, the underlying
422
// Exception is likely to be more informative, so rewrap that one.
423
if (e instanceof MethodInvocationException)
424         {
425             e = ((MethodInvocationException)e).getWrappedThrowable();
426         }
427
428         throw new ServiceException(err, e); //EXCEPTION
429
}
430
431     /**
432      * Setup the velocity runtime by using a subset of the
433      * Turbine configuration which relates to velocity.
434      *
435      * @exception InitializationException For any errors during initialization.
436      */

437     private void initVelocity()
438         throws InitializationException
439     {
440         // Now we have to perform a couple of path translations
441
// for our log file and template paths.
442
String JavaDoc path = getRealPath(
443             getConfiguration().getString(VelocityEngine.RUNTIME_LOG, null));
444
445         if (path != null && path.length() > 0)
446         {
447             getConfiguration().setProperty(VelocityEngine.RUNTIME_LOG, path);
448         }
449         else
450         {
451             String JavaDoc msg = EmailService.SERVICE_NAME + " runtime log file " +
452                 "is misconfigured: '" + path + "' is not a valid log file";
453
454             throw new Error JavaDoc(msg); //EXCEPTION
455
}
456
457         // Get all the template paths where the velocity
458
// runtime should search for templates and
459
// collect them into a separate vector
460
// to avoid concurrent modification exceptions.
461
String JavaDoc key;
462         List JavaDoc keys = new ArrayList JavaDoc();
463         for (Iterator JavaDoc i = getConfiguration().getKeys(); i.hasNext();)
464         {
465             key = (String JavaDoc) i.next();
466             if (key.endsWith(RESOURCE_LOADER_PATH))
467             {
468                 keys.add(key);
469             }
470         }
471
472         // Loop through all template paths, clear the corresponding
473
// velocity properties and translate them all to the webapp space.
474
int ind;
475         List JavaDoc paths;
476         String JavaDoc entry;
477         for (Iterator JavaDoc i = keys.iterator(); i.hasNext();)
478         {
479             key = (String JavaDoc) i.next();
480             paths = getConfiguration().getList(key,null);
481             if (paths != null)
482             {
483                 velocityEngine.clearProperty(key);
484                 getConfiguration().clearProperty(key);
485
486                 for (Iterator JavaDoc j = paths.iterator(); j.hasNext();)
487                 {
488                     path = (String JavaDoc) j.next();
489                     if (path.startsWith(JAR_PREFIX + "file"))
490                     {
491                         // A local jar resource URL path is a bit more
492
// complicated, but we can translate it as well.
493
ind = path.indexOf("!/");
494                         if (ind >= 0)
495                         {
496                             entry = path.substring(ind);
497                             path = path.substring(9,ind);
498                         }
499                         else
500                         {
501                             entry = "!/";
502                             path = path.substring(9);
503                         }
504                         path = JAR_PREFIX + "file:" + getRealPath(path) +
505                             entry;
506                     }
507                     else if (path.startsWith(ABSOLUTE_PREFIX))
508                     {
509                         path = path.substring (ABSOLUTE_PREFIX.length(),
510                                                path.length());
511                     }
512                     else if (!path.startsWith(JAR_PREFIX))
513                     {
514                         // But we don't translate remote jar URLs.
515
path = getRealPath(path);
516                     }
517                     // Put the translated paths back to the configuration.
518
getConfiguration().addProperty(key,path);
519                 }
520             }
521         }
522
523         try
524         {
525             velocityEngine.setExtendedProperties(ConfigurationConverter
526                     .getExtendedProperties(getConfiguration()));
527
528             velocityEngine.init();
529         }
530         catch(Exception JavaDoc e)
531         {
532             // This will be caught and rethrown by the init() method.
533
// Oh well, that will protect us from RuntimeException folk showing
534
// up somewhere above this try/catch
535
throw new InitializationException(
536                 "Failed to set up VelocityEmailService", e); //EXCEPTION
537
}
538     }
539
540     /**
541      * Find out if a given template exists. Velocity
542      * will do its own searching to determine whether
543      * a template exists or not.
544      *
545      * @param String template to search for
546      * @return boolean
547      */

548     public boolean templateExists(String JavaDoc template)
549     {
550         return velocityEngine.templateExists(template);
551     }
552 }
553
Popular Tags