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