| /* |
| * 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. |
| */ |
| |
| package org.apache.commons.configuration; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.net.URL; |
| import java.util.Collection; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.Map; |
| |
| import org.apache.commons.configuration.plist.PropertyListConfiguration; |
| import org.apache.commons.configuration.plist.XMLPropertyListConfiguration; |
| import org.apache.commons.digester.AbstractObjectCreationFactory; |
| import org.apache.commons.digester.CallMethodRule; |
| import org.apache.commons.digester.Digester; |
| import org.apache.commons.digester.ObjectCreationFactory; |
| import org.apache.commons.digester.Substitutor; |
| import org.apache.commons.digester.substitution.MultiVariableExpander; |
| import org.apache.commons.digester.substitution.VariableSubstitutor; |
| import org.apache.commons.digester.xmlrules.DigesterLoader; |
| import org.apache.commons.lang.StringUtils; |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.xml.sax.Attributes; |
| import org.xml.sax.SAXException; |
| |
| /** |
| * <p> |
| * Factory class to create a CompositeConfiguration from a .xml file using |
| * Digester. By default it can handle the Configurations from commons- |
| * configuration. If you need to add your own, then you can pass in your own |
| * digester rules to use. It is also namespace aware, by providing a |
| * digesterRuleNamespaceURI. |
| * </p> |
| * <p> |
| * <em>Note:</em> Almost all of the features provided by this class and many |
| * more are also available for the <code>{@link DefaultConfigurationBuilder}</code> |
| * class. <code>DefaultConfigurationBuilder</code> also has a more robust |
| * merge algorithm for constructing combined configurations. So it is |
| * recommended to use this class instead of <code>ConfigurationFactory</code>. |
| * </p> |
| * |
| * @author <a href="mailto:epugh@upstate.com">Eric Pugh</a> |
| * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a> |
| * @author <a href="mailto:oliver.heger@t-online.de">Oliver Heger</a> |
| * @version $Id$ |
| */ |
| public class ConfigurationFactory |
| { |
| /** Constant for the root element in the info file.*/ |
| private static final String SEC_ROOT = "configuration/"; |
| |
| /** Constant for the override section.*/ |
| private static final String SEC_OVERRIDE = SEC_ROOT + "override/"; |
| |
| /** Constant for the additional section.*/ |
| private static final String SEC_ADDITIONAL = SEC_ROOT + "additional/"; |
| |
| /** Constant for the optional attribute.*/ |
| private static final String ATTR_OPTIONAL = "optional"; |
| |
| /** Constant for the fileName attribute.*/ |
| private static final String ATTR_FILENAME = "fileName"; |
| |
| /** Constant for the load method.*/ |
| private static final String METH_LOAD = "load"; |
| |
| /** Constant for the default base path (points to actual directory).*/ |
| private static final String DEF_BASE_PATH = "."; |
| |
| /** static logger */ |
| private static Log log = LogFactory.getLog(ConfigurationFactory.class); |
| |
| /** The XML file with the details about the configuration to load */ |
| private String configurationFileName; |
| |
| /** The URL to the XML file with the details about the configuration to load. */ |
| private URL configurationURL; |
| |
| /** |
| * The implicit base path for included files. This path is determined by |
| * the configuration to load and used unless no other base path was |
| * explicitely specified. |
| */ |
| private String implicitBasePath; |
| |
| /** The basePath to prefix file paths for file based property files. */ |
| private String basePath; |
| |
| /** URL for xml digester rules file */ |
| private URL digesterRules; |
| |
| /** The digester namespace to parse */ |
| private String digesterRuleNamespaceURI; |
| |
| /** |
| * Constructor |
| */ |
| public ConfigurationFactory() |
| { |
| setBasePath(DEF_BASE_PATH); |
| } |
| /** |
| * Constructor with ConfigurationFile Name passed |
| * |
| * @param configurationFileName The path to the configuration file |
| */ |
| public ConfigurationFactory(String configurationFileName) |
| { |
| setConfigurationFileName(configurationFileName); |
| } |
| |
| /** |
| * Return the configuration provided by this factory. It loads the |
| * configuration file which is a XML description of the actual |
| * configurations to load. It can contain various different types of |
| * configuration, e.g. Properties, XML and JNDI. |
| * |
| * @return A Configuration object |
| * @throws ConfigurationException A generic exception that we had trouble during the |
| * loading of the configuration data. |
| */ |
| public Configuration getConfiguration() throws ConfigurationException |
| { |
| Digester digester; |
| InputStream input = null; |
| ConfigurationBuilder builder = new ConfigurationBuilder(); |
| URL url = getConfigurationURL(); |
| try |
| { |
| if (url == null) |
| { |
| url = ConfigurationUtils.locate(implicitBasePath, getConfigurationFileName()); |
| } |
| input = url.openStream(); |
| } |
| catch (Exception e) |
| { |
| log.error("Exception caught opening stream to URL", e); |
| throw new ConfigurationException("Exception caught opening stream to URL", e); |
| } |
| |
| if (getDigesterRules() == null) |
| { |
| digester = new Digester(); |
| configureNamespace(digester); |
| initDefaultDigesterRules(digester); |
| } |
| else |
| { |
| digester = DigesterLoader.createDigester(getDigesterRules()); |
| // This might already be too late. As far as I can see, the namespace |
| // awareness must be configured before the digester rules are loaded. |
| configureNamespace(digester); |
| } |
| |
| // Configure digester to always enable the context class loader |
| digester.setUseContextClassLoader(true); |
| // Add a substitutor to resolve system properties |
| enableDigesterSubstitutor(digester); |
| // Put the composite builder object below all of the other objects. |
| digester.push(builder); |
| // Parse the input stream to configure our mappings |
| try |
| { |
| digester.parse(input); |
| input.close(); |
| } |
| catch (SAXException saxe) |
| { |
| log.error("SAX Exception caught", saxe); |
| throw new ConfigurationException("SAX Exception caught", saxe); |
| } |
| catch (IOException ioe) |
| { |
| log.error("IO Exception caught", ioe); |
| throw new ConfigurationException("IO Exception caught", ioe); |
| } |
| return builder.getConfiguration(); |
| } |
| |
| /** |
| * Returns the configurationFile. |
| * |
| * @return The name of the configuration file. Can be null. |
| */ |
| public String getConfigurationFileName() |
| { |
| return configurationFileName; |
| } |
| |
| /** |
| * Sets the configurationFile. |
| * |
| * @param configurationFileName The name of the configurationFile to use. |
| */ |
| public void setConfigurationFileName(String configurationFileName) |
| { |
| File file = new File(configurationFileName).getAbsoluteFile(); |
| this.configurationFileName = file.getName(); |
| implicitBasePath = file.getParent(); |
| } |
| |
| /** |
| * Returns the URL of the configuration file to be loaded. |
| * |
| * @return the URL of the configuration to load |
| */ |
| public URL getConfigurationURL() |
| { |
| return configurationURL; |
| } |
| |
| /** |
| * Sets the URL of the configuration to load. This configuration can be |
| * either specified by a file name or by a URL. |
| * |
| * @param url the URL of the configuration to load |
| */ |
| public void setConfigurationURL(URL url) |
| { |
| configurationURL = url; |
| implicitBasePath = url.toString(); |
| } |
| |
| /** |
| * Returns the digesterRules. |
| * |
| * @return URL |
| */ |
| public URL getDigesterRules() |
| { |
| return digesterRules; |
| } |
| |
| /** |
| * Sets the digesterRules. |
| * |
| * @param digesterRules The digesterRules to set |
| */ |
| public void setDigesterRules(URL digesterRules) |
| { |
| this.digesterRules = digesterRules; |
| } |
| |
| /** |
| * Adds a substitutor to interpolate system properties |
| * |
| * @param digester The digester to which we add the substitutor |
| */ |
| protected void enableDigesterSubstitutor(Digester digester) |
| { |
| Map systemProperties = System.getProperties(); |
| MultiVariableExpander expander = new MultiVariableExpander(); |
| expander.addSource("$", systemProperties); |
| |
| // allow expansion in both xml attributes and element text |
| Substitutor substitutor = new VariableSubstitutor(expander); |
| digester.setSubstitutor(substitutor); |
| } |
| |
| /** |
| * Initializes the parsing rules for the default digester |
| * |
| * This allows the Configuration Factory to understand the default types: |
| * Properties, XML and JNDI. Two special sections are introduced: |
| * <code><override></code> and <code><additional></code>. |
| * |
| * @param digester The digester to configure |
| */ |
| protected void initDefaultDigesterRules(Digester digester) |
| { |
| initDigesterSectionRules(digester, SEC_ROOT, false); |
| initDigesterSectionRules(digester, SEC_OVERRIDE, false); |
| initDigesterSectionRules(digester, SEC_ADDITIONAL, true); |
| } |
| |
| /** |
| * Sets up digester rules for a specified section of the configuration |
| * info file. |
| * |
| * @param digester the current digester instance |
| * @param matchString specifies the section |
| * @param additional a flag if rules for the additional section are to be |
| * added |
| */ |
| protected void initDigesterSectionRules(Digester digester, String matchString, boolean additional) |
| { |
| setupDigesterInstance( |
| digester, |
| matchString + "properties", |
| new PropertiesConfigurationFactory(), |
| METH_LOAD, |
| additional); |
| |
| setupDigesterInstance( |
| digester, |
| matchString + "plist", |
| new PropertyListConfigurationFactory(), |
| METH_LOAD, |
| additional); |
| |
| setupDigesterInstance( |
| digester, |
| matchString + "xml", |
| new FileConfigurationFactory(XMLConfiguration.class), |
| METH_LOAD, |
| additional); |
| |
| setupDigesterInstance( |
| digester, |
| matchString + "hierarchicalXml", |
| new FileConfigurationFactory(XMLConfiguration.class), |
| METH_LOAD, |
| additional); |
| |
| setupDigesterInstance( |
| digester, |
| matchString + "jndi", |
| new JNDIConfigurationFactory(), |
| null, |
| additional); |
| |
| setupDigesterInstance( |
| digester, |
| matchString + "system", |
| new SystemConfigurationFactory(), |
| null, |
| additional); |
| } |
| |
| /** |
| * Sets up digester rules for a configuration to be loaded. |
| * |
| * @param digester the current digester |
| * @param matchString the pattern to match with this rule |
| * @param factory an ObjectCreationFactory instance to use for creating new |
| * objects |
| * @param method the name of a method to be called or <b>null</b> for none |
| * @param additional a flag if rules for the additional section are to be |
| * added |
| */ |
| protected void setupDigesterInstance( |
| Digester digester, |
| String matchString, |
| ObjectCreationFactory factory, |
| String method, |
| boolean additional) |
| { |
| if (additional) |
| { |
| setupUnionRules(digester, matchString); |
| } |
| |
| digester.addFactoryCreate(matchString, factory); |
| digester.addSetProperties(matchString); |
| |
| if (method != null) |
| { |
| digester.addRule(matchString, new CallOptionalMethodRule(method)); |
| } |
| |
| digester.addSetNext(matchString, "addConfiguration", Configuration.class.getName()); |
| } |
| |
| /** |
| * Sets up rules for configurations in the additional section. |
| * |
| * @param digester the current digester |
| * @param matchString the pattern to match with this rule |
| */ |
| protected void setupUnionRules(Digester digester, String matchString) |
| { |
| digester.addObjectCreate(matchString, |
| AdditionalConfigurationData.class); |
| digester.addSetProperties(matchString); |
| digester.addSetNext(matchString, "addAdditionalConfig", |
| AdditionalConfigurationData.class.getName()); |
| } |
| |
| /** |
| * Returns the digesterRuleNamespaceURI. |
| * |
| * @return A String with the digesterRuleNamespaceURI. |
| */ |
| public String getDigesterRuleNamespaceURI() |
| { |
| return digesterRuleNamespaceURI; |
| } |
| |
| /** |
| * Sets the digesterRuleNamespaceURI. |
| * |
| * @param digesterRuleNamespaceURI The new digesterRuleNamespaceURI to use |
| */ |
| public void setDigesterRuleNamespaceURI(String digesterRuleNamespaceURI) |
| { |
| this.digesterRuleNamespaceURI = digesterRuleNamespaceURI; |
| } |
| |
| /** |
| * Configure the current digester to be namespace aware and to have |
| * a Configuration object to which all of the other configurations |
| * should be added |
| * |
| * @param digester The Digester to configure |
| */ |
| private void configureNamespace(Digester digester) |
| { |
| if (getDigesterRuleNamespaceURI() != null) |
| { |
| digester.setNamespaceAware(true); |
| digester.setRuleNamespaceURI(getDigesterRuleNamespaceURI()); |
| } |
| else |
| { |
| digester.setNamespaceAware(false); |
| } |
| digester.setValidating(false); |
| } |
| |
| /** |
| * Returns the Base path from which this Configuration Factory operates. |
| * This is never null. If you set the BasePath to null, then a base path |
| * according to the configuration to load is returned. |
| * |
| * @return The base Path of this configuration factory. |
| */ |
| public String getBasePath() |
| { |
| String path = StringUtils.isEmpty(basePath) |
| || DEF_BASE_PATH.equals(basePath) ? implicitBasePath : basePath; |
| return StringUtils.isEmpty(path) ? DEF_BASE_PATH : path; |
| } |
| |
| /** |
| * Sets the basePath for all file references from this Configuration Factory. |
| * Normally a base path need not to be set because it is determined by |
| * the location of the configuration file to load. All relative pathes in |
| * this file are resolved relative to this file. Setting a base path makes |
| * sense if such relative pathes should be otherwise resolved, e.g. if |
| * the configuration file is loaded from the class path and all sub |
| * configurations it refers to are stored in a special config directory. |
| * |
| * @param basePath The new basePath to set. |
| */ |
| public void setBasePath(String basePath) |
| { |
| this.basePath = basePath; |
| } |
| |
| /** |
| * A base class for digester factory classes. This base class maintains |
| * a default class for the objects to be created. |
| * There will be sub classes for specific configuration implementations. |
| */ |
| public class DigesterConfigurationFactory extends AbstractObjectCreationFactory |
| { |
| /** Actual class to use. */ |
| private Class clazz; |
| |
| /** |
| * Creates a new instance of <code>DigesterConfigurationFactory</code>. |
| * |
| * @param clazz the class which we should instantiate |
| */ |
| public DigesterConfigurationFactory(Class clazz) |
| { |
| this.clazz = clazz; |
| } |
| |
| /** |
| * Creates an instance of the specified class. |
| * |
| * @param attribs the attributes (ignored) |
| * @return the new object |
| * @throws Exception if object creation fails |
| */ |
| public Object createObject(Attributes attribs) throws Exception |
| { |
| return clazz.newInstance(); |
| } |
| } |
| |
| /** |
| * A tiny inner class that allows the Configuration Factory to |
| * let the digester construct FileConfiguration objects |
| * that already have the correct base Path set. |
| * |
| */ |
| public class FileConfigurationFactory extends DigesterConfigurationFactory |
| { |
| /** |
| * C'tor |
| * |
| * @param clazz The class which we should instantiate. |
| */ |
| public FileConfigurationFactory(Class clazz) |
| { |
| super(clazz); |
| } |
| |
| /** |
| * Gets called by the digester. |
| * |
| * @param attributes the actual attributes |
| * @return the new object |
| * @throws Exception Couldn't instantiate the requested object. |
| */ |
| public Object createObject(Attributes attributes) throws Exception |
| { |
| FileConfiguration conf = createConfiguration(attributes); |
| conf.setBasePath(getBasePath()); |
| return conf; |
| } |
| |
| /** |
| * Creates the object, a <code>FileConfiguration</code>. |
| * |
| * @param attributes the actual attributes |
| * @return the file configuration |
| * @throws Exception if the object could not be created |
| */ |
| protected FileConfiguration createConfiguration(Attributes attributes) throws Exception |
| { |
| return (FileConfiguration) super.createObject(attributes); |
| } |
| } |
| |
| /** |
| * A factory that returns an XMLPropertiesConfiguration for .xml files |
| * and a PropertiesConfiguration for the others. |
| * |
| * @since 1.2 |
| */ |
| public class PropertiesConfigurationFactory extends FileConfigurationFactory |
| { |
| /** |
| * Creates a new instance of <code>PropertiesConfigurationFactory</code>. |
| */ |
| public PropertiesConfigurationFactory() |
| { |
| super(null); |
| } |
| |
| /** |
| * Creates the new configuration object. Based on the file name |
| * provided in the attributes either a <code>PropertiesConfiguration</code> |
| * or a <code>XMLPropertiesConfiguration</code> object will be |
| * returned. |
| * |
| * @param attributes the attributes |
| * @return the new configuration object |
| * @throws Exception if an error occurs |
| */ |
| protected FileConfiguration createConfiguration(Attributes attributes) throws Exception |
| { |
| String filename = attributes.getValue(ATTR_FILENAME); |
| |
| if (filename != null && filename.toLowerCase().trim().endsWith(".xml")) |
| { |
| return new XMLPropertiesConfiguration(); |
| } |
| else |
| { |
| return new PropertiesConfiguration(); |
| } |
| } |
| } |
| |
| /** |
| * A factory that returns an XMLPropertyListConfiguration for .xml files |
| * and a PropertyListConfiguration for the others. |
| * |
| * @since 1.2 |
| */ |
| public class PropertyListConfigurationFactory extends FileConfigurationFactory |
| { |
| /** |
| * Creates a new instance of <code>PropertyListConfigurationFactory</code>. |
| */ |
| public PropertyListConfigurationFactory() |
| { |
| super(null); |
| } |
| |
| /** |
| * Creates the new configuration object. Based on the file name |
| * provided in the attributes either a <code>XMLPropertyListConfiguration</code> |
| * or a <code>PropertyListConfiguration</code> object will be |
| * returned. |
| * |
| * @param attributes the attributes |
| * @return the new configuration object |
| * @throws Exception if an error occurs |
| */ |
| protected FileConfiguration createConfiguration(Attributes attributes) throws Exception |
| { |
| String filename = attributes.getValue(ATTR_FILENAME); |
| |
| if (filename != null && filename.toLowerCase().trim().endsWith(".xml")) |
| { |
| return new XMLPropertyListConfiguration(); |
| } |
| else |
| { |
| return new PropertyListConfiguration(); |
| } |
| } |
| } |
| |
| /** |
| * A tiny inner class that allows the Configuration Factory to |
| * let the digester construct JNDIConfiguration objects. |
| */ |
| private class JNDIConfigurationFactory extends DigesterConfigurationFactory |
| { |
| /** |
| * Creates a new instance of <code>JNDIConfigurationFactory</code>. |
| */ |
| public JNDIConfigurationFactory() |
| { |
| super(JNDIConfiguration.class); |
| } |
| } |
| |
| /** |
| * A tiny inner class that allows the Configuration Factory to |
| * let the digester construct SystemConfiguration objects. |
| */ |
| private class SystemConfigurationFactory extends DigesterConfigurationFactory |
| { |
| /** |
| * Creates a new instance of <code>SystemConfigurationFactory</code>. |
| */ |
| public SystemConfigurationFactory() |
| { |
| super(SystemConfiguration.class); |
| } |
| } |
| |
| /** |
| * A simple data class that holds all information about a configuration |
| * from the <code><additional></code> section. |
| */ |
| public static class AdditionalConfigurationData |
| { |
| /** Stores the configuration object.*/ |
| private Configuration configuration; |
| |
| /** Stores the location of this configuration in the global tree.*/ |
| private String at; |
| |
| /** |
| * Returns the value of the <code>at</code> attribute. |
| * |
| * @return the at attribute |
| */ |
| public String getAt() |
| { |
| return at; |
| } |
| |
| /** |
| * Sets the value of the <code>at</code> attribute. |
| * |
| * @param string the attribute value |
| */ |
| public void setAt(String string) |
| { |
| at = string; |
| } |
| |
| /** |
| * Returns the configuration object. |
| * |
| * @return the configuration |
| */ |
| public Configuration getConfiguration() |
| { |
| return configuration; |
| } |
| |
| /** |
| * Sets the configuration object. Note: Normally this method should be |
| * named <code>setConfiguration()</code>, but the name |
| * <code>addConfiguration()</code> is required by some of the digester |
| * rules. |
| * |
| * @param config the configuration to set |
| */ |
| public void addConfiguration(Configuration config) |
| { |
| configuration = config; |
| } |
| } |
| |
| /** |
| * An internally used helper class for constructing the composite |
| * configuration object. |
| */ |
| public static class ConfigurationBuilder |
| { |
| /** Stores the composite configuration.*/ |
| private CompositeConfiguration config; |
| |
| /** Stores a collection with the configs from the additional section.*/ |
| private Collection additionalConfigs; |
| |
| /** |
| * Creates a new instance of <code>ConfigurationBuilder</code>. |
| */ |
| public ConfigurationBuilder() |
| { |
| config = new CompositeConfiguration(); |
| additionalConfigs = new LinkedList(); |
| } |
| |
| /** |
| * Adds a new configuration to this object. This method is called by |
| * Digester. |
| * |
| * @param conf the configuration to be added |
| */ |
| public void addConfiguration(Configuration conf) |
| { |
| config.addConfiguration(conf); |
| } |
| |
| /** |
| * Adds information about an additional configuration. This method is |
| * called by Digester. |
| * |
| * @param data the data about the additional configuration |
| */ |
| public void addAdditionalConfig(AdditionalConfigurationData data) |
| { |
| additionalConfigs.add(data); |
| } |
| |
| /** |
| * Returns the final composite configuration. |
| * |
| * @return the final configuration object |
| */ |
| public CompositeConfiguration getConfiguration() |
| { |
| if (!additionalConfigs.isEmpty()) |
| { |
| Configuration unionConfig = createAdditionalConfiguration(additionalConfigs); |
| if (unionConfig != null) |
| { |
| addConfiguration(unionConfig); |
| } |
| additionalConfigs.clear(); |
| } |
| |
| return config; |
| } |
| |
| /** |
| * Creates a configuration object with the union of all properties |
| * defined in the <code><additional></code> section. This |
| * implementation returns a <code>HierarchicalConfiguration</code> |
| * object. |
| * |
| * @param configs a collection with |
| * <code>AdditionalConfigurationData</code> objects |
| * @return the union configuration (can be <b>null</b>) |
| */ |
| protected Configuration createAdditionalConfiguration(Collection configs) |
| { |
| HierarchicalConfiguration result = new HierarchicalConfiguration(); |
| |
| for (Iterator it = configs.iterator(); it.hasNext();) |
| { |
| AdditionalConfigurationData cdata = |
| (AdditionalConfigurationData) it.next(); |
| result.addNodes(cdata.getAt(), |
| createRootNode(cdata).getChildren()); |
| } |
| |
| return result.isEmpty() ? null : result; |
| } |
| |
| /** |
| * Creates a configuration root node for the specified configuration. |
| * |
| * @param cdata the configuration data object |
| * @return a root node for this configuration |
| */ |
| private HierarchicalConfiguration.Node createRootNode(AdditionalConfigurationData cdata) |
| { |
| if (cdata.getConfiguration() instanceof HierarchicalConfiguration) |
| { |
| // we can directly use this configuration's root node |
| return ((HierarchicalConfiguration) cdata.getConfiguration()).getRoot(); |
| } |
| else |
| { |
| // transform configuration to a hierarchical root node |
| HierarchicalConfiguration hc = new HierarchicalConfiguration(); |
| ConfigurationUtils.copy(cdata.getConfiguration(), hc); |
| return hc.getRoot(); |
| } |
| } |
| } |
| |
| /** |
| * A special implementation of Digester's <code>CallMethodRule</code> that |
| * is internally used for calling a file configuration's <code>load()</code> |
| * method. This class difers from its ancestor that it catches all occuring |
| * exceptions when the specified method is called. It then checks whether |
| * for the corresponding configuration the optional attribute is set. If |
| * this is the case, the exception will simply be ignored. |
| * |
| * @since 1.4 |
| */ |
| private static class CallOptionalMethodRule extends CallMethodRule |
| { |
| /** A flag whether the optional attribute is set for this node. */ |
| private boolean optional; |
| |
| /** |
| * Creates a new instance of <code>CallOptionalMethodRule</code> and |
| * sets the name of the method to invoke. |
| * |
| * @param methodName the name of the method |
| */ |
| public CallOptionalMethodRule(String methodName) |
| { |
| super(methodName); |
| } |
| |
| /** |
| * Checks if the optional attribute is set. |
| * |
| * @param attrs the attributes |
| * @throws Exception if an error occurs |
| */ |
| public void begin(Attributes attrs) throws Exception |
| { |
| optional = attrs.getValue(ATTR_OPTIONAL) != null |
| && PropertyConverter.toBoolean( |
| attrs.getValue(ATTR_OPTIONAL)).booleanValue(); |
| super.begin(attrs); |
| } |
| |
| /** |
| * Calls the method. If the optional attribute was set, occurring |
| * exceptions will be ignored. |
| * |
| * @throws Exception if an error occurs |
| */ |
| public void end() throws Exception |
| { |
| try |
| { |
| super.end(); |
| } |
| catch (Exception ex) |
| { |
| if (optional) |
| { |
| log.warn("Could not create optional configuration!", ex); |
| } |
| else |
| { |
| throw ex; |
| } |
| } |
| } |
| } |
| } |