blob: 90e8311f4295643e90b0fa9853cbda3afa08dca0 [file] [log] [blame]
package org.apache.velocity.tools.config;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.tools.ClassUtils;
import org.apache.velocity.tools.ToolboxFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Utility methods for handling tool configurations.
*
* @author Nathan Bubna
* @version $Id: ConfigurationUtils.java 511959 2007-02-26 19:24:39Z nbubna $
*/
public class ConfigurationUtils
{
public static final String GENERIC_DEFAULTS_PATH =
"/org/apache/velocity/tools/generic/tools.xml";
public static final String VIEW_DEFAULTS_PATH =
"/org/apache/velocity/tools/view/tools.xml";
public static final String AUTOLOADED_XML_PATH = "tools.xml";
public static final String AUTOLOADED_PROPS_PATH = "tools.properties";
public static final String SYSTEM_PROPERTY_KEY =
"org.apache.velocity.tools";
public static final ConfigurationUtils INSTANCE = new ConfigurationUtils();
private ConfigurationUtils() {}
public ConfigurationUtils getInstance()
{
return INSTANCE;
}
/**
* Returns the "default" {@link FactoryConfiguration}. This includes
* all the standard tools developed by this project and available in
* the jar being used. In other words, if the velocity-tools-generic-2.x.jar
* is being used, then only the generic tools will be included. If
* the velocity-tools-struts-2.x.jar is being used, then all VelocityTools
* will be available. This also means that subclasses in the larger jars
* will override their superclasses. So, if you are using the VelocityStruts
* jar, then your $link reference will be a StrutsLinkTool. If you are using
* the VelocityView jar, it will be a standard LinkTool.
* @return the default {@link FactoryConfiguration}
*/
public static FactoryConfiguration getDefaultTools()
{
FileFactoryConfiguration config =
new XmlFactoryConfiguration("ConfigurationUtils.getDefaultTools()");
config.read(GENERIC_DEFAULTS_PATH);
// view tools may not be available
config.read(VIEW_DEFAULTS_PATH, false);
// defaults should *always* be clean!
clean(config);
return config;
}
/**
* Returns a {@link FactoryConfiguration} including all default
* "GenericTools" available and no others.
* @return the generic tools default {@link FactoryConfiguration}
*/
public static FactoryConfiguration getGenericTools()
{
FileFactoryConfiguration config =
new XmlFactoryConfiguration("ConfigurationUtils.getGenericTools()");
config.read(GENERIC_DEFAULTS_PATH);
// defaults should *always* be clean!
clean(config);
return config;
}
/**
* <p>Returns a {@link FactoryConfiguration} including all default
* "velocity-tools-view" tools available as well as the default "GenericTools".</p>
* @return all default tools {@link FactoryConfiguration}
* @throws ConfigurationException if a tools.xml is not found at the {@link #VIEW_DEFAULTS_PATH}.
*/
public static FactoryConfiguration getVelocityView()
{
FileFactoryConfiguration config =
new XmlFactoryConfiguration("ConfigurationUtils.getVelocityView()");
config.read(GENERIC_DEFAULTS_PATH);
config.read(VIEW_DEFAULTS_PATH);
// defaults should *always* be clean!
clean(config);
return config;
}
/**
* Returns a {@link FactoryConfiguration} loaded from the path specified
* in the "org.apache.velocity.tools" system property (if any).
* If no such property has been set {@code null} will be returned.
* @return system property defined {@link FactoryConfiguration}
* @throws ResourceNotFoundException if the system property has a value
* but no configuration file was found at the specified location
*/
public static FactoryConfiguration findFromSystemProperty()
{
String path = System.getProperty(SYSTEM_PROPERTY_KEY);
if (path == null || path.length() == 0)
{
return null;
}
return load(path);
}
/**
* Returns a new, standard {@link ToolboxFactory} configured
* with the results of {@link #findFromSystemProperty()}.
* @return new {@link ToolboxFactory}
*/
public static ToolboxFactory createFactory()
{
// include any config specified via system property
FactoryConfiguration sys = findFromSystemProperty();
ToolboxFactory factory = new ToolboxFactory();
if (sys != null)
{
factory.configure(sys);
}
return factory;
}
/**
* Convenience method that automatically creates a new
* {@link ConfigurationCleaner} and applies it to the specified
* {@link Configuration}.
* @param config {@link Configuration}
*/
public static void clean(Configuration config)
{
// since most config will happen at startup and a cleaner
// is not otherwise necessary, don't keep one of these statically
ConfigurationCleaner cleaner = new ConfigurationCleaner();
cleaner.clean(config);
}
/**
* Returns a {@link FactoryConfiguration} loaded from a configuration file
* at the specified path. If no such file is found at that path, this
* will throw a {@link ResourceNotFoundException}.
*
* @param path configuration path
* @return new {@link FactoryConfiguration}
* @see #find(String path)
*/
public static FactoryConfiguration load(String path)
{
FactoryConfiguration config = find(path);
if (config == null)
{
throw new ResourceNotFoundException("Could not find configuration at "+path);
}
return config;
}
/**
* Searches for a configuration file at the specified path and returns
* it in the form of a {@link FactoryConfiguration}. This method will
* look for a matching file in both the classpath and the file system.
* If perchance a match is found in both, then both are loaded and the
* configuration loaded from the file system is given precedence (i.e.
* it is added onto the other). If no match is found in either, then
* this will return {@code null}.
* @param path configuration classpath or filesystem path
* @return new {@link FactoryConfiguration} for the given path
*/
public static FactoryConfiguration find(String path)
{
FactoryConfiguration cp = findInClasspath(path);
FactoryConfiguration fs = findInFileSystem(path);
if (cp != null)
{
if (fs != null)
{
cp.addConfiguration(fs);
}
return cp;
}
else
{
return fs;
}
}
/**
* Searches the file system for a configuration file matching the
* specified path. If found, it will read and return it as a
* {@link FactoryConfiguration}. If not found, this will return
* {@code null}.
* @param path filesystem path
* @return new {@link FactoryConfiguration} for the given path
* @throws IllegalStateException if the file exists, but its path could not be converted to a URL for reading.
*/
public static FactoryConfiguration findInFileSystem(String path)
{
File file = new File(path);
if (file.exists())
{
try
{
return read(file.toURI().toURL());
}
catch (MalformedURLException mue)
{
throw new IllegalStateException("Could not convert existing file path \""+path+"\" to URL", mue);
}
}
return null;
}
/**
* @param path configuration classpath
* @return new {@link FactoryConfiguration} for the given classpath
* @see #findInClasspath(String path, Object caller)
*/
public static FactoryConfiguration findInClasspath(String path)
{
// pretend this was called by a non-static ConfigurationUtils
return findInClasspath(path, new ConfigurationUtils());
}
/**
* Searches the classpath for a configuration file matching the
* specified path. If found, it will read and return it as a
* {@link FactoryConfiguration}. If not found, this will return
* {@code null}. If there are multiple matching resources in the
* classpath, then they will be added together in the order found
* (i.e. the last one will have precedence).
* @param path configuration classpath
* @param caller classloader context
* @return new {@link FactoryConfiguration} for the given classpath
* @see ClassUtils#getResources(String path, Object caller)
*/
public static FactoryConfiguration findInClasspath(String path, Object caller)
{
// find all resources at this path
List<URL> found = ClassUtils.getResources(path, caller);
if (found.isEmpty())
{
return null;
}
else if (found.size() == 1)
{
// load and return the one config at that URL (if any)
return read(found.get(0));
}
else
{
// create a base config to combine the others into
FactoryConfiguration config =
new FactoryConfiguration("ConfigurationUtils.findInClassPath("+path+","+caller+")");
boolean readAConfig = false;
for (URL resource : found)
{
FactoryConfiguration c = read(resource);
if (c != null)
{
readAConfig = true;
config.addConfiguration(c);
}
}
// only return a configuration if we really found one
if (readAConfig)
{
return config;
}
else
{
return null;
}
}
}
/**
* Returns a {@link FactoryConfiguration} read from a known configuration
* file type at the specified {@link URL}. If the file is missing or unreadable,
* this will simply return {@code null} (i.e. if an IOException is thrown).
* @param url configuration URL
* @return new {@link FactoryConfiguration} for the given URL
* @throws UnsupportedOperationException if the file type (identified via suffix)
* is neither ".xml" or ".properties"
*/
public static FactoryConfiguration read(URL url)
{
FileFactoryConfiguration config = null;
String path = url.toString();
String source = "ConfigurationUtils.read("+url.toString()+")";
if (path.endsWith(".xml"))
{
config = new XmlFactoryConfiguration(source);
}
else if (path.endsWith(".properties"))
{
config = new PropertiesFactoryConfiguration(source);
}
else if (path.endsWith(".class"))
{
// convert the url to a FQN
String fqn = path.substring(0, path.indexOf('.')).replace('/','.');
// retrieve a configuration from that class
return getFromClass(fqn);
}
else
{
String msg = "Unknown configuration file type: " + url.toString() +
"\nOnly .xml and .properties configuration files are supported at this time.";
throw new UnsupportedOperationException(msg);
}
// now, try to read the file
try
{
config.read(url);
}
catch (Exception e)
{
return null;
}
return config;
}
/**
* Get a specific {@link FactoryConfiguration} class
* @param classname FacotyConfiguration class
* @return new FactoryConfiguration instance
*/
public static FactoryConfiguration getFromClass(String classname)
{
try
{
Class configFactory = ClassUtils.getClass(classname);
return getFromClass(configFactory);
}
catch (ClassNotFoundException cnfe)
{
throw new IllegalArgumentException("Could not find class "+classname, cnfe);
}
}
public static final String CONFIG_FACTORY_METHOD = "getConfiguration";
public static FactoryConfiguration getFromClass(Class factory)
{
//TODO: look for a getConfiguration() method
Method getConf = null;
try
{
// check for a public setup(Map) method first
getConf = factory.getMethod(CONFIG_FACTORY_METHOD, (Class[])null);
}
catch (NoSuchMethodException nsme)
{
throw new IllegalArgumentException("Could not find "+CONFIG_FACTORY_METHOD+" in class "+factory.getName(), nsme);
}
// get an instance if the method is not static
Object instance = null;
if (!Modifier.isStatic(getConf.getModifiers()))
{
try
{
instance = factory.newInstance();
}
catch (Exception e)
{
throw new IllegalArgumentException(factory.getName()+" must have usable default constructor or else "+CONFIG_FACTORY_METHOD+" must be declared static", e);
}
}
// invoke the method
try
{
FactoryConfiguration result =
(FactoryConfiguration)getConf.invoke(instance, (Object[])null);
if (result == null)
{
throw new IllegalArgumentException("Method "+CONFIG_FACTORY_METHOD+" in class "+factory.getName()+" should not return null or void");
}
else
{
return result;
}
}
catch (IllegalAccessException iae)
{
throw new IllegalArgumentException("Failed to invoke "+CONFIG_FACTORY_METHOD+" in class "+factory.getName(), iae);
}
catch (IllegalArgumentException iae)
{
// if this happens, it's more a problem w/this code than the users
throw iae;
}
catch (InvocationTargetException ite)
{
// if this happens, it's all their issue
throw new IllegalArgumentException("There was an exception while executing "+CONFIG_FACTORY_METHOD+" in class "+factory.getName(), ite.getCause());
}
}
public static Logger getLog(VelocityEngine engine, String childNamespace)
{
/* first check config for a logger instance, then for a base logger name
this is mostly what RuntimeServices.getLog(String) does, but we don't
have access to RuntimeServices here
*/
Logger logger = (Logger)engine.getProperty(RuntimeConstants.RUNTIME_LOG_INSTANCE);
if (logger == null)
{
String basename = (String)engine.getProperty(RuntimeConstants.RUNTIME_LOG_NAME);
if (basename == null)
{
basename = RuntimeConstants.DEFAULT_RUNTIME_LOG_NAME;
}
logger = LoggerFactory.getLogger(basename + "." + childNamespace);
}
return logger;
}
}