blob: 28f652bf913a5f6b36234f283941ed83911f881a [file] [log] [blame]
/*
* 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.chain2.config.xml;
import org.apache.commons.chain2.CatalogFactory;
import org.apache.commons.chain2.config.ChainConfigurationException;
import org.apache.commons.chain2.config.ConfigParser;
import org.apache.commons.chain2.impl.CatalogFactoryBase;
import org.apache.commons.digester3.Digester;
import org.apache.commons.digester3.RuleSet;
import java.net.URL;
import java.util.Map;
/**
* <p>Class to parse the contents of an XML configuration file (using
* Commons Digester) that defines and configures commands and command chains
* to be registered in a {@link org.apache.commons.chain2.Catalog}. Advanced users can configure the
* detailed parsing behavior by configuring the properties of an instance
* of this class prior to calling the <code>parse()</code> method. It
* is legal to call the <code>parse()</code> method more than once, in order
* to parse more than one configuration document.</p>
*
* @version $Id: ConfigParser.java 1364104 2012-07-21 14:25:54Z elijah $
*/
public class XmlConfigParser implements ConfigParser {
// ----------------------------------------------------- Instance Variables
/**
* <p>The <code>RuleSet</code> to be used for configuring our Digester
* parsing rules.</p>
*/
private RuleSet ruleSet = new ConfigRuleSet();
/**
* <p>Should Digester use the context class loader?
*/
private boolean useContextClassLoader = true;
// ------------------------------------------------------------- Constructor
public XmlConfigParser() {
}
public XmlConfigParser(String ruleSet, ClassLoader loader) {
if (ruleSet == null) {
throw new IllegalArgumentException("ConfigParser can't be " +
"instantiated with a null ruleSet class name");
}
if (loader == null) {
throw new IllegalArgumentException("ConfigParser can't be " +
"instantiated with a null class loader reference");
}
try {
Class<?> clazz = loader.loadClass(ruleSet);
setRuleSet((RuleSet) clazz.newInstance());
} catch (Exception e) {
throw new RuntimeException("Exception initializing RuleSet '"
+ ruleSet + "' instance: "
+ e.getMessage());
}
}
// ------------------------------------------------------------- Properties
/**
* <p>Return the <code>Digester</code> instance to be used for
* parsing, creating one if necessary.</p>
* @return A Digester instance.
*/
public Digester getDigester() {
Digester digester = new Digester();
RuleSet ruleSet = getRuleSet();
digester.setNamespaceAware(ruleSet.getNamespaceURI() != null);
digester.setUseContextClassLoader(getUseContextClassLoader());
digester.setValidating(false);
digester.addRuleSet(ruleSet);
return digester;
}
/**
* <p>Return the <code>RuleSet</code> to be used for configuring
* our <code>Digester</code> parsing rules, creating one if necessary.</p>
* @return The RuleSet for configuring a Digester instance.
*/
public RuleSet getRuleSet() {
return ruleSet;
}
/**
* <p>Set the <code>RuleSet</code> to be used for configuring
* our <code>Digester</code> parsing rules.</p>
*
* @param ruleSet The new RuleSet to use
*/
public void setRuleSet(RuleSet ruleSet) {
this.ruleSet = ruleSet;
}
/**
* <p>Return the "use context class loader" flag. If set to
* <code>true</code>, Digester will attempt to instantiate new
* command and chain instances from the context class loader.</p>
* @return <code>true</code> if Digester should use the context class loader.
*/
public boolean getUseContextClassLoader() {
return this.useContextClassLoader;
}
/**
* <p>Set the "use context class loader" flag.</p>
*
* @param useContextClassLoader The new flag value
*/
public void setUseContextClassLoader(boolean useContextClassLoader) {
this.useContextClassLoader = useContextClassLoader;
}
// --------------------------------------------------------- Public Methods
/**
* <p>Parse the XML document at the specified URL using the configured
* <code>RuleSet</code>, registering catalogs with nested chains and
* commands as they are encountered. Use this method <strong>only</strong>
* if you have included one or more <code>factory</code> elements in your
* configuration resource.</p>
*
* @param <K> the type of keys maintained by the context associated with this command
* @param <V> the type of mapped values
* @param <C> Type of the context associated with this command
* @param url <code>URL</code> of the XML document to be parsed
* @return a CatalogFactory instance parsed from the given location
* @throws ChainConfigurationException if a parsing error occurs
*/
public <K, V, C extends Map<K, V>> CatalogFactory<K, V, C> parse(URL url) {
// Prepare our Digester instance
Digester digester = getDigester();
digester.clear();
// Parse the configuration document
try {
digester.parse(url);
} catch (Exception e) {
String msg = String.format(
"Error parsing digester configuration at url: %s",
url);
throw new ChainConfigurationException(msg, e);
}
// FIXME get rid of singleton pattern and create a new instance here
return CatalogFactoryBase.getInstance();
}
}