001: package org.apache.velocity.app;
002:
003: /*
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021:
022: import java.io.BufferedReader;
023: import java.io.IOException;
024: import java.io.InputStream;
025: import java.io.InputStreamReader;
026: import java.io.Reader;
027: import java.io.StringReader;
028: import java.io.UnsupportedEncodingException;
029: import java.io.Writer;
030: import java.util.Properties;
031:
032: import org.apache.commons.collections.ExtendedProperties;
033: import org.apache.velocity.Template;
034: import org.apache.velocity.context.Context;
035: import org.apache.velocity.context.InternalContextAdapterImpl;
036: import org.apache.velocity.exception.MethodInvocationException;
037: import org.apache.velocity.exception.ParseErrorException;
038: import org.apache.velocity.exception.ResourceNotFoundException;
039: import org.apache.velocity.exception.TemplateInitException;
040: import org.apache.velocity.runtime.RuntimeConstants;
041: import org.apache.velocity.runtime.RuntimeInstance;
042: import org.apache.velocity.runtime.log.Log;
043: import org.apache.velocity.runtime.parser.ParseException;
044: import org.apache.velocity.runtime.parser.node.SimpleNode;
045:
046: /**
047: * <p>
048: * This class provides a separate new-able instance of the
049: * Velocity template engine. The alternative model for use
050: * is using the Velocity class which employs the singleton
051: * model.
052: * </p>
053: *
054: * <p>
055: * Please ensure that you call one of the init() variants.
056: * This is critical for proper behavior.
057: * </p>
058: *
059: * <p> Coming soon : Velocity will call
060: * the parameter-less init() at the first use of this class
061: * if the init() wasn't explicitly called. While this will
062: * ensure that Velocity functions, it almost certainly won't
063: * function in the way you intend, so please make sure to
064: * call init().
065: * </p>
066: *
067: * @version $Id: VelocityEngine.java 471381 2006-11-05 08:56:58Z wglass $
068: */
069: public class VelocityEngine implements RuntimeConstants {
070: private RuntimeInstance ri = new RuntimeInstance();
071:
072: /**
073: * Init-less CTOR
074: */
075: public VelocityEngine() {
076: // do nothing
077: }
078:
079: /**
080: * CTOR that invokes an init(String), initializing
081: * the engine using the properties file specified
082: *
083: * @param propsFilename name of properties file to init with
084: * @throws Exception
085: */
086: public VelocityEngine(String propsFilename) throws Exception {
087: ri.init(propsFilename);
088: }
089:
090: /**
091: * CTOR that invokes an init(String), initializing
092: * the engine using the Properties specified
093: *
094: * @param p name of properties to init with
095: * @throws Exception
096: */
097: public VelocityEngine(Properties p) throws Exception {
098: ri.init(p);
099: }
100:
101: /**
102: * initialize the Velocity runtime engine, using the default
103: * properties of the Velocity distribution
104: * @throws Exception
105: */
106: public void init() throws Exception {
107: ri.init();
108: }
109:
110: /**
111: * initialize the Velocity runtime engine, using default properties
112: * plus the properties in the properties file passed in as the arg
113: *
114: * @param propsFilename file containing properties to use to initialize
115: * the Velocity runtime
116: * @throws Exception
117: */
118: public void init(String propsFilename) throws Exception {
119: ri.init(propsFilename);
120: }
121:
122: /**
123: * initialize the Velocity runtime engine, using default properties
124: * plus the properties in the passed in java.util.Properties object
125: *
126: * @param p Proprties object containing initialization properties
127: * @throws Exception
128: *
129: */
130: public void init(Properties p) throws Exception {
131: ri.init(p);
132: }
133:
134: /**
135: * Set a Velocity Runtime property.
136: *
137: * @param key
138: * @param value
139: */
140: public void setProperty(String key, Object value) {
141: ri.setProperty(key, value);
142: }
143:
144: /**
145: * Add a Velocity Runtime property.
146: *
147: * @param key
148: * @param value
149: */
150: public void addProperty(String key, Object value) {
151: ri.addProperty(key, value);
152: }
153:
154: /**
155: * Clear a Velocity Runtime property.
156: *
157: * @param key of property to clear
158: */
159: public void clearProperty(String key) {
160: ri.clearProperty(key);
161: }
162:
163: /**
164: * Set an entire configuration at once. This is
165: * useful in cases where the parent application uses
166: * the ExtendedProperties class and the velocity configuration
167: * is a subset of the parent application's configuration.
168: *
169: * @param configuration
170: *
171: */
172: public void setExtendedProperties(ExtendedProperties configuration) {
173: ri.setConfiguration(configuration);
174: }
175:
176: /**
177: * Get a Velocity Runtime property.
178: *
179: * @param key property to retrieve
180: * @return property value or null if the property
181: * not currently set
182: */
183: public Object getProperty(String key) {
184: return ri.getProperty(key);
185: }
186:
187: /**
188: * renders the input string using the context into the output writer.
189: * To be used when a template is dynamically constructed, or want to use
190: * Velocity as a token replacer.
191: *
192: * @param context context to use in rendering input string
193: * @param out Writer in which to render the output
194: * @param logTag string to be used as the template name for log
195: * messages in case of error
196: * @param instring input string containing the VTL to be rendered
197: *
198: * @return true if successful, false otherwise. If false, see
199: * Velocity runtime log
200: * @throws ParseErrorException
201: * @throws MethodInvocationException
202: * @throws ResourceNotFoundException
203: * @throws IOException
204: */
205: public boolean evaluate(Context context, Writer out, String logTag,
206: String instring) throws ParseErrorException,
207: MethodInvocationException, ResourceNotFoundException,
208: IOException {
209: return evaluate(context, out, logTag, new BufferedReader(
210: new StringReader(instring)));
211: }
212:
213: /**
214: * Renders the input stream using the context into the output writer.
215: * To be used when a template is dynamically constructed, or want to
216: * use Velocity as a token replacer.
217: *
218: * @param context context to use in rendering input string
219: * @param writer Writer in which to render the output
220: * @param logTag string to be used as the template name for log messages
221: * in case of error
222: * @param instream input stream containing the VTL to be rendered
223: *
224: * @return true if successful, false otherwise. If false, see
225: * Velocity runtime log
226: * @throws ParseErrorException
227: * @throws MethodInvocationException
228: * @throws ResourceNotFoundException
229: * @throws IOException
230: * @deprecated Use
231: * {@link #evaluate( Context context, Writer writer,
232: * String logTag, Reader reader ) }
233: */
234: public boolean evaluate(Context context, Writer writer,
235: String logTag, InputStream instream)
236: throws ParseErrorException, MethodInvocationException,
237: ResourceNotFoundException, IOException {
238: /*
239: * first, parse - convert ParseException if thrown
240: */
241: BufferedReader br = null;
242: String encoding = null;
243:
244: try {
245: encoding = ri.getString(INPUT_ENCODING, ENCODING_DEFAULT);
246: br = new BufferedReader(new InputStreamReader(instream,
247: encoding));
248: } catch (UnsupportedEncodingException uce) {
249: String msg = "Unsupported input encoding : " + encoding
250: + " for template " + logTag;
251: throw new ParseErrorException(msg);
252: }
253:
254: return evaluate(context, writer, logTag, br);
255: }
256:
257: /**
258: * Renders the input reader using the context into the output writer.
259: * To be used when a template is dynamically constructed, or want to
260: * use Velocity as a token replacer.
261: *
262: * @param context context to use in rendering input string
263: * @param writer Writer in which to render the output
264: * @param logTag string to be used as the template name for log messages
265: * in case of error
266: * @param reader Reader containing the VTL to be rendered
267: *
268: * @return true if successful, false otherwise. If false, see
269: * Velocity runtime log
270: * @throws ParseErrorException
271: * @throws MethodInvocationException
272: * @throws ResourceNotFoundException
273: * @throws IOException
274: *
275: * @since Velocity v1.1
276: */
277: public boolean evaluate(Context context, Writer writer,
278: String logTag, Reader reader) throws ParseErrorException,
279: MethodInvocationException, ResourceNotFoundException,
280: IOException {
281: SimpleNode nodeTree = null;
282:
283: try {
284: nodeTree = ri.parse(reader, logTag);
285: } catch (ParseException pex) {
286: throw new ParseErrorException(pex);
287: } catch (TemplateInitException pex) {
288: throw new ParseErrorException(pex);
289: }
290:
291: /*
292: * now we want to init and render
293: */
294:
295: if (nodeTree != null) {
296: InternalContextAdapterImpl ica = new InternalContextAdapterImpl(
297: context);
298:
299: ica.pushCurrentTemplateName(logTag);
300:
301: try {
302: try {
303: nodeTree.init(ica, ri);
304: } catch (TemplateInitException pex) {
305: throw new ParseErrorException(pex);
306: }
307: /**
308: * pass through application level runtime exceptions
309: */
310: catch (RuntimeException e) {
311: throw e;
312: } catch (Exception e) {
313: getLog().error(
314: "Velocity.evaluate() : init exception for tag = "
315: + logTag, e);
316: }
317:
318: /*
319: * now render, and let any exceptions fly
320: */
321:
322: nodeTree.render(ica, writer);
323: } finally {
324: ica.popCurrentTemplateName();
325: }
326:
327: return true;
328: }
329:
330: return false;
331: }
332:
333: /**
334: * Invokes a currently registered Velocimacro with the parms provided
335: * and places the rendered stream into the writer.
336: *
337: * Note : currently only accepts args to the VM if they are in the context.
338: *
339: * @param vmName name of Velocimacro to call
340: * @param logTag string to be used for template name in case of error
341: * @param params args used to invoke Velocimacro. In context key format :
342: * eg "foo","bar" (rather than "$foo","$bar")
343: * @param context Context object containing data/objects used for rendering.
344: * @param writer Writer for output stream
345: * @return true if Velocimacro exists and successfully invoked, false otherwise.
346: * @throws Exception
347: */
348: public boolean invokeVelocimacro(String vmName, String logTag,
349: String params[], Context context, Writer writer)
350: throws Exception {
351: /*
352: * check parms
353: */
354:
355: if (vmName == null || params == null || context == null
356: || writer == null || logTag == null) {
357: getLog()
358: .error(
359: "VelocityEngine.invokeVelocimacro() : invalid parameter");
360: return false;
361: }
362:
363: /*
364: * does the VM exist?
365: */
366:
367: if (!ri.isVelocimacro(vmName, logTag)) {
368: getLog().error(
369: "VelocityEngine.invokeVelocimacro() : VM '"
370: + vmName + "' not registered.");
371: return false;
372: }
373:
374: /*
375: * now just create the VM call, and use evaluate
376: */
377:
378: StringBuffer construct = new StringBuffer("#");
379:
380: construct.append(vmName);
381: construct.append("(");
382:
383: for (int i = 0; i < params.length; i++) {
384: construct.append(" $");
385: construct.append(params[i]);
386: }
387:
388: construct.append(" )");
389:
390: try {
391: boolean retval = evaluate(context, writer, logTag,
392: construct.toString());
393:
394: return retval;
395: }
396: /**
397: * pass through application level runtime exceptions
398: */
399: catch (RuntimeException e) {
400: throw e;
401: } catch (Exception e) {
402: getLog().error(
403: "VelocityEngine.invokeVelocimacro() : error ", e);
404: throw e;
405: }
406: }
407:
408: /**
409: * Merges a template and puts the rendered stream into the writer.
410: * The default encoding that Velocity uses to read template files is defined in
411: * the property input.encoding and defaults to ISO-8859-1.
412: *
413: * @param templateName name of template to be used in merge
414: * @param context filled context to be used in merge
415: * @param writer writer to write template into
416: *
417: * @return true if successful, false otherwise. Errors
418: * logged to velocity log.
419: * @throws ResourceNotFoundException
420: * @throws ParseErrorException
421: * @throws MethodInvocationException
422: * @throws Exception
423: * * @deprecated Use
424: * {@link #mergeTemplate( String templateName, String encoding,
425: * Context context, Writer writer )}
426: */
427: public boolean mergeTemplate(String templateName, Context context,
428: Writer writer) throws ResourceNotFoundException,
429: ParseErrorException, MethodInvocationException, Exception {
430: return mergeTemplate(templateName, ri.getString(INPUT_ENCODING,
431: ENCODING_DEFAULT), context, writer);
432: }
433:
434: /**
435: * merges a template and puts the rendered stream into the writer
436: *
437: * @param templateName name of template to be used in merge
438: * @param encoding encoding used in template
439: * @param context filled context to be used in merge
440: * @param writer writer to write template into
441: *
442: * @return true if successful, false otherwise. Errors
443: * logged to velocity log
444: * @throws ResourceNotFoundException
445: * @throws ParseErrorException
446: * @throws MethodInvocationException
447: * @throws Exception
448: *
449: * @since Velocity v1.1
450: */
451: public boolean mergeTemplate(String templateName, String encoding,
452: Context context, Writer writer)
453: throws ResourceNotFoundException, ParseErrorException,
454: MethodInvocationException, Exception {
455: Template template = ri.getTemplate(templateName, encoding);
456:
457: if (template == null) {
458: getLog().error(
459: "Velocity.mergeTemplate() was unable to load template '"
460: + templateName + "'");
461: return false;
462: } else {
463: template.merge(context, writer);
464: return true;
465: }
466: }
467:
468: /**
469: * Returns a <code>Template</code> from the Velocity
470: * resource management system.
471: *
472: * @param name The file name of the desired template.
473: * @return The template.
474: * @throws ResourceNotFoundException if template not found
475: * from any available source.
476: * @throws ParseErrorException if template cannot be parsed due
477: * to syntax (or other) error.
478: * @throws Exception if an error occurs in template initialization
479: */
480: public Template getTemplate(String name)
481: throws ResourceNotFoundException, ParseErrorException,
482: Exception {
483: return ri.getTemplate(name);
484: }
485:
486: /**
487: * Returns a <code>Template</code> from the Velocity
488: * resource management system.
489: *
490: * @param name The file name of the desired template.
491: * @param encoding The character encoding to use for the template.
492: * @return The template.
493: * @throws ResourceNotFoundException if template not found
494: * from any available source.
495: * @throws ParseErrorException if template cannot be parsed due
496: * to syntax (or other) error.
497: * @throws Exception if an error occurs in template initialization
498: *
499: * @since Velocity v1.1
500: */
501: public Template getTemplate(String name, String encoding)
502: throws ResourceNotFoundException, ParseErrorException,
503: Exception {
504: return ri.getTemplate(name, encoding);
505: }
506:
507: /**
508: * Determines if a resource is accessable via the currently
509: * configured resource loaders.
510: * <br><br>
511: * Note that the current implementation will <b>not</b>
512: * change the state of the system in any real way - so this
513: * cannot be used to pre-load the resource cache, as the
514: * previous implementation did as a side-effect.
515: * <br><br>
516: * The previous implementation exhibited extreme lazyness and
517: * sloth, and the author has been flogged.
518: *
519: * @param resourceName name of the resource to search for
520: * @return true if found, false otherwise
521: */
522: public boolean resourceExists(String resourceName) {
523: return (ri.getLoaderNameForResource(resourceName) != null);
524: }
525:
526: /**
527: * @param resourceName
528: * @return True if the template exists.
529: * @see #resourceExists(String)
530: * @deprecated Use resourceExists(String) instead.
531: */
532: public boolean templateExists(String resourceName) {
533: return resourceExists(resourceName);
534: }
535:
536: /**
537: * Returns a convenient Log instance that wraps the current LogChute.
538: * Use this to log error messages. It has the usual methods you'd expect.
539: * @return A log object.
540: */
541: public Log getLog() {
542: return ri.getLog();
543: }
544:
545: /**
546: * @param message
547: * @deprecated Use getLog() and call warn() on it.
548: */
549: public void warn(Object message) {
550: getLog().warn(message);
551: }
552:
553: /**
554: * @param message
555: * @deprecated Use getLog() and call warn() on it.
556: */
557: public void info(Object message) {
558: getLog().info(message);
559: }
560:
561: /**
562: * @param message
563: * @deprecated Use getLog() and call warn() on it.
564: */
565: public void error(Object message) {
566: getLog().error(message);
567: }
568:
569: /**
570: * @param message
571: * @deprecated Use getLog() and call warn() on it.
572: */
573: public void debug(Object message) {
574: getLog().debug(message);
575: }
576:
577: /**
578: * <p>
579: * Sets an application attribute (which can be any Object) that will be
580: * accessible from any component of the system that gets a
581: * RuntimeServices. This allows communication between the application
582: * environment and custom pluggable components of the Velocity engine,
583: * such as ResourceLoaders and LogChutes.
584: * </p>
585: *
586: * <p>
587: * Note that there is no enforcement or rules for the key
588: * used - it is up to the application developer. However, to
589: * help make the intermixing of components possible, using
590: * the target Class name (e.g. com.foo.bar ) as the key
591: * might help avoid collision.
592: * </p>
593: *
594: * @param key object 'name' under which the object is stored
595: * @param value object to store under this key
596: */
597: public void setApplicationAttribute(Object key, Object value) {
598: ri.setApplicationAttribute(key, value);
599: }
600:
601: /**
602: * <p>
603: * Return an application attribute (which can be any Object)
604: * that was set by the application in order to be accessible from
605: * any component of the system that gets a RuntimeServices.
606: * This allows communication between the application
607: * environment and custom pluggable components of the
608: * Velocity engine, such as ResourceLoaders and LogChutes.
609: * </p>
610: *
611: * @param key object 'name' under which the object is stored
612: * @return value object to store under this key
613: */
614: public Object getApplicationAttribute(Object key) {
615: return ri.getApplicationAttribute(key);
616: }
617:
618: }
|