You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by cr...@apache.org on 2004/07/16 21:06:01 UTC
cvs commit: jakarta-commons/chain/src/test/org/apache/commons/chain/config ConfigParser2TestCase.java test-config-2.xml
craigmcc 2004/07/16 12:06:01
Modified: chain/src/java/org/apache/commons/chain/config
ConfigRuleSet.java
Added: chain/src/java/org/apache/commons/chain/config
ConfigDefineRule.java
chain/src/test/org/apache/commons/chain/config
ConfigParser2TestCase.java test-config-2.xml
Log:
Enhance the readability/writeability of configuration files for command
chains, by supporting a new <define> element that dynamically adds new
Digester rules to the running instance, allowing use of elements without
having to specify the command or chain implementation class every time.
For example, assume you wanted to use two different instances of a
particular command in two different chains:
<chains>
...
<chain ...>
...
<command className="com.mycompany.mycommands.FooCommand"/>
...
</chain>
<chain ...>
...
<command clasName="com.mycompany.mycommands.FooCommand"/>
...
</chain>
...
</chains>
Now, you can define an alias for this command and reuse it:
<chains>
...
<define name="foo" className="com.mycompany.mycommands.FooCommand"/>
...
<chain ...>
...
<foo>
...
</chain>
<chain ...>
...
<foo>
...
</chain>
...
</chains>
The dynamically generated rules include a Set Properties rule, so that you
can configure properties on aliased commands just like you can with the
<chain> or <command> elements directly.
See the unit test input file (test-config-2.xml) below for more examples.
Revision Changes Path
1.7 +29 -1 jakarta-commons/chain/src/java/org/apache/commons/chain/config/ConfigRuleSet.java
Index: ConfigRuleSet.java
===================================================================
RCS file: /home/cvs/jakarta-commons/chain/src/java/org/apache/commons/chain/config/ConfigRuleSet.java,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- ConfigRuleSet.java 9 Jul 2004 00:03:25 -0000 1.6
+++ ConfigRuleSet.java 16 Jul 2004 19:06:01 -0000 1.7
@@ -47,6 +47,10 @@
* representing the addition of a {@link Command}. An implementation
* class name must be provided on the attribute named by the
* <code>classAttribute</code> property. [command]</li>
+ * <li><strong>defineElement</strong> -- Name of the XML element
+ * that associates the element specified by the <code>nameAttribute</code>
+ * attributes with a {@link Command} or {@link Chain} implementation class
+ * named by the <code>classAttribute</code> attribute. [define]</li>
* <li><strong>nameAttribute</strong> -- Attribute on an outermost chain or
* command element that will be used to register this command with the
* associated {@link Catalog} instance on the stack. [name]</li>
@@ -69,6 +73,7 @@
private String chainElement = "chain";
private String classAttribute = "className";
private String commandElement = "command";
+ private String defineElement = "define";
private String nameAttribute = "name";
@@ -148,6 +153,24 @@
/**
+ * <p>Return the element name of a define element.</p>
+ */
+ public String getDefineElement() {
+ return (this.defineElement);
+ }
+
+
+ /**
+ * <p>Set the element name of a define element.</p>
+ *
+ * @param defineElement The new element name
+ */
+ public void setDefineElement(String defineElement) {
+ this.defineElement = defineElement;
+ }
+
+
+ /**
* <p>Return the attribute name of a name attribute.</p>
*/
public String getNameAttribute() {
@@ -194,6 +217,11 @@
digester.addSetProperties("*/" + getCommandElement());
digester.addRule("*/" + getCommandElement(),
new ConfigRegisterRule(nameAttribute));
+
+ // Add rules for a define element
+ digester.addRule("*/" + getDefineElement(),
+ new ConfigDefineRule(getNameAttribute(),
+ getClassAttribute()));
}
1.1 jakarta-commons/chain/src/java/org/apache/commons/chain/config/ConfigDefineRule.java
Index: ConfigDefineRule.java
===================================================================
/*
* Copyright 1999-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.
*/
package org.apache.commons.chain.config;
import org.apache.commons.chain.Catalog;
import org.apache.commons.chain.Chain;
import org.apache.commons.chain.Command;
import org.apache.commons.digester.Rule;
import org.xml.sax.Attributes;
/**
* <p>Digester rule that will dynamically register a new set of rules
* for a specified element name and default implementation class. This
* allows "alias" elements to be created for {@link Chain} and {@link Command}
* implementation classes that are commonly used. Besides factoring out
* the class names to make changes easier, this also makes configuration
* files <strong>much</strong> easier to read and write.</p>
*
* @version $Revision: 1.1 $ $Date: 2004/07/16 19:06:01 $
*/
class ConfigDefineRule extends Rule {
// ----------------------------------------------------------- Constructors
/**
* <p>Construct a new instance of this rule that will in turn
* dynamically register appropriate rules for a new alias element.</p>
*
* @param nameAttribute Name of the attribute containing the name
* of the new element for which rules should generated
* @param classAttribute Name of the attribute containing the
* implementation class for the new chain or command
*/
public ConfigDefineRule(String nameAttribute, String classAttribute) {
super();
this.nameAttribute = nameAttribute;
this.classAttribute = classAttribute;
}
// ----------------------------------------------------- Instance Variables
/**
* <p>The name of the attribute under which we can retrieve the
* fully qualified class name of the implementation class for this
* new element.</p>
*/
private String classAttribute = null;
/**
* <p>The name of the attribute under which we can retrieve the name
* this element for which rules should be created.</p>
*/
private String nameAttribute = null;
// --------------------------------------------------------- Public Methods
/**
* <p>Register new rules for the specified name and class.</p>
*
* @param namespace the namespace URI of the matching element, or an
* empty string if the parser is not namespace aware or the element has
* no namespace
* @param name the local name if the parser is namespace aware, or just
* the element name otherwise
* @param attributes The attribute list of this element
*/
public void begin(String namespace, String name, Attributes attributes)
throws Exception {
// Extract the actual name and implementation class to use
String nameValue = attributes.getValue(nameAttribute);
String classValue = attributes.getValue(classAttribute);
// Add rules for this new element
digester.addObjectCreate("*/" + nameValue, classValue);
digester.addSetProperties("*/" + nameValue);
digester.addRule("*/" + nameValue,
new ConfigRegisterRule(nameAttribute));
}
}
1.1 jakarta-commons/chain/src/test/org/apache/commons/chain/config/ConfigParser2TestCase.java
Index: ConfigParser2TestCase.java
===================================================================
/*
* Copyright 1999-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.
*/
package org.apache.commons.chain.config;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.apache.commons.chain.Catalog;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
import org.apache.commons.chain.impl.*;
import org.apache.commons.digester.Digester;
import java.util.Iterator;
/**
* <p>Test case identical to {@link ConfigParserTestCase} except
* that it uses the <code>define</code> rule to define aliases
* for the commands and chains used in the test.</p>
*/
public class ConfigParser2TestCase extends TestCase {
private static final String DEFAULT_XML =
"/org/apache/commons/chain/config/test-config-2.xml";
// ------------------------------------------------------------ Constructors
/**
* Construct a new instance of this test case.
*
* @param name Name of the test case
*/
public ConfigParser2TestCase(String name) {
super(name);
}
// ------------------------------------------------------ Instance Variables
/**
* <p>The <code>Catalog</code> to contain our configured commands.</p>
*/
protected Catalog catalog = null;
/**
* <p>The <code>Context</code> to use for execution tests.</p>
*/
protected Context context = null;
/**
* <p>The <code>ConfigParser</code> instance under test.</p>
*/
protected ConfigParser parser = null;
// ---------------------------------------------------- Overall Test Methods
/**
* Set up instance variables required by this test case.
*/
public void setUp() {
catalog = new CatalogBase();
context = new ContextBase();
parser = new ConfigParser();
}
/**
* Return the tests included in this test suite.
*/
public static Test suite() {
return (new TestSuite(ConfigParser2TestCase.class));
}
/**
* Tear down instance variables required by this test case.
*/
public void tearDown() {
parser = null;
context = null;
catalog = null;
}
// ------------------------------------------------ Individual Test Methods
// Load the default test-config.xml file and examine the results
public void testDefaut() throws Exception {
// Check overall command count
load(DEFAULT_XML);
checkCommandCount(17);
// Check individual single command instances
Command command = null;
command = catalog.getCommand("AddingCommand");
assertNotNull(command);
assertTrue(command instanceof AddingCommand);
command = catalog.getCommand("DelegatingCommand");
assertNotNull(command);
assertTrue(command instanceof DelegatingCommand);
command = catalog.getCommand("DelegatingFilter");
assertNotNull(command);
assertTrue(command instanceof DelegatingFilter);
command = catalog.getCommand("ExceptionCommand");
assertNotNull(command);
assertTrue(command instanceof ExceptionCommand);
command = catalog.getCommand("ExceptionFilter");
assertNotNull(command);
assertTrue(command instanceof ExceptionFilter);
command = catalog.getCommand("NonDelegatingCommand");
assertNotNull(command);
assertTrue(command instanceof NonDelegatingCommand);
command = catalog.getCommand("NonDelegatingFilter");
assertNotNull(command);
assertTrue(command instanceof NonDelegatingFilter);
command = catalog.getCommand("ChainBase");
assertNotNull(command);
assertTrue(command instanceof ChainBase);
assertTrue(command instanceof TestChain);
// Check configurable properties instance
TestCommand tcommand = (TestCommand) catalog.getCommand("Configurable");
assertNotNull(tcommand);
assertEquals("Foo Value", tcommand.getFoo());
assertEquals("Bar Value", tcommand.getBar());
}
// Test execution of chain "Execute2a"
public void testExecute2a() throws Exception {
load(DEFAULT_XML);
assertTrue("Chain returned true",
catalog.getCommand("Execute2a").execute(context));
checkExecuteLog("1/2/3");
}
// Test execution of chain "Execute2b"
public void testExecute2b() throws Exception {
load(DEFAULT_XML);
assertTrue("Chain returned false",
!catalog.getCommand("Execute2b").execute(context));
checkExecuteLog("1/2/3");
}
// Test execution of chain "Execute2c"
public void testExecute2c() throws Exception {
load(DEFAULT_XML);
try {
catalog.getCommand("Execute2c").execute(context);
} catch (ArithmeticException e) {
assertEquals("Correct exception id",
"3", e.getMessage());
}
checkExecuteLog("1/2/3");
}
// Test execution of chain "Execute2d"
public void testExecute2d() throws Exception {
load(DEFAULT_XML);
try {
catalog.getCommand("Execute2d").execute(context);
} catch (ArithmeticException e) {
assertEquals("Correct exception id",
"2", e.getMessage());
}
checkExecuteLog("1/2");
}
// Test execution of chain "Execute4a"
public void testExecute4a() throws Exception {
load(DEFAULT_XML);
assertTrue("Chain returned true",
catalog.getCommand("Execute4a").execute(context));
checkExecuteLog("1/2/3/c/a");
}
// Test execution of chain "Execute2b"
public void testExecute4b() throws Exception {
load(DEFAULT_XML);
assertTrue("Chain returned false",
!catalog.getCommand("Execute4b").execute(context));
checkExecuteLog("1/2/3/b");
}
// Test execution of chain "Execute4c"
public void testExecute4c() throws Exception {
load(DEFAULT_XML);
try {
catalog.getCommand("Execute4c").execute(context);
} catch (ArithmeticException e) {
assertEquals("Correct exception id",
"3", e.getMessage());
}
checkExecuteLog("1/2/3/c/b/a");
}
// Test execution of chain "Execute4d"
public void testExecute4d() throws Exception {
load(DEFAULT_XML);
try {
catalog.getCommand("Execute4d").execute(context);
} catch (ArithmeticException e) {
assertEquals("Correct exception id",
"2", e.getMessage());
}
checkExecuteLog("1/2/b/a");
}
// Test a pristine ConfigParser instance
public void testPristine() {
// Validate the "digester" property
Digester digester = parser.getDigester();
assertNotNull("Returned a Digester instance", digester);
assertTrue("Default namespaceAware",
!digester.getNamespaceAware());
assertTrue("Default useContextClassLoader",
digester.getUseContextClassLoader());
assertTrue("Default validating",
!digester.getValidating());
// Validate the "ruleSet" property
ConfigRuleSet ruleSet = (ConfigRuleSet) parser.getRuleSet();
assertNotNull("Returned a RuleSet instance", ruleSet);
assertEquals("Default chainElement",
"chain", ruleSet.getChainElement());
assertEquals("Default classAttribute",
"className", ruleSet.getClassAttribute());
assertEquals("Default commandElement",
"command", ruleSet.getCommandElement());
assertEquals("Default nameAttribute",
"name", ruleSet.getNameAttribute());
assertNull("Default namespaceURI",
ruleSet.getNamespaceURI());
// Validate the "useContextClassLoader" property
assertTrue("Defaults to use context class loader",
parser.getUseContextClassLoader());
// Ensure that there are no preconfigured commands in the catalog
checkCommandCount(0);
}
// --------------------------------------------------------- Private Methods
// Verify the number of configured commands
protected void checkCommandCount(int expected) {
int n = 0;
Iterator names = catalog.getNames();
while (names.hasNext()) {
String name = (String) names.next();
n++;
assertNotNull(name + " exists", catalog.getCommand(name));
}
assertEquals("Correct command count", expected, n);
}
// Verify the contents of the execution log
protected void checkExecuteLog(String expected) {
StringBuffer log = (StringBuffer) context.get("log");
assertNotNull("Context returned log");
assertEquals("Context returned correct log",
expected, log.toString());
}
// Load the specified catalog from the specified resource path
protected void load(String path) throws Exception {
parser.parse(catalog, this.getClass().getResource(path));
}
}
1.1 jakarta-commons/chain/src/test/org/apache/commons/chain/config/test-config-2.xml
Index: test-config-2.xml
===================================================================
<chains>
<!-- Define command and chain aliases -->
<define name="adding-command"
className="org.apache.commons.chain.impl.AddingCommand"/>
<define name="delegating-command"
className="org.apache.commons.chain.impl.DelegatingCommand"/>
<define name="delegating-filter"
className="org.apache.commons.chain.impl.DelegatingFilter"/>
<define name="exception-command"
className="org.apache.commons.chain.impl.ExceptionCommand"/>
<define name="exception-filter"
className="org.apache.commons.chain.impl.ExceptionFilter"/>
<define name="non-delegating-command"
className="org.apache.commons.chain.impl.NonDelegatingCommand"/>
<define name="non-delegating-filter"
className="org.apache.commons.chain.impl.NonDelegatingFilter"/>
<define name="test-chain"
className="org.apache.commons.chain.config.TestChain"/>
<define name="test-command"
className="org.apache.commons.chain.config.TestCommand"/>
<!-- Single command "chains" from CatalogBaseTestCase -->
<adding-command
name="AddingCommand"/>
<delegating-command
name="DelegatingCommand"/>
<delegating-filter
name="DelegatingFilter"/>
<exception-command
name="ExceptionCommand"/>
<exception-filter
name="ExceptionFilter"/>
<non-delegating-command
name="NonDelegatingCommand"/>
<non-delegating-filter
name="NonDelegatingFilter"/>
<test-chain
name="ChainBase"/>
<!-- Configurable command with settable properties -->
<test-command
name="Configurable"
foo="Foo Value"
bar="Bar Value"/>
<!-- Chains with nested commands -->
<test-chain name="Execute2a">
<delegating-command id="1"/>
<delegating-command id="2"/>
<non-delegating-command id="3"/>
</test-chain>
<test-chain name="Execute2b">
<delegating-command id="1"/>
<delegating-command id="2"/>
<delegating-command id="3"/>
</test-chain>
<test-chain name="Execute2c">
<delegating-command id="1"/>
<delegating-command id="2"/>
<exception-command id="3"/>
</test-chain>
<test-chain name="Execute2d">
<delegating-command id="1"/>
<exception-command id="2"/>
<non-delegating-command id="3"/>
</test-chain>
<test-chain name="Execute4a">
<delegating-filter id="1"
id2="a"/>
<delegating-command id="2"/>
<non-delegating-filter id="3"
id2="c"/>
</test-chain>
<test-chain name="Execute4b">
<delegating-command id="1"/>
<delegating-filter id="2"
id2="b"/>
<delegating-command id="3"/>
</test-chain>
<test-chain name="Execute4c">
<delegating-filter id="1"
id2="a"/>
<delegating-filter id="2"
id2="b"/>
<exception-filter id="3"
id2="c"/>
</test-chain>
<test-chain name="Execute4d">
<delegating-filter id="1"
id2="a"/>
<exception-filter id="2"
id2="b"/>
<non-delegating-filter id="3"
id3="c"/>
</test-chain>
</chains>
---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org