Java tutorial
package org.apache.flex.forks.velocity.runtime; /* * Copyright 2000-2001,2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Modified by Adobe Flex. */ import java.io.InputStream; import java.io.File; import java.io.IOException; import java.io.Reader; import java.util.Map; import java.util.Hashtable; import java.util.Properties; import java.util.Enumeration; import java.util.HashMap; import org.apache.flex.forks.velocity.Template; import org.apache.flex.forks.velocity.runtime.log.LogManager; import org.apache.flex.forks.velocity.runtime.log.LogSystem; import org.apache.flex.forks.velocity.runtime.log.PrimordialLogSystem; import org.apache.flex.forks.velocity.runtime.log.NullLogSystem; import org.apache.flex.forks.velocity.runtime.parser.Parser; import org.apache.flex.forks.velocity.runtime.parser.ParseException; import org.apache.flex.forks.velocity.runtime.parser.node.Node; import org.apache.flex.forks.velocity.runtime.parser.node.SimpleNode; import org.apache.flex.forks.velocity.runtime.directive.Directive; import org.apache.flex.forks.velocity.runtime.VelocimacroFactory; import org.apache.flex.forks.velocity.runtime.resource.ContentResource; import org.apache.flex.forks.velocity.runtime.resource.ResourceManager; import org.apache.flex.forks.velocity.util.SimplePool; import org.apache.flex.forks.velocity.util.StringUtils; import org.apache.flex.forks.velocity.util.introspection.Introspector; import org.apache.flex.forks.velocity.util.introspection.Uberspect; import org.apache.flex.forks.velocity.util.introspection.UberspectLoggable; import org.apache.flex.forks.velocity.exception.ResourceNotFoundException; import org.apache.flex.forks.velocity.exception.ParseErrorException; import org.apache.commons.collections.ExtendedProperties; //import sun.management.VMManagement; /** * This is the Runtime system for Velocity. It is the * single access point for all functionality in Velocity. * It adheres to the mediator pattern and is the only * structure that developers need to be familiar with * in order to get Velocity to perform. * * The Runtime will also cooperate with external * systems like Turbine. Runtime properties can * set and then the Runtime is initialized. * * Turbine, for example, knows where the templates * are to be loaded from, and where the Velocity * log file should be placed. * * So in the case of Velocity cooperating with Turbine * the code might look something like the following: * * <blockquote><code><pre> * ri.setProperty(Runtime.FILE_RESOURCE_LOADER_PATH, templatePath); * ri.setProperty(Runtime.RUNTIME_LOG, pathToVelocityLog); * ri.init(); * </pre></code></blockquote> * * <pre> * ----------------------------------------------------------------------- * 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 * ----------------------------------------------------------------------- * init() * * If init() is called by itself the RuntimeInstance will initialize * with a set of default values. * ----------------------------------------------------------------------- * init(String/Properties) * * In this case the default velocity properties are layed down * first to provide a solid base, then any properties provided * in the given properties object will override the corresponding * default property. * ----------------------------------------------------------------------- * </pre> * * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a> * @author <a href="mailto:jlb@houseofdistraction.com">Jeff Bowden</a> * @author <a href="mailto:geirm@optonline.net">Geir Magusson Jr.</a> * @version $Id: RuntimeInstance.java,v 1.19.4.1 2004/03/03 23:22:55 geirm Exp $ */ public class RuntimeInstance implements RuntimeConstants, RuntimeServices { /** * VelocimacroFactory object to manage VMs */ private VelocimacroFactory vmFactory = null; /** * The Runtime logger. We start with an instance of * a 'primordial logger', which just collects log messages * then, when the log system is initialized, we dump * all messages out of the primordial one into the real one. */ private LogSystem logSystem = new PrimordialLogSystem(); /** * The Runtime parser pool */ private SimplePool parserPool; /** * Indicate whether the Runtime has been fully initialized. */ private boolean initialized; /** * These are the properties that are laid down over top * of the default properties when requested. */ private ExtendedProperties overridingProperties = null; /** * This is a hashtable of initialized directives. * The directives that populate this hashtable are * taken from the RUNTIME_DEFAULT_DIRECTIVES * property file. This hashtable is passed * to each parser that is created. */ private Hashtable runtimeDirectives; /** * Object that houses the configuration options for * the velocity runtime. The ExtendedProperties object allows * the convenient retrieval of a subset of properties. * For example all the properties for a resource loader * can be retrieved from the main ExtendedProperties object * using something like the following: * * ExtendedProperties loaderConfiguration = * configuration.subset(loaderID); * * And a configuration is a lot more convenient to deal * with then conventional properties objects, or Maps. */ private ExtendedProperties configuration = new ExtendedProperties(); private ResourceManager resourceManager = null; /* * Each runtime instance has it's own introspector * to ensure that each instance is completely separate. */ private Introspector introspector = null; /* * Opaque reference to something specificed by the * application for use in application supplied/specified * pluggable components */ private Map applicationAttributes = null; private Uberspect uberSpect; public RuntimeInstance() { /* * create a VM factory, resource manager * and introspector */ vmFactory = new VelocimacroFactory(this); /* * make a new introspector and initialize it */ introspector = new Introspector(this); /* * and a store for the application attributes */ applicationAttributes = new HashMap(); } /* * This is the primary initialization method in the Velocity * Runtime. The systems that are setup/initialized here are * as follows: * * <ul> * <li>Logging System</li> * <li>ResourceManager</li> * <li>Parser Pool</li> * <li>Global Cache</li> * <li>Static Content Include System</li> * <li>Velocimacro System</li> * </ul> */ public synchronized void init() throws Exception { if (initialized == false) { info("************************************************************** "); info("Starting Jakarta Velocity v1.4"); info("RuntimeInstance initializing."); initializeProperties(); initializeLogger(); initializeResourceManager(); initializeDirectives(); initializeParserPool(); initializeIntrospection(); /* * initialize the VM Factory. It will use the properties * accessable from Runtime, so keep this here at the end. */ vmFactory.initVelocimacro(); info("Velocity successfully started."); initialized = true; } } /** * Gets the classname for the Uberspect introspection package and * instantiates an instance. */ private void initializeIntrospection() throws Exception { String rm = getString(RuntimeConstants.UBERSPECT_CLASSNAME); if (rm != null && rm.length() > 0) { Object o = null; try { o = Class.forName(rm).newInstance(); } catch (ClassNotFoundException cnfe) { String err = "The specified class for Uberspect (" + rm + ") does not exist (or is not accessible to the current classlaoder."; error(err); throw new Exception(err); } if (!(o instanceof Uberspect)) { String err = "The specified class for Uberspect (" + rm + ") does not implement org.apache.flex.forks.velocity.util.introspector.Uberspect." + " Velocity not initialized correctly."; error(err); throw new Exception(err); } uberSpect = (Uberspect) o; if (uberSpect instanceof UberspectLoggable) { ((UberspectLoggable) uberSpect).setRuntimeLogger(this); } uberSpect.init(); } else { /* * someone screwed up. Lets not fool around... */ String err = "It appears that no class was specified as the" + " Uberspect. Please ensure that all configuration" + " information is correct."; error(err); throw new Exception(err); } } /** * Initializes the Velocity Runtime with properties file. * The properties file may be in the file system proper, * or the properties file may be in the classpath. */ private void setDefaultProperties() { InputStream inputStream = null; try { inputStream = getClass().getResourceAsStream('/' + DEFAULT_RUNTIME_PROPERTIES); configuration.load(inputStream); info("Default Properties File: " + new File(DEFAULT_RUNTIME_PROPERTIES).getPath()); } catch (IOException ioe) { //System.err.println(msg); String msg = "Cannot get Velocity Runtime default properties!"; error(msg); } finally { try { if (inputStream != null) { inputStream.close(); } } catch (IOException ioe) { String msg = "Cannot close Velocity Runtime default properties!"; debug(msg); } } } /** * Allows an external system to set a property in * the Velocity Runtime. * * @param String property key * @param String property value */ public void setProperty(String key, Object value) { if (overridingProperties == null) { overridingProperties = new ExtendedProperties(); } overridingProperties.setProperty(key, value); } /** * Allow an external system to set an ExtendedProperties * object to use. This is useful where the external * system also uses the ExtendedProperties class and * the velocity configuration is a subset of * parent application's configuration. This is * the case with Turbine. * * @param ExtendedProperties configuration */ public void setConfiguration(ExtendedProperties configuration) { if (overridingProperties == null) { overridingProperties = configuration; } else { // Avoid possible ConcurrentModificationException if (overridingProperties != configuration) { overridingProperties.combine(configuration); } } } /** * Add a property to the configuration. If it already * exists then the value stated here will be added * to the configuration entry. For example, if * * resource.loader = file * * is already present in the configuration and you * * addProperty("resource.loader", "classpath") * * Then you will end up with a Vector like the * following: * * ["file", "classpath"] * * @param String key * @param String value */ public void addProperty(String key, Object value) { if (overridingProperties == null) { overridingProperties = new ExtendedProperties(); } overridingProperties.addProperty(key, value); } /** * Clear the values pertaining to a particular * property. * * @param String key of property to clear */ public void clearProperty(String key) { if (overridingProperties != null) { overridingProperties.clearProperty(key); } } /** * Allows an external caller to get a property. The calling * routine is required to know the type, as this routine * will return an Object, as that is what properties can be. * * @param key property to return */ public Object getProperty(String key) { return configuration.getProperty(key); } /** * Initialize Velocity properties, if the default * properties have not been laid down first then * do so. Then proceed to process any overriding * properties. Laying down the default properties * gives a much greater chance of having a * working system. */ private void initializeProperties() { /* * Always lay down the default properties first as * to provide a solid base. */ if (configuration.isInitialized() == false) { setDefaultProperties(); } if (overridingProperties != null) { configuration.combine(overridingProperties); } } /** * Initialize the Velocity Runtime with a Properties * object. * * @param Properties */ public void init(Properties p) throws Exception { overridingProperties = ExtendedProperties.convertProperties(p); init(); } /** * Initialize the Velocity Runtime with the name of * ExtendedProperties object. * * @param Properties */ public void init(String configurationFile) throws Exception { overridingProperties = new ExtendedProperties(configurationFile); init(); } private void initializeResourceManager() throws Exception { /* * Which resource manager? */ String rm = getString(RuntimeConstants.RESOURCE_MANAGER_CLASS); if (rm != null && rm.length() > 0) { /* * if something was specified, then make one. * if that isn't a ResourceManager, consider * this a huge error and throw */ Object o = null; try { o = Class.forName(rm).newInstance(); } catch (ClassNotFoundException cnfe) { String err = "The specified class for Resourcemanager (" + rm + ") does not exist (or is not accessible to the current classlaoder."; error(err); throw new Exception(err); } if (!(o instanceof ResourceManager)) { String err = "The specified class for ResourceManager (" + rm + ") does not implement org.apache.runtime.resource.ResourceManager." + " Velocity not initialized correctly."; error(err); throw new Exception(err); } resourceManager = (ResourceManager) o; resourceManager.initialize(this); } else { /* * someone screwed up. Lets not fool around... */ String err = "It appears that no class was specified as the" + " ResourceManager. Please ensure that all configuration" + " information is correct."; error(err); throw new Exception(err); } } /** * Initialize the Velocity logging system. * * @throws Exception */ private void initializeLogger() throws Exception { /* * Initialize the logger. We will eventually move all * logging into the logging manager. */ if (logSystem instanceof PrimordialLogSystem) { PrimordialLogSystem pls = (PrimordialLogSystem) logSystem; logSystem = LogManager.createLogSystem(this); /* * in the event of failure, lets do something to let it * limp along. */ if (logSystem == null) { logSystem = new NullLogSystem(); } else { pls.dumpLogMessages(logSystem); } } } /** * This methods initializes all the directives * that are used by the Velocity Runtime. The * directives to be initialized are listed in * the RUNTIME_DEFAULT_DIRECTIVES properties * file. * * @throws Exception */ private void initializeDirectives() throws Exception { /* * Initialize the runtime directive table. * This will be used for creating parsers. */ runtimeDirectives = new Hashtable(); Properties directiveProperties = new Properties(); /* * Grab the properties file with the list of directives * that we should initialize. */ InputStream inputStream = null; try { inputStream = getClass().getResourceAsStream('/' + DEFAULT_RUNTIME_DIRECTIVES); if (inputStream == null) throw new Exception( "Error loading directive.properties! " + "Something is very wrong if these properties " + "aren't being located. Either your Velocity " + "distribution is incomplete or your Velocity " + "jar file is corrupted!"); directiveProperties.load(inputStream); } catch (IOException ioe) { String msg = "Error while loading directive properties!"; error(msg); throw new Exception(msg, ioe); } finally { try { if (inputStream != null) { inputStream.close(); } } catch (IOException ioe) { String msg = "Cannot close directive properties!"; debug(msg); } } /* * Grab all the values of the properties. These * are all class names for example: * * org.apache.flex.forks.velocity.runtime.directive.Foreach */ Enumeration directiveClasses = directiveProperties.elements(); while (directiveClasses.hasMoreElements()) { String directiveClass = (String) directiveClasses.nextElement(); loadDirective(directiveClass, "System"); } /* * now the user's directives */ String[] userdirective = configuration.getStringArray("userdirective"); for (int i = 0; i < userdirective.length; i++) { loadDirective(userdirective[i], "User"); } } public Hashtable getDirectives() { return runtimeDirectives; } /** * instantiates and loads the directive with some basic checks * * @param directiveClass classname of directive to load */ private void loadDirective(String directiveClass, String caption) { try { Object o = Class.forName(directiveClass).newInstance(); if (o instanceof Directive) { Directive directive = (Directive) o; runtimeDirectives.put(directive.getName(), directive); info("Loaded " + caption + " Directive: " + directiveClass); } else { error(caption + " Directive " + directiveClass + " is not org.apache.flex.forks.velocity.runtime.directive.Directive." + " Ignoring. "); } } catch (Exception e) { error("Exception Loading " + caption + " Directive: " + directiveClass + " : " + e); } } /** * Initializes the Velocity parser pool. * This still needs to be implemented. */ private void initializeParserPool() { int numParsers = getInt(PARSER_POOL_SIZE, NUMBER_OF_PARSERS); parserPool = new SimplePool(numParsers); for (int i = 0; i < numParsers; i++) { parserPool.put(createNewParser()); } info("Created: " + numParsers + " parsers."); } /** * Returns a JavaCC generated Parser. * * @return Parser javacc generated parser */ public Parser createNewParser() { Parser parser = new Parser(this); parser.setDirectives(runtimeDirectives); return parser; } /** * Parse the input and return the root of * AST node structure. * <br><br> * In the event that it runs out of parsers in the * pool, it will create and let them be GC'd * dynamically, logging that it has to do that. This * is considered an exceptional condition. It is * expected that the user will set the * PARSER_POOL_SIZE property appropriately for their * application. We will revisit this. * * @param InputStream inputstream retrieved by a resource loader * @param String name of the template being parsed */ public SimpleNode parse(Reader reader, String templateName) throws ParseException { /* * do it and dump the VM namespace for this template */ return parse(reader, templateName, true); } /** * Parse the input and return the root of the AST node structure. * * @param InputStream inputstream retrieved by a resource loader * @param String name of the template being parsed * @param dumpNamespace flag to dump the Velocimacro namespace for this template */ public SimpleNode parse(Reader reader, String templateName, boolean dumpNamespace) throws ParseException { SimpleNode ast = null; Parser parser = (Parser) parserPool.get(); boolean madeNew = false; if (parser == null) { /* * if we couldn't get a parser from the pool * make one and log it. */ error("Runtime : ran out of parsers. Creating new. " + " Please increment the parser.pool.size property." + " The current value is too small."); parser = createNewParser(); if (parser != null) { madeNew = true; } } /* * now, if we have a parser */ if (parser != null) { try { /* * dump namespace if we are told to. Generally, you want to * do this - you don't in special circumstances, such as * when a VM is getting init()-ed & parsed */ if (dumpNamespace) { dumpVMNamespace(templateName); } ast = parser.parse(reader, templateName); } finally { /* * if this came from the pool, then put back */ if (!madeNew) { parserPool.put(parser); } } } else { error("Runtime : ran out of parsers and unable to create more."); } return ast; } /** * Returns a <code>Template</code> from the resource manager. * This method assumes that the character encoding of the * template is set by the <code>input.encoding</code> * property. The default is "ISO-8859-1" * * @param name The file name of the desired template. * @return The template. * @throws ResourceNotFoundException if template not found * from any available source. * @throws ParseErrorException if template cannot be parsed due * to syntax (or other) error. * @throws Exception if an error occurs in template initialization */ public Template getTemplate(String name) throws ResourceNotFoundException, ParseErrorException, Exception { return getTemplate(name, getString(INPUT_ENCODING, ENCODING_DEFAULT)); } /** * Returns a <code>Template</code> from the resource manager * * @param name The name of the desired template. * @param encoding Character encoding of the template * @return The template. * @throws ResourceNotFoundException if template not found * from any available source. * @throws ParseErrorException if template cannot be parsed due * to syntax (or other) error. * @throws Exception if an error occurs in template initialization */ public Template getTemplate(String name, String encoding) throws ResourceNotFoundException, ParseErrorException, Exception { return (Template) resourceManager.getResource(name, ResourceManager.RESOURCE_TEMPLATE, encoding); } /** * Returns a static content resource from the * resource manager. Uses the current value * if INPUT_ENCODING as the character encoding. * * @param name Name of content resource to get * @return parsed ContentResource object ready for use * @throws ResourceNotFoundException if template not found * from any available source. */ public ContentResource getContent(String name) throws ResourceNotFoundException, ParseErrorException, Exception { /* * the encoding is irrelvant as we don't do any converstion * the bytestream should be dumped to the output stream */ return getContent(name, getString(INPUT_ENCODING, ENCODING_DEFAULT)); } /** * Returns a static content resource from the * resource manager. * * @param name Name of content resource to get * @param encoding Character encoding to use * @return parsed ContentResource object ready for use * @throws ResourceNotFoundException if template not found * from any available source. */ public ContentResource getContent(String name, String encoding) throws ResourceNotFoundException, ParseErrorException, Exception { return (ContentResource) resourceManager.getResource(name, ResourceManager.RESOURCE_CONTENT, encoding); } /** * Determines is a template exists, and returns name of the loader that * provides it. This is a slightly less hokey way to support * the Velocity.templateExists() utility method, which was broken * when per-template encoding was introduced. We can revisit this. * * @param resourceName Name of template or content resource * @return class name of loader than can provide it */ public String getLoaderNameForResource(String resourceName) { return resourceManager.getLoaderNameForResource(resourceName); } /** * Added this to check and make sure that the configuration * is initialized before trying to get properties from it. * This occurs when there are errors during initialization * and the default properties have yet to be layed down. */ private boolean showStackTrace() { if (configuration.isInitialized()) { return getBoolean(RUNTIME_LOG_WARN_STACKTRACE, false); } else { return false; } } /** * Handle logging. * * @param String message to log */ private void log(int level, Object message) { String out; /* * now, see if the logging stacktrace is on * and modify the message to suit */ if (showStackTrace() && (message instanceof Throwable || message instanceof Exception)) { out = StringUtils.stackTrace((Throwable) message); } else { out = message.toString(); } /* * just log it, as we are guaranteed now to have some * kind of logger - save the if() */ logSystem.logVelocityMessage(level, out); } /** * Log a warning message. * * @param Object message to log */ public void warn(Object message) { log(LogSystem.WARN_ID, message); } /** * Log an info message. * * @param Object message to log */ public void info(Object message) { log(LogSystem.INFO_ID, message); } /** * Log an error message. * * @param Object message to log */ public void error(Object message) { log(LogSystem.ERROR_ID, message); } /** * Log a debug message. * * @param Object message to log */ public void debug(Object message) { log(LogSystem.DEBUG_ID, message); } /** * String property accessor method with default to hide the * configuration implementation. * * @param String key property key * @param String defaultValue default value to return if key not * found in resource manager. * @return String value of key or default */ public String getString(String key, String defaultValue) { return configuration.getString(key, defaultValue); } /** * Returns the appropriate VelocimacroProxy object if strVMname * is a valid current Velocimacro. * * @param String vmName Name of velocimacro requested * @return String VelocimacroProxy */ public Directive getVelocimacro(String vmName, String templateName) { return vmFactory.getVelocimacro(vmName, templateName); } /** * Adds a new Velocimacro. Usually called by Macro only while parsing. * * @param String name Name of velocimacro * @param String macro String form of macro body * @param String argArray Array of strings, containing the * #macro() arguments. the 0th is the name. * @return boolean True if added, false if rejected for some * reason (either parameters or permission settings) */ public boolean addVelocimacro(String name, Node macro, String argArray[], String sourceTemplate) { return vmFactory.addVelocimacro(name, macro, argArray, sourceTemplate); } /** * Checks to see if a VM exists * * @param name Name of velocimacro * @return boolean True if VM by that name exists, false if not */ public boolean isVelocimacro(String vmName, String templateName) { return vmFactory.isVelocimacro(vmName, templateName); } /** * tells the vmFactory to dump the specified namespace. This is to support * clearing the VM list when in inline-VM-local-scope mode */ public boolean dumpVMNamespace(String namespace) { return vmFactory.dumpVMNamespace(namespace); } /* -------------------------------------------------------------------- * R U N T I M E A C C E S S O R M E T H O D S * -------------------------------------------------------------------- * These are the getXXX() methods that are a simple wrapper * around the configuration object. This is an attempt * to make a the Velocity Runtime the single access point * for all things Velocity, and allow the Runtime to * adhere as closely as possible the the Mediator pattern * which is the ultimate goal. * -------------------------------------------------------------------- */ /** * String property accessor method to hide the configuration implementation * @param key property key * @return value of key or null */ public String getString(String key) { return configuration.getString(key); } /** * Int property accessor method to hide the configuration implementation. * * @param String key property key * @return int value */ public int getInt(String key) { return configuration.getInt(key); } /** * Int property accessor method to hide the configuration implementation. * * @param key property key * @param int default value * @return int value */ public int getInt(String key, int defaultValue) { return configuration.getInt(key, defaultValue); } /** * Boolean property accessor method to hide the configuration implementation. * * @param String key property key * @param boolean default default value if property not found * @return boolean value of key or default value */ public boolean getBoolean(String key, boolean def) { return configuration.getBoolean(key, def); } /** * Return the velocity runtime configuration object. * * @return ExtendedProperties configuration object which houses * the velocity runtime properties. */ public ExtendedProperties getConfiguration() { return configuration; } /** * Return the Introspector for this instance */ public Introspector getIntrospector() { return introspector; } public Object getApplicationAttribute(Object key) { return applicationAttributes.get(key); } public Object setApplicationAttribute(Object key, Object o) { return applicationAttributes.put(key, o); } public Uberspect getUberspect() { return uberSpect; } public void setNamespaceUsage(boolean on) { vmFactory.setNamespaceUsage(on); } }