0001: package org.apache.velocity.runtime;
0002:
0003: /*
0004: * Licensed to the Apache Software Foundation (ASF) under one
0005: * or more contributor license agreements. See the NOTICE file
0006: * distributed with this work for additional information
0007: * regarding copyright ownership. The ASF licenses this file
0008: * to you under the Apache License, Version 2.0 (the
0009: * "License"); you may not use this file except in compliance
0010: * with the License. You may obtain a copy of the License at
0011: *
0012: * http://www.apache.org/licenses/LICENSE-2.0
0013: *
0014: * Unless required by applicable law or agreed to in writing,
0015: * software distributed under the License is distributed on an
0016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
0017: * KIND, either express or implied. See the License for the
0018: * specific language governing permissions and limitations
0019: * under the License.
0020: */
0021:
0022: import java.io.File;
0023: import java.io.IOException;
0024: import java.io.InputStream;
0025: import java.io.Reader;
0026: import java.util.Enumeration;
0027: import java.util.HashMap;
0028: import java.util.Hashtable;
0029: import java.util.Map;
0030: import java.util.Properties;
0031:
0032: import org.apache.commons.collections.ExtendedProperties;
0033: import org.apache.velocity.Template;
0034: import org.apache.velocity.app.event.EventCartridge;
0035: import org.apache.velocity.app.event.EventHandler;
0036: import org.apache.velocity.app.event.IncludeEventHandler;
0037: import org.apache.velocity.app.event.InvalidReferenceEventHandler;
0038: import org.apache.velocity.app.event.MethodExceptionEventHandler;
0039: import org.apache.velocity.app.event.NullSetEventHandler;
0040: import org.apache.velocity.app.event.ReferenceInsertionEventHandler;
0041: import org.apache.velocity.exception.ParseErrorException;
0042: import org.apache.velocity.exception.ResourceNotFoundException;
0043: import org.apache.velocity.runtime.directive.Directive;
0044: import org.apache.velocity.runtime.log.Log;
0045: import org.apache.velocity.runtime.log.LogManager;
0046: import org.apache.velocity.runtime.parser.ParseException;
0047: import org.apache.velocity.runtime.parser.Parser;
0048: import org.apache.velocity.runtime.parser.node.SimpleNode;
0049: import org.apache.velocity.runtime.resource.ContentResource;
0050: import org.apache.velocity.runtime.resource.ResourceManager;
0051: import org.apache.velocity.util.ClassUtils;
0052: import org.apache.velocity.util.RuntimeServicesAware;
0053: import org.apache.velocity.util.StringUtils;
0054: import org.apache.velocity.util.introspection.Introspector;
0055: import org.apache.velocity.util.introspection.Uberspect;
0056: import org.apache.velocity.util.introspection.UberspectLoggable;
0057:
0058: /**
0059: * This is the Runtime system for Velocity. It is the
0060: * single access point for all functionality in Velocity.
0061: * It adheres to the mediator pattern and is the only
0062: * structure that developers need to be familiar with
0063: * in order to get Velocity to perform.
0064: *
0065: * The Runtime will also cooperate with external
0066: * systems like Turbine. Runtime properties can
0067: * set and then the Runtime is initialized.
0068: *
0069: * Turbine, for example, knows where the templates
0070: * are to be loaded from, and where the Velocity
0071: * log file should be placed.
0072: *
0073: * So in the case of Velocity cooperating with Turbine
0074: * the code might look something like the following:
0075: *
0076: * <blockquote><code><pre>
0077: * ri.setProperty(Runtime.FILE_RESOURCE_LOADER_PATH, templatePath);
0078: * ri.setProperty(Runtime.RUNTIME_LOG, pathToVelocityLog);
0079: * ri.init();
0080: * </pre></code></blockquote>
0081: *
0082: * <pre>
0083: * -----------------------------------------------------------------------
0084: * N O T E S O N R U N T I M E I N I T I A L I Z A T I O N
0085: * -----------------------------------------------------------------------
0086: * init()
0087: *
0088: * If init() is called by itself the RuntimeInstance will initialize
0089: * with a set of default values.
0090: * -----------------------------------------------------------------------
0091: * init(String/Properties)
0092: *
0093: * In this case the default velocity properties are layed down
0094: * first to provide a solid base, then any properties provided
0095: * in the given properties object will override the corresponding
0096: * default property.
0097: * -----------------------------------------------------------------------
0098: * </pre>
0099: *
0100: * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
0101: * @author <a href="mailto:jlb@houseofdistraction.com">Jeff Bowden</a>
0102: * @author <a href="mailto:geirm@optonline.net">Geir Magusson Jr.</a>
0103: * @version $Id: RuntimeInstance.java 474051 2006-11-12 21:42:02Z henning $
0104: */
0105: public class RuntimeInstance implements RuntimeConstants,
0106: RuntimeServices {
0107: /**
0108: * VelocimacroFactory object to manage VMs
0109: */
0110: private VelocimacroFactory vmFactory = null;
0111:
0112: /**
0113: * The Runtime logger. We start with an instance of
0114: * a 'primordial logger', which just collects log messages
0115: * then, when the log system is initialized, all the
0116: * messages get dumpted out of the primordial one into the real one.
0117: */
0118: private Log log = new Log();
0119:
0120: /**
0121: * The Runtime parser pool
0122: */
0123: private ParserPool parserPool;
0124:
0125: /**
0126: * Indicate whether the Runtime is in the midst of initialization.
0127: */
0128: private boolean initializing = false;
0129:
0130: /**
0131: * Indicate whether the Runtime has been fully initialized.
0132: */
0133: private boolean initialized = false;
0134:
0135: /**
0136: * These are the properties that are laid down over top
0137: * of the default properties when requested.
0138: */
0139: private ExtendedProperties overridingProperties = null;
0140:
0141: /**
0142: * This is a hashtable of initialized directives.
0143: * The directives that populate this hashtable are
0144: * taken from the RUNTIME_DEFAULT_DIRECTIVES
0145: * property file. This hashtable is passed
0146: * to each parser that is created.
0147: */
0148: private Hashtable runtimeDirectives;
0149:
0150: /**
0151: * Object that houses the configuration options for
0152: * the velocity runtime. The ExtendedProperties object allows
0153: * the convenient retrieval of a subset of properties.
0154: * For example all the properties for a resource loader
0155: * can be retrieved from the main ExtendedProperties object
0156: * using something like the following:
0157: *
0158: * ExtendedProperties loaderConfiguration =
0159: * configuration.subset(loaderID);
0160: *
0161: * And a configuration is a lot more convenient to deal
0162: * with then conventional properties objects, or Maps.
0163: */
0164: private ExtendedProperties configuration = new ExtendedProperties();
0165:
0166: private ResourceManager resourceManager = null;
0167:
0168: /**
0169: * This stores the engine-wide set of event handlers. Event handlers for
0170: * each specific merge are stored in the context.
0171: */
0172: private EventCartridge eventCartridge = null;
0173:
0174: /*
0175: * Each runtime instance has it's own introspector
0176: * to ensure that each instance is completely separate.
0177: */
0178: private Introspector introspector = null;
0179:
0180: /*
0181: * Opaque reference to something specificed by the
0182: * application for use in application supplied/specified
0183: * pluggable components
0184: */
0185: private Map applicationAttributes = null;
0186:
0187: private Uberspect uberSpect;
0188:
0189: /**
0190: * Creates a new RuntimeInstance object.
0191: */
0192: public RuntimeInstance() {
0193: /*
0194: * create a VM factory, introspector, and application attributes
0195: */
0196: vmFactory = new VelocimacroFactory(this );
0197:
0198: /*
0199: * make a new introspector and initialize it
0200: */
0201: introspector = new Introspector(getLog());
0202:
0203: /*
0204: * and a store for the application attributes
0205: */
0206: applicationAttributes = new HashMap();
0207: }
0208:
0209: /**
0210: * This is the primary initialization method in the Velocity
0211: * Runtime. The systems that are setup/initialized here are
0212: * as follows:
0213: *
0214: * <ul>
0215: * <li>Logging System</li>
0216: * <li>ResourceManager</li>
0217: * <li>EventHandler</li>
0218: * <li>Parser Pool</li>
0219: * <li>Global Cache</li>
0220: * <li>Static Content Include System</li>
0221: * <li>Velocimacro System</li>
0222: * </ul>
0223: * @throws Exception When an error occured during initialization.
0224: */
0225: public synchronized void init() throws Exception {
0226: if (!initialized && !initializing) {
0227: initializing = true;
0228:
0229: log
0230: .trace("*******************************************************************");
0231: log
0232: .debug("Starting Apache Velocity v@build.version@ (compiled: @build.time@)");
0233: log.trace("RuntimeInstance initializing.");
0234:
0235: initializeProperties();
0236: initializeLog();
0237: initializeResourceManager();
0238: initializeDirectives();
0239: initializeEventHandlers();
0240: initializeParserPool();
0241:
0242: initializeIntrospection();
0243: /*
0244: * initialize the VM Factory. It will use the properties
0245: * accessable from Runtime, so keep this here at the end.
0246: */
0247: vmFactory.initVelocimacro();
0248:
0249: log.trace("RuntimeInstance successfully initialized.");
0250:
0251: initialized = true;
0252: initializing = false;
0253: }
0254: }
0255:
0256: /**
0257: * Returns true if the RuntimeInstance has been successfully initialized.
0258: * @return True if the RuntimeInstance has been successfully initialized.
0259: */
0260: public boolean isInitialized() {
0261: return initialized;
0262: }
0263:
0264: /**
0265: * Gets the classname for the Uberspect introspection package and
0266: * instantiates an instance.
0267: */
0268: private void initializeIntrospection() throws Exception {
0269: String rm = getString(RuntimeConstants.UBERSPECT_CLASSNAME);
0270:
0271: if (rm != null && rm.length() > 0) {
0272: Object o = null;
0273:
0274: try {
0275: o = ClassUtils.getNewInstance(rm);
0276: } catch (ClassNotFoundException cnfe) {
0277: String err = "The specified class for Uberspect ("
0278: + rm
0279: + ") does not exist or is not accessible to the current classloader.";
0280: log.error(err);
0281: throw new Exception(err);
0282: }
0283:
0284: if (!(o instanceof Uberspect)) {
0285: String err = "The specified class for Uberspect (" + rm
0286: + ") does not implement "
0287: + Uberspect.class.getName()
0288: + "; Velocity is not initialized correctly.";
0289:
0290: log.error(err);
0291: throw new Exception(err);
0292: }
0293:
0294: uberSpect = (Uberspect) o;
0295:
0296: if (uberSpect instanceof UberspectLoggable) {
0297: ((UberspectLoggable) uberSpect).setLog(getLog());
0298: }
0299:
0300: if (uberSpect instanceof RuntimeServicesAware) {
0301: ((RuntimeServicesAware) uberSpect)
0302: .setRuntimeServices(this );
0303: }
0304:
0305: uberSpect.init();
0306: } else {
0307: /*
0308: * someone screwed up. Lets not fool around...
0309: */
0310:
0311: String err = "It appears that no class was specified as the"
0312: + " Uberspect. Please ensure that all configuration"
0313: + " information is correct.";
0314:
0315: log.error(err);
0316: throw new Exception(err);
0317: }
0318: }
0319:
0320: /**
0321: * Initializes the Velocity Runtime with properties file.
0322: * The properties file may be in the file system proper,
0323: * or the properties file may be in the classpath.
0324: */
0325: private void setDefaultProperties() {
0326: InputStream inputStream = null;
0327: try {
0328: inputStream = getClass().getResourceAsStream(
0329: '/' + DEFAULT_RUNTIME_PROPERTIES);
0330:
0331: configuration.load(inputStream);
0332:
0333: if (log.isDebugEnabled()) {
0334: log.debug("Default Properties File: "
0335: + new File(DEFAULT_RUNTIME_PROPERTIES)
0336: .getPath());
0337: }
0338:
0339: } catch (IOException ioe) {
0340: log.error(
0341: "Cannot get Velocity Runtime default properties!",
0342: ioe);
0343: } finally {
0344: try {
0345: if (inputStream != null) {
0346: inputStream.close();
0347: }
0348: } catch (IOException ioe) {
0349: log
0350: .error(
0351: "Cannot close Velocity Runtime default properties!",
0352: ioe);
0353: }
0354: }
0355: }
0356:
0357: /**
0358: * Allows an external system to set a property in
0359: * the Velocity Runtime.
0360: *
0361: * @param key property key
0362: * @param value property value
0363: */
0364: public void setProperty(String key, Object value) {
0365: if (overridingProperties == null) {
0366: overridingProperties = new ExtendedProperties();
0367: }
0368:
0369: overridingProperties.setProperty(key, value);
0370: }
0371:
0372: /**
0373: * Allow an external system to set an ExtendedProperties
0374: * object to use. This is useful where the external
0375: * system also uses the ExtendedProperties class and
0376: * the velocity configuration is a subset of
0377: * parent application's configuration. This is
0378: * the case with Turbine.
0379: *
0380: * @param configuration
0381: */
0382: public void setConfiguration(ExtendedProperties configuration) {
0383: if (overridingProperties == null) {
0384: overridingProperties = configuration;
0385: } else {
0386: // Avoid possible ConcurrentModificationException
0387: if (overridingProperties != configuration) {
0388: overridingProperties.combine(configuration);
0389: }
0390: }
0391: }
0392:
0393: /**
0394: * Add a property to the configuration. If it already
0395: * exists then the value stated here will be added
0396: * to the configuration entry. For example, if
0397: *
0398: * resource.loader = file
0399: *
0400: * is already present in the configuration and you
0401: *
0402: * addProperty("resource.loader", "classpath")
0403: *
0404: * Then you will end up with a Vector like the
0405: * following:
0406: *
0407: * ["file", "classpath"]
0408: *
0409: * @param key
0410: * @param value
0411: */
0412: public void addProperty(String key, Object value) {
0413: if (overridingProperties == null) {
0414: overridingProperties = new ExtendedProperties();
0415: }
0416:
0417: overridingProperties.addProperty(key, value);
0418: }
0419:
0420: /**
0421: * Clear the values pertaining to a particular
0422: * property.
0423: *
0424: * @param key of property to clear
0425: */
0426: public void clearProperty(String key) {
0427: if (overridingProperties != null) {
0428: overridingProperties.clearProperty(key);
0429: }
0430: }
0431:
0432: /**
0433: * Allows an external caller to get a property. The calling
0434: * routine is required to know the type, as this routine
0435: * will return an Object, as that is what properties can be.
0436: *
0437: * @param key property to return
0438: * @return Value of the property or null if it does not exist.
0439: */
0440: public Object getProperty(String key) {
0441: Object o = null;
0442:
0443: /**
0444: * Before initialization, check the user-entered properties first.
0445: */
0446: if (!initialized && !initializing
0447: && overridingProperties != null) {
0448: o = overridingProperties.get(key);
0449: }
0450:
0451: /**
0452: * After initialization, configuration will hold all properties.
0453: */
0454: if (o == null) {
0455: o = configuration.getProperty(key);
0456: }
0457: if (o instanceof String) {
0458: return StringUtils.nullTrim((String) o);
0459: } else {
0460: return o;
0461: }
0462: }
0463:
0464: /**
0465: * Initialize Velocity properties, if the default
0466: * properties have not been laid down first then
0467: * do so. Then proceed to process any overriding
0468: * properties. Laying down the default properties
0469: * gives a much greater chance of having a
0470: * working system.
0471: */
0472: private void initializeProperties() {
0473: /*
0474: * Always lay down the default properties first as
0475: * to provide a solid base.
0476: */
0477: if (configuration.isInitialized() == false) {
0478: setDefaultProperties();
0479: }
0480:
0481: if (overridingProperties != null) {
0482: configuration.combine(overridingProperties);
0483: }
0484: }
0485:
0486: /**
0487: * Initialize the Velocity Runtime with a Properties
0488: * object.
0489: *
0490: * @param p
0491: * @throws Exception When an error occurs during initialization.
0492: */
0493: public void init(Properties p) throws Exception {
0494: overridingProperties = ExtendedProperties.convertProperties(p);
0495: init();
0496: }
0497:
0498: /**
0499: * Initialize the Velocity Runtime with the name of
0500: * ExtendedProperties object.
0501: *
0502: * @param configurationFile
0503: * @throws Exception When an error occurs during initialization.
0504: */
0505: public void init(String configurationFile) throws Exception {
0506: overridingProperties = new ExtendedProperties(configurationFile);
0507: init();
0508: }
0509:
0510: private void initializeResourceManager() throws Exception {
0511: /*
0512: * Which resource manager?
0513: */
0514:
0515: String rm = getString(RuntimeConstants.RESOURCE_MANAGER_CLASS);
0516:
0517: if (rm != null && rm.length() > 0) {
0518: /*
0519: * if something was specified, then make one.
0520: * if that isn't a ResourceManager, consider
0521: * this a huge error and throw
0522: */
0523:
0524: Object o = null;
0525:
0526: try {
0527: o = ClassUtils.getNewInstance(rm);
0528: } catch (ClassNotFoundException cnfe) {
0529: String err = "The specified class for ResourceManager ("
0530: + rm
0531: + ") does not exist or is not accessible to the current classloader.";
0532: log.error(err);
0533: throw new Exception(err);
0534: }
0535:
0536: if (!(o instanceof ResourceManager)) {
0537: String err = "The specified class for ResourceManager ("
0538: + rm
0539: + ") does not implement "
0540: + ResourceManager.class.getName()
0541: + "; Velocity is not initialized correctly.";
0542:
0543: log.error(err);
0544: throw new Exception(err);
0545: }
0546:
0547: resourceManager = (ResourceManager) o;
0548:
0549: resourceManager.initialize(this );
0550: } else {
0551: /*
0552: * someone screwed up. Lets not fool around...
0553: */
0554:
0555: String err = "It appears that no class was specified as the"
0556: + " ResourceManager. Please ensure that all configuration"
0557: + " information is correct.";
0558:
0559: log.error(err);
0560: throw new Exception(err);
0561: }
0562: }
0563:
0564: private void initializeEventHandlers() throws Exception {
0565:
0566: eventCartridge = new EventCartridge();
0567:
0568: /**
0569: * For each type of event handler, get the class name, instantiate it, and store it.
0570: */
0571:
0572: String[] referenceinsertion = configuration
0573: .getStringArray(RuntimeConstants.EVENTHANDLER_REFERENCEINSERTION);
0574: if (referenceinsertion != null) {
0575: for (int i = 0; i < referenceinsertion.length; i++) {
0576: EventHandler ev = initializeSpecificEventHandler(
0577: referenceinsertion[i],
0578: RuntimeConstants.EVENTHANDLER_REFERENCEINSERTION,
0579: ReferenceInsertionEventHandler.class);
0580: if (ev != null)
0581: eventCartridge
0582: .addReferenceInsertionEventHandler((ReferenceInsertionEventHandler) ev);
0583: }
0584: }
0585:
0586: String[] nullset = configuration
0587: .getStringArray(RuntimeConstants.EVENTHANDLER_NULLSET);
0588: if (nullset != null) {
0589: for (int i = 0; i < nullset.length; i++) {
0590: EventHandler ev = initializeSpecificEventHandler(
0591: nullset[i],
0592: RuntimeConstants.EVENTHANDLER_NULLSET,
0593: NullSetEventHandler.class);
0594: if (ev != null)
0595: eventCartridge
0596: .addNullSetEventHandler((NullSetEventHandler) ev);
0597: }
0598: }
0599:
0600: String[] methodexception = configuration
0601: .getStringArray(RuntimeConstants.EVENTHANDLER_METHODEXCEPTION);
0602: if (methodexception != null) {
0603: for (int i = 0; i < methodexception.length; i++) {
0604: EventHandler ev = initializeSpecificEventHandler(
0605: methodexception[i],
0606: RuntimeConstants.EVENTHANDLER_METHODEXCEPTION,
0607: MethodExceptionEventHandler.class);
0608: if (ev != null)
0609: eventCartridge
0610: .addMethodExceptionHandler((MethodExceptionEventHandler) ev);
0611: }
0612: }
0613:
0614: String[] includeHandler = configuration
0615: .getStringArray(RuntimeConstants.EVENTHANDLER_INCLUDE);
0616: if (includeHandler != null) {
0617: for (int i = 0; i < includeHandler.length; i++) {
0618: EventHandler ev = initializeSpecificEventHandler(
0619: includeHandler[i],
0620: RuntimeConstants.EVENTHANDLER_INCLUDE,
0621: IncludeEventHandler.class);
0622: if (ev != null)
0623: eventCartridge
0624: .addIncludeEventHandler((IncludeEventHandler) ev);
0625: }
0626: }
0627:
0628: String[] invalidReferenceSet = configuration
0629: .getStringArray(RuntimeConstants.EVENTHANDLER_INVALIDREFERENCES);
0630: if (invalidReferenceSet != null) {
0631: for (int i = 0; i < invalidReferenceSet.length; i++) {
0632: EventHandler ev = initializeSpecificEventHandler(
0633: invalidReferenceSet[i],
0634: RuntimeConstants.EVENTHANDLER_INVALIDREFERENCES,
0635: InvalidReferenceEventHandler.class);
0636: if (ev != null) {
0637: eventCartridge
0638: .addInvalidReferenceEventHandler((InvalidReferenceEventHandler) ev);
0639: }
0640: }
0641: }
0642:
0643: }
0644:
0645: private EventHandler initializeSpecificEventHandler(
0646: String classname, String paramName,
0647: Class EventHandlerInterface) throws Exception {
0648: if (classname != null && classname.length() > 0) {
0649: Object o = null;
0650: try {
0651: o = ClassUtils.getNewInstance(classname);
0652: } catch (ClassNotFoundException cnfe) {
0653: String err = "The specified class for "
0654: + paramName
0655: + " ("
0656: + classname
0657: + ") does not exist or is not accessible to the current classloader.";
0658: log.error(err);
0659: throw new Exception(err);
0660: }
0661:
0662: if (!EventHandlerInterface
0663: .isAssignableFrom(EventHandlerInterface)) {
0664: String err = "The specified class for " + paramName
0665: + " (" + classname + ") does not implement "
0666: + EventHandlerInterface.getName()
0667: + "; Velocity is not initialized correctly.";
0668:
0669: log.error(err);
0670: throw new Exception(err);
0671: }
0672:
0673: EventHandler ev = (EventHandler) o;
0674: if (ev instanceof RuntimeServicesAware)
0675: ((RuntimeServicesAware) ev).setRuntimeServices(this );
0676: return ev;
0677:
0678: } else
0679: return null;
0680: }
0681:
0682: /**
0683: * Initialize the Velocity logging system.
0684: *
0685: * @throws Exception
0686: */
0687: private void initializeLog() throws Exception {
0688: // since the Log we started with was just placeholding,
0689: // let's update it with the real LogChute settings.
0690: LogManager.updateLog(this .log, this );
0691: }
0692:
0693: /**
0694: * This methods initializes all the directives
0695: * that are used by the Velocity Runtime. The
0696: * directives to be initialized are listed in
0697: * the RUNTIME_DEFAULT_DIRECTIVES properties
0698: * file.
0699: *
0700: * @throws Exception
0701: */
0702: private void initializeDirectives() throws Exception {
0703: /*
0704: * Initialize the runtime directive table.
0705: * This will be used for creating parsers.
0706: */
0707: runtimeDirectives = new Hashtable();
0708:
0709: Properties directiveProperties = new Properties();
0710:
0711: /*
0712: * Grab the properties file with the list of directives
0713: * that we should initialize.
0714: */
0715:
0716: InputStream inputStream = null;
0717:
0718: try {
0719: inputStream = getClass().getResourceAsStream(
0720: '/' + DEFAULT_RUNTIME_DIRECTIVES);
0721:
0722: if (inputStream == null) {
0723: throw new Exception(
0724: "Error loading directive.properties! "
0725: + "Something is very wrong if these properties "
0726: + "aren't being located. Either your Velocity "
0727: + "distribution is incomplete or your Velocity "
0728: + "jar file is corrupted!");
0729: }
0730:
0731: directiveProperties.load(inputStream);
0732:
0733: } catch (IOException ioe) {
0734: log.error("Error while loading directive properties!", ioe);
0735: } finally {
0736: try {
0737: if (inputStream != null) {
0738: inputStream.close();
0739: }
0740: } catch (IOException ioe) {
0741: log.error("Cannot close directive properties!", ioe);
0742: }
0743: }
0744:
0745: /*
0746: * Grab all the values of the properties. These
0747: * are all class names for example:
0748: *
0749: * org.apache.velocity.runtime.directive.Foreach
0750: */
0751: Enumeration directiveClasses = directiveProperties.elements();
0752:
0753: while (directiveClasses.hasMoreElements()) {
0754: String directiveClass = (String) directiveClasses
0755: .nextElement();
0756: loadDirective(directiveClass);
0757: log.debug("Loaded System Directive: " + directiveClass);
0758: }
0759:
0760: /*
0761: * now the user's directives
0762: */
0763:
0764: String[] userdirective = configuration
0765: .getStringArray("userdirective");
0766:
0767: for (int i = 0; i < userdirective.length; i++) {
0768: loadDirective(userdirective[i]);
0769: if (log.isInfoEnabled()) {
0770: log.info("Loaded User Directive: " + userdirective[i]);
0771: }
0772: }
0773:
0774: }
0775:
0776: /**
0777: * instantiates and loads the directive with some basic checks
0778: *
0779: * @param directiveClass classname of directive to load
0780: */
0781: private void loadDirective(String directiveClass) {
0782: try {
0783: Object o = ClassUtils.getNewInstance(directiveClass);
0784:
0785: if (o instanceof Directive) {
0786: Directive directive = (Directive) o;
0787: runtimeDirectives.put(directive.getName(), directive);
0788: } else {
0789: log.error(directiveClass + " does not implement "
0790: + Directive.class.getName()
0791: + "; it cannot be loaded.");
0792: }
0793: }
0794: // The ugly threesome: ClassNotFoundException,
0795: // IllegalAccessException, InstantiationException.
0796: // Ignore Findbugs complaint for now.
0797: catch (Exception e) {
0798: log.error("Failed to load Directive: " + directiveClass, e);
0799: }
0800: }
0801:
0802: /**
0803: * Initializes the Velocity parser pool.
0804: */
0805: private void initializeParserPool() throws Exception {
0806: /*
0807: * Which parser pool?
0808: */
0809: String pp = getString(RuntimeConstants.PARSER_POOL_CLASS);
0810:
0811: if (pp != null && pp.length() > 0) {
0812: /*
0813: * if something was specified, then make one.
0814: * if that isn't a ParserPool, consider
0815: * this a huge error and throw
0816: */
0817:
0818: Object o = null;
0819:
0820: try {
0821: o = ClassUtils.getNewInstance(pp);
0822: } catch (ClassNotFoundException cnfe) {
0823: String err = "The specified class for ParserPool ("
0824: + pp
0825: + ") does not exist (or is not accessible to the current classloader.";
0826: log.error(err);
0827: throw new Exception(err);
0828: }
0829:
0830: if (!(o instanceof ParserPool)) {
0831: String err = "The specified class for ParserPool ("
0832: + pp + ") does not implement "
0833: + ParserPool.class
0834: + " Velocity not initialized correctly.";
0835:
0836: log.error(err);
0837: throw new Exception(err);
0838: }
0839:
0840: parserPool = (ParserPool) o;
0841:
0842: parserPool.initialize(this );
0843: } else {
0844: /*
0845: * someone screwed up. Lets not fool around...
0846: */
0847:
0848: String err = "It appears that no class was specified as the"
0849: + " ParserPool. Please ensure that all configuration"
0850: + " information is correct.";
0851:
0852: log.error(err);
0853: throw new Exception(err);
0854: }
0855:
0856: }
0857:
0858: /**
0859: * Returns a JavaCC generated Parser.
0860: *
0861: * @return Parser javacc generated parser
0862: */
0863: public Parser createNewParser() {
0864: /* must be initialized before we use runtimeDirectives */
0865: if (!initialized && !initializing) {
0866: log
0867: .debug("Velocity was not initialized! Calling init()...");
0868: try {
0869: init();
0870: } catch (Exception e) {
0871: getLog().error("Could not auto-initialize Velocity", e);
0872: throw new IllegalStateException(
0873: "Velocity could not be initialized!");
0874: }
0875: }
0876:
0877: Parser parser = new Parser(this );
0878: parser.setDirectives(runtimeDirectives);
0879: return parser;
0880: }
0881:
0882: /**
0883: * Parse the input and return the root of
0884: * AST node structure.
0885: * <br><br>
0886: * In the event that it runs out of parsers in the
0887: * pool, it will create and let them be GC'd
0888: * dynamically, logging that it has to do that. This
0889: * is considered an exceptional condition. It is
0890: * expected that the user will set the
0891: * PARSER_POOL_SIZE property appropriately for their
0892: * application. We will revisit this.
0893: *
0894: * @param reader Reader retrieved by a resource loader
0895: * @param templateName name of the template being parsed
0896: * @return A root node representing the template as an AST tree.
0897: * @throws ParseException When the template could not be parsed.
0898: */
0899: public SimpleNode parse(Reader reader, String templateName)
0900: throws ParseException {
0901: /*
0902: * do it and dump the VM namespace for this template
0903: */
0904: return parse(reader, templateName, true);
0905: }
0906:
0907: /**
0908: * Parse the input and return the root of the AST node structure.
0909: *
0910: * @param reader Reader retrieved by a resource loader
0911: * @param templateName name of the template being parsed
0912: * @param dumpNamespace flag to dump the Velocimacro namespace for this template
0913: * @return A root node representing the template as an AST tree.
0914: * @throws ParseException When the template could not be parsed.
0915: */
0916: public SimpleNode parse(Reader reader, String templateName,
0917: boolean dumpNamespace) throws ParseException {
0918: /* must be initialized before using parserPool */
0919: if (!initialized && !initializing) {
0920: log
0921: .debug("Velocity was not initialized! Calling init()...");
0922: try {
0923: init();
0924: } catch (Exception e) {
0925: getLog().error("Could not auto-initialize Velocity", e);
0926: throw new IllegalStateException(
0927: "Velocity could not be initialized!");
0928: }
0929: }
0930:
0931: SimpleNode ast = null;
0932: Parser parser = (Parser) parserPool.get();
0933:
0934: if (parser == null) {
0935: /*
0936: * if we couldn't get a parser from the pool
0937: * make one and log it.
0938: */
0939:
0940: if (log.isInfoEnabled()) {
0941: log
0942: .info("Runtime : ran out of parsers. Creating a new one. "
0943: + " Please increment the parser.pool.size property."
0944: + " The current value is too small.");
0945: }
0946:
0947: parser = createNewParser();
0948:
0949: }
0950:
0951: /*
0952: * now, if we have a parser
0953: */
0954:
0955: if (parser != null) {
0956: try {
0957: /*
0958: * dump namespace if we are told to. Generally, you want to
0959: * do this - you don't in special circumstances, such as
0960: * when a VM is getting init()-ed & parsed
0961: */
0962:
0963: if (dumpNamespace) {
0964: dumpVMNamespace(templateName);
0965: }
0966:
0967: ast = parser.parse(reader, templateName);
0968: } finally {
0969: /*
0970: * put it back
0971: */
0972: parserPool.put(parser);
0973:
0974: }
0975: } else {
0976: log
0977: .error("Runtime : ran out of parsers and unable to create more.");
0978: }
0979: return ast;
0980: }
0981:
0982: /**
0983: * Returns a <code>Template</code> from the resource manager.
0984: * This method assumes that the character encoding of the
0985: * template is set by the <code>input.encoding</code>
0986: * property. The default is "ISO-8859-1"
0987: *
0988: * @param name The file name of the desired template.
0989: * @return The template.
0990: * @throws ResourceNotFoundException if template not found
0991: * from any available source.
0992: * @throws ParseErrorException if template cannot be parsed due
0993: * to syntax (or other) error.
0994: * @throws Exception if an error occurs in template initialization
0995: */
0996: public Template getTemplate(String name)
0997: throws ResourceNotFoundException, ParseErrorException,
0998: Exception {
0999: return getTemplate(name, getString(INPUT_ENCODING,
1000: ENCODING_DEFAULT));
1001: }
1002:
1003: /**
1004: * Returns a <code>Template</code> from the resource manager
1005: *
1006: * @param name The name of the desired template.
1007: * @param encoding Character encoding of the template
1008: * @return The template.
1009: * @throws ResourceNotFoundException if template not found
1010: * from any available source.
1011: * @throws ParseErrorException if template cannot be parsed due
1012: * to syntax (or other) error.
1013: * @throws Exception if an error occurs in template initialization
1014: */
1015: public Template getTemplate(String name, String encoding)
1016: throws ResourceNotFoundException, ParseErrorException,
1017: Exception {
1018: /* must be initialized before using resourceManager */
1019: if (!initialized && !initializing) {
1020: log.info("Velocity not initialized yet. Calling init()...");
1021: init();
1022: }
1023:
1024: return (Template) resourceManager.getResource(name,
1025: ResourceManager.RESOURCE_TEMPLATE, encoding);
1026: }
1027:
1028: /**
1029: * Returns a static content resource from the
1030: * resource manager. Uses the current value
1031: * if INPUT_ENCODING as the character encoding.
1032: *
1033: * @param name Name of content resource to get
1034: * @return parsed ContentResource object ready for use
1035: * @throws ResourceNotFoundException if template not found
1036: * from any available source.
1037: * @throws ParseErrorException When the template could not be parsed.
1038: * @throws Exception Any other error.
1039: */
1040: public ContentResource getContent(String name)
1041: throws ResourceNotFoundException, ParseErrorException,
1042: Exception {
1043: /*
1044: * the encoding is irrelvant as we don't do any converstion
1045: * the bytestream should be dumped to the output stream
1046: */
1047:
1048: return getContent(name, getString(INPUT_ENCODING,
1049: ENCODING_DEFAULT));
1050: }
1051:
1052: /**
1053: * Returns a static content resource from the
1054: * resource manager.
1055: *
1056: * @param name Name of content resource to get
1057: * @param encoding Character encoding to use
1058: * @return parsed ContentResource object ready for use
1059: * @throws ResourceNotFoundException if template not found
1060: * from any available source.
1061: * @throws ParseErrorException When the template could not be parsed.
1062: * @throws Exception Any other error.
1063: */
1064: public ContentResource getContent(String name, String encoding)
1065: throws ResourceNotFoundException, ParseErrorException,
1066: Exception {
1067: /* must be initialized before using resourceManager */
1068: if (!initialized && !initializing) {
1069: log.info("Velocity not initialized yet. Calling init()...");
1070: init();
1071: }
1072:
1073: return (ContentResource) resourceManager.getResource(name,
1074: ResourceManager.RESOURCE_CONTENT, encoding);
1075: }
1076:
1077: /**
1078: * Determines if a template exists and returns name of the loader that
1079: * provides it. This is a slightly less hokey way to support
1080: * the Velocity.resourceExists() utility method, which was broken
1081: * when per-template encoding was introduced. We can revisit this.
1082: *
1083: * @param resourceName Name of template or content resource
1084: * @return class name of loader than can provide it
1085: */
1086: public String getLoaderNameForResource(String resourceName) {
1087: /* must be initialized before using resourceManager */
1088: if (!initialized && !initializing) {
1089: log
1090: .debug("Velocity was not initialized! Calling init()...");
1091: try {
1092: init();
1093: } catch (Exception e) {
1094: getLog().error("Could not initialize Velocity", e);
1095: throw new IllegalStateException(
1096: "Velocity could not be initialized!");
1097: }
1098: }
1099:
1100: return resourceManager.getLoaderNameForResource(resourceName);
1101: }
1102:
1103: /**
1104: * Returns a convenient Log instance that wraps the current LogChute.
1105: * Use this to log error messages. It has the usual methods.
1106: *
1107: * @return A convenience Log instance that wraps the current LogChute.
1108: */
1109: public Log getLog() {
1110: return log;
1111: }
1112:
1113: /**
1114: * @deprecated Use getLog() and call warn() on it.
1115: * @see Log#warn(Object)
1116: * @param message The message to log.
1117: */
1118: public void warn(Object message) {
1119: getLog().warn(message);
1120: }
1121:
1122: /**
1123: * @deprecated Use getLog() and call info() on it.
1124: * @see Log#info(Object)
1125: * @param message The message to log.
1126: */
1127: public void info(Object message) {
1128: getLog().info(message);
1129: }
1130:
1131: /**
1132: * @deprecated Use getLog() and call error() on it.
1133: * @see Log#error(Object)
1134: * @param message The message to log.
1135: */
1136: public void error(Object message) {
1137: getLog().error(message);
1138: }
1139:
1140: /**
1141: * @deprecated Use getLog() and call debug() on it.
1142: * @see Log#debug(Object)
1143: * @param message The message to log.
1144: */
1145: public void debug(Object message) {
1146: getLog().debug(message);
1147: }
1148:
1149: /**
1150: * String property accessor method with default to hide the
1151: * configuration implementation.
1152: *
1153: * @param key property key
1154: * @param defaultValue default value to return if key not
1155: * found in resource manager.
1156: * @return value of key or default
1157: */
1158: public String getString(String key, String defaultValue) {
1159: return configuration.getString(key, defaultValue);
1160: }
1161:
1162: /**
1163: * Returns the appropriate VelocimacroProxy object if strVMname
1164: * is a valid current Velocimacro.
1165: *
1166: * @param vmName Name of velocimacro requested
1167: * @param templateName Name of the template that contains the velocimacro.
1168: * @return The requested VelocimacroProxy.
1169: */
1170: public Directive getVelocimacro(String vmName, String templateName) {
1171: return vmFactory.getVelocimacro(vmName, templateName);
1172: }
1173:
1174: /**
1175: * Adds a new Velocimacro. Usually called by Macro only while parsing.
1176: *
1177: * @param name Name of velocimacro
1178: * @param macro String form of macro body
1179: * @param argArray Array of strings, containing the
1180: * #macro() arguments. the 0th is the name.
1181: * @param sourceTemplate Name of the template that contains the velocimacro.
1182: * @return True if added, false if rejected for some
1183: * reason (either parameters or permission settings)
1184: */
1185: public boolean addVelocimacro(String name, String macro,
1186: String argArray[], String sourceTemplate) {
1187: return vmFactory.addVelocimacro(name, macro, argArray,
1188: sourceTemplate);
1189: }
1190:
1191: /**
1192: * Checks to see if a VM exists
1193: *
1194: * @param vmName Name of the Velocimacro.
1195: * @param templateName Template on which to look for the Macro.
1196: * @return True if VM by that name exists, false if not
1197: */
1198: public boolean isVelocimacro(String vmName, String templateName) {
1199: return vmFactory.isVelocimacro(vmName, templateName);
1200: }
1201:
1202: /**
1203: * tells the vmFactory to dump the specified namespace. This is to support
1204: * clearing the VM list when in inline-VM-local-scope mode
1205: * @param namespace Namespace to dump.
1206: * @return True if namespace was dumped successfully.
1207: */
1208: public boolean dumpVMNamespace(String namespace) {
1209: return vmFactory.dumpVMNamespace(namespace);
1210: }
1211:
1212: /* --------------------------------------------------------------------
1213: * R U N T I M E A C C E S S O R M E T H O D S
1214: * --------------------------------------------------------------------
1215: * These are the getXXX() methods that are a simple wrapper
1216: * around the configuration object. This is an attempt
1217: * to make a the Velocity Runtime the single access point
1218: * for all things Velocity, and allow the Runtime to
1219: * adhere as closely as possible the the Mediator pattern
1220: * which is the ultimate goal.
1221: * --------------------------------------------------------------------
1222: */
1223:
1224: /**
1225: * String property accessor method to hide the configuration implementation
1226: * @param key property key
1227: * @return value of key or null
1228: */
1229: public String getString(String key) {
1230: return StringUtils.nullTrim(configuration.getString(key));
1231: }
1232:
1233: /**
1234: * Int property accessor method to hide the configuration implementation.
1235: *
1236: * @param key Property key
1237: * @return value
1238: */
1239: public int getInt(String key) {
1240: return configuration.getInt(key);
1241: }
1242:
1243: /**
1244: * Int property accessor method to hide the configuration implementation.
1245: *
1246: * @param key property key
1247: * @param defaultValue The default value.
1248: * @return value
1249: */
1250: public int getInt(String key, int defaultValue) {
1251: return configuration.getInt(key, defaultValue);
1252: }
1253:
1254: /**
1255: * Boolean property accessor method to hide the configuration implementation.
1256: *
1257: * @param key property key
1258: * @param def The default value if property not found.
1259: * @return value of key or default value
1260: */
1261: public boolean getBoolean(String key, boolean def) {
1262: return configuration.getBoolean(key, def);
1263: }
1264:
1265: /**
1266: * Return the velocity runtime configuration object.
1267: *
1268: * @return Configuration object which houses the Velocity runtime
1269: * properties.
1270: */
1271: public ExtendedProperties getConfiguration() {
1272: return configuration;
1273: }
1274:
1275: /**
1276: * Return the Introspector for this instance
1277: * @return The Introspector for this instance
1278: */
1279: public Introspector getIntrospector() {
1280: return introspector;
1281: }
1282:
1283: /**
1284: * Returns the event handlers for the application.
1285: * @return The event handlers for the application.
1286: */
1287: public EventCartridge getApplicationEventCartridge() {
1288: return eventCartridge;
1289: }
1290:
1291: /**
1292: * Gets the application attribute for the given key
1293: *
1294: * @param key
1295: * @return The application attribute for the given key.
1296: */
1297: public Object getApplicationAttribute(Object key) {
1298: return applicationAttributes.get(key);
1299: }
1300:
1301: /**
1302: * Sets the application attribute for the given key
1303: *
1304: * @param key
1305: * @param o The new application attribute.
1306: * @return The old value of this attribute or null if it hasn't been set before.
1307: */
1308: public Object setApplicationAttribute(Object key, Object o) {
1309: return applicationAttributes.put(key, o);
1310: }
1311:
1312: /**
1313: * Returns the Uberspect object for this Instance.
1314: *
1315: * @return The Uberspect object for this Instance.
1316: */
1317: public Uberspect getUberspect() {
1318: return uberSpect;
1319: }
1320:
1321: }
|