epugh       2004/03/08 15:21:31

  Modified:    configuration/conf testDigesterConfiguration.xml
                        testDigesterConfigurationBasePath.xml
                        testDigesterConfigurationReverseOrder.xml
                        testDigesterConfiguration2.xml
               configuration/xdocs changes.xml navigation.xml
               configuration project.properties
  Added:       configuration/xdocs howto_xml.xml
  Log:
  Bug 26944 from Oliver Heger.  ConfigurationXMLDocument was lost during migration 
from sandbox.
  
  Revision  Changes    Path
  1.2       +2 -2      jakarta-commons/configuration/conf/testDigesterConfiguration.xml
  
  Index: testDigesterConfiguration.xml
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons/configuration/conf/testDigesterConfiguration.xml,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- testDigesterConfiguration.xml     23 Dec 2003 15:09:05 -0000      1.1
  +++ testDigesterConfiguration.xml     8 Mar 2004 23:21:30 -0000       1.2
  @@ -1,8 +1,8 @@
   <?xml version="1.0" encoding="ISO-8859-1" ?>
   
   <configuration>
  -  <properties className="org.apache.commons.configuration.PropertiesConfiguration" 
fileName="conf/test.properties"/>
  -  <dom4j className="org.apache.commons.configuration.DOM4JConfiguration" 
fileName="conf/test.xml"/>
  +  <properties fileName="conf/test.properties"/>
  +  <dom4j fileName="conf/test.xml"/>
   </configuration>
   
     
  
  
  
  1.2       +1 -1      
jakarta-commons/configuration/conf/testDigesterConfigurationBasePath.xml
  
  Index: testDigesterConfigurationBasePath.xml
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons/configuration/conf/testDigesterConfigurationBasePath.xml,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- testDigesterConfigurationBasePath.xml     23 Dec 2003 15:09:05 -0000      1.1
  +++ testDigesterConfigurationBasePath.xml     8 Mar 2004 23:21:30 -0000       1.2
  @@ -1,7 +1,7 @@
   <?xml version="1.0" encoding="ISO-8859-1" ?>
   
   <configuration>
  -  <properties className="org.apache.commons.configuration.PropertiesConfiguration" 
fileName="test.properties"/>
  +  <properties fileName="test.properties"/>
   </configuration>
   
     
  
  
  
  1.2       +2 -2      
jakarta-commons/configuration/conf/testDigesterConfigurationReverseOrder.xml
  
  Index: testDigesterConfigurationReverseOrder.xml
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons/configuration/conf/testDigesterConfigurationReverseOrder.xml,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- testDigesterConfigurationReverseOrder.xml 23 Dec 2003 15:09:05 -0000      1.1
  +++ testDigesterConfigurationReverseOrder.xml 8 Mar 2004 23:21:30 -0000       1.2
  @@ -1,8 +1,8 @@
   <?xml version="1.0" encoding="ISO-8859-1" ?>
   
   <configuration>
  -  <dom4j className="org.apache.commons.configuration.DOM4JConfiguration" 
fileName="conf/test.xml"/>
  -  <properties className="org.apache.commons.configuration.PropertiesConfiguration" 
fileName="conf/test.properties"/>
  +  <dom4j fileName="conf/test.xml"/>
  +  <properties fileName="conf/test.properties"/>
     
   </configuration>
   
  
  
  
  1.3       +2 -2      
jakarta-commons/configuration/conf/testDigesterConfiguration2.xml
  
  Index: testDigesterConfiguration2.xml
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons/configuration/conf/testDigesterConfiguration2.xml,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- testDigesterConfiguration2.xml    16 Jan 2004 14:56:45 -0000      1.2
  +++ testDigesterConfiguration2.xml    8 Mar 2004 23:21:30 -0000       1.3
  @@ -4,8 +4,8 @@
   
   <configuration>
     <additional>
  -    <dom4j 
className="org.apache.commons.configuration.HierarchicalDOM4JConfiguration" 
fileName="testHierarchicalDOM4JConfiguration.xml"/>
  -    <dom4j 
className="org.apache.commons.configuration.HierarchicalDOM4JConfiguration" 
fileName="testDigesterConfigurationInclude1.xml" at="tables"/>
  +    <hierarchicalDom4j fileName="testHierarchicalDOM4JConfiguration.xml"/>
  +    <hierarchicalDom4j fileName="testDigesterConfigurationInclude1.xml" 
at="tables"/>
       <properties fileName="testDigesterConfigurationInclude2.properties" at="mail"/>
     </additional>
   
  
  
  
  1.14      +3 -0      jakarta-commons/configuration/xdocs/changes.xml
  
  Index: changes.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/configuration/xdocs/changes.xml,v
  retrieving revision 1.13
  retrieving revision 1.14
  diff -u -r1.13 -r1.14
  --- changes.xml       27 Feb 2004 17:41:36 -0000      1.13
  +++ changes.xml       8 Mar 2004 23:21:31 -0000       1.14
  @@ -7,6 +7,9 @@
   
     <body>
       <release version="1.0-dev-4" date="">  
  +     <action dev="oheger" type="fix">
  +             Reapply the ConfigurationXMLDocument that went missing during 
migration out of sandbox.
  +     </action>               
        <action dev="epugh" type="update">
                Apply ASL 2.0 license.  Thanks to Jeff Painter for scripting the 
conversion!
        </action>     
  
  
  
  1.6       +1 -0      jakarta-commons/configuration/xdocs/navigation.xml
  
  Index: navigation.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/configuration/xdocs/navigation.xml,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- navigation.xml    2 Mar 2004 03:27:48 -0000       1.5
  +++ navigation.xml    8 Mar 2004 23:21:31 -0000       1.6
  @@ -18,6 +18,7 @@
         <item name="Overview"            href="/overview.html"/>
         <item name="Simple Howto"        href="/howto_properties.html"/>
         <item name="ConfigurationFactory Howto"        
href="/howto_configurationfactory.html"/>      
  +      <item name="XML Howto"           href="/howto_xml.html"/>
         <item name="Composite Configuration Details"        
href="/howto_compositeconfiguration.html"/>
         <item name="To Do List"          href="/tasks.html"/>      
       </menu>
  
  
  
  1.1                  jakarta-commons/configuration/xdocs/howto_xml.xml
  
  Index: howto_xml.xml
  ===================================================================
  <?xml version="1.0"?>
  
  <document>
  
   <properties>
    <title>Configuration Factory and Hierarchical Structured Data Howto</title>
    <author email="[EMAIL PROTECTED]">Oliver Heger</author>
   </properties>
  
  <body>                
        <section name="Using a XML based Configurations">
                <p>
                        This section explains how to use Hierarchical
                and Structured XML datasets.
        </p>
      </section>        
                
        
        <section name="Hierarchical properties">
                <p>
                        The XML document we used so far was quite simple. Because of 
its
                        tree-like nature XML documents can represent data that is
                        structured in many ways. This section explains how to deal with
                        such structured documents.
                </p>
                <subsection name="Structured XML">
                        <p>
                                Consider the following scenario: An application 
operates on
                                database tables and wants to load a definition of the 
database
                                schema from its configuration. A XML document provides 
this
                                information. It could look as follows:
                        </p>
                        <source>
  <![CDATA[
  <?xml version="1.0" encoding="ISO-8859-1" ?>
  
  <database>
    <tables>
      <table tableType="system">
        <name>users</name>
        <fields>
          <field>
            <name>uid</name>
            <type>long</type>
          </field>
          <field>
            <name>uname</name>
            <type>java.lang.String</type>
          </field>
          <field>
            <name>firstName</name>
            <type>java.lang.String</type>
          </field>
          <field>
            <name>lastName</name>
            <type>java.lang.String</type>
          </field>
          <field>
            <name>email</name>
            <type>java.lang.String</type>
          </field>
        </fields>
      </table>
      <table tableType="application">
        <name>documents</name>
        <fields>
          <field>
            <name>docid</name>
            <type>long</type>
          </field>
          <field>
            <name>name</name>
            <type>java.lang.String</type>
          </field>
          <field>
            <name>creationDate</name>
            <type>java.util.Date</type>
          </field>
          <field>
            <name>authorID</name>
            <type>long</type>
          </field>
          <field>
            <name>version</name>
            <type>int</type>
          </field>
        </fields>
      </table>
    </tables>
  </database>
  ]]>
                        </source>
                        <p>
                                This XML is quite self explanatory; there is an 
arbitrary number
                                of table elements, each of it has a name and a list of 
fields.
                                A field in turn consists of a name and a data type.
                                To access the data stored in this document it must be 
included
                                in the configuration definition file:
                        </p>
                        <source>
  <![CDATA[
  <?xml version="1.0" encoding="ISO-8859-1" ?>
  
  <configuration>
    <properties fileName="usergui.properties"/>
    <dom4j fileName="gui.xml"/>
    <dom4j fileName="tables.xml"/>
  </configuration>
  ]]>
                        </source>
                        <p>
                                The additional <code>dom4j</code> element causes the 
document
                                with the table definitions to be loaded. When we now 
want to
                                read some of the properties we face a problem: the 
syntax for
                                constructing configuration keys we learned so far is 
not
                                powerful enough to access all of the data stored in 
the tables
                                document.
                        </p>
                        <p>
                                Because the document contains a list of tables some 
properties
                                are defined more than once. E.g. the configuration key
                                <code>tables.table.name</code> refers to a 
<code>name</code>
                                element inside a <code>table</code> element inside a
                                <code>tables</code> element. This constellation 
happens to
                                occur twice in the tables document.
                        </p>
                        <p>
                                Multiple definitions of a property do not cause 
problems and are
                                supported by all classes of Configuration. If such a 
property
                                is queried using <code>getProperty()</code>, the method
                                recognizes that there are multiple values for that 
property and
                                returns a collection with all these values. So we 
could write
                        </p>
                        <source>
  <![CDATA[
  Object prop = config.getProperty("tables.table.name");
  if(prop instanceof Collection)
  {
        System.out.println("Number of tables: " + ((Collection) prop).size());
  }
  ]]>
                        </source>
                        <p>
                                An alternative to this code would be the 
<code>getList()</code>
                                method of <code>Configuration</code>. If a property is 
known to
                                have multiple values (as is the table name property in 
this example),
                                <code>getList()</code> allows to retrieve all values 
at once.
                                <b>Note:</b> it is NOT legal to call 
<code>getString()</code>
                                or one of the other getter methods on a property with 
multiple
                                values; as the entire list is attempted to be returned.
                        </p>
                </subsection>
                <subsection name="Accessing structured properties">
                        <p>
                                Okay, we can obtain a list with the name of all defined
                                tables. In the same way we can retrieve a list with 
the names
                                of all table fields: just pass the key
                                <code>tables.table.fields.field.name</code> to the
                                <code>getList()</code> method. In our example this list
                                would contain 10 elements, the names of all fields of 
all tables.
                                This is fine, but how do we know, which field belongs 
to
                                which table?
                        </p>
                        <p>
                                The answer is, with our actual approach we have no 
chance to
                                obtain this knowledge! If XML documents are loaded 
this way,
                                their exact structure is lost. Though all field names 
are found
                                and stored the information which field belongs to 
which table
                                is not saved. Fortunately Configuration provides a way 
of
                                dealing with structured XML documents. To enable this 
feature
                                the configuration definition file has to be slightly 
altered.
                                It becomes:
                        </p>
                        <source>
  <![CDATA[
  <?xml version="1.0" encoding="ISO-8859-1" ?>
  
  <configuration>
    <properties fileName="usergui.properties"/>
    <dom4j fileName="gui.xml"/>
    <dom4j className="org.apache.commons.configuration.HierarchicalDOM4JConfiguration"
     fileName="tables.xml"/>
  </configuration>
  ]]>
                        </source>
                        <p>
                                Note the additional <code>className</code> attribute 
of the last
                                <code>dom4j</code> element. This attribute tells the 
configuration
                                factory that not the default class for processing XML 
documents
                                should be used, but the class 
<code>HierarchicalDOM4JConfiguration</code>.
                                As the name implies this class is capable of saving the
                                hierarchy of XML documents thus keeping their 
structure.
                        </p>
                        <p>
                                When working with such hierarchical properties 
configuration keys
                                used to query properties support an extended syntax. 
All components
                                of a key can be appended by a numerical value in 
parentheses that
                                determines the index of the affected property. This is 
explained best
                                by some examples:
                        </p>
                        <p>
                                We will now provide some configuration keys and show 
the results
                                of a <code>getProperty()</code> call with these keys 
as arguments.
                                <dl>
                                        <dt><code>tables.table(0).name</code></dt>
                                        <dd>
                                                Returns the name of the first table 
(all indices are 0 based),
                                                in this example the string 
<em>users</em>.
                                        </dd>
                                        <dt><code>tables.table(0)[EMAIL 
PROTECTED]</code></dt>
                                        <dd>
                                                Returns the value of the tableType 
attribute of the first
                                                table (<em>system</em>).
                                        </dd>
                                        <dt><code>tables.table(1).name</code></dt>
                                        <dd>
                                                Analogous to the first example returns 
the name of the
                                                second table (<em>documents</em>).
                                        </dd>
                                        <dt><code>tables.table(2).name</code></dt>
                                        <dd>
                                                Here the name of a third table is 
queried, but because there
                                                are only two tables result is 
<b>null</b>. The fact that a
                                                <b>null</b> value is returned for 
invalid indices can be used
                                                to find out how many values are 
defined for a certain property:
                                                just increment the index in a loop as 
long as valid objects
                                                are returned.
                                        </dd>
                                        
<dt><code>tables.table(1).fields.field.name</code></dt>
                                        <dd>
                                                Returns a collection with the names of 
all fields that
                                                belong to the second table. With such 
kind of keys it is
                                                now possible to find out, which fields 
belong to which table.
                                        </dd>
                                        
<dt><code>tables.table(1).fields.field(2).name</code></dt>
                                        <dd>
                                                The additional index after field 
selects a certain field.
                                                This expression represents the name of 
the third field in
                                                the second table 
(<em>creationDate</em>).
                                        </dd>
                                        
<dt><code>tables.table.fields.field(0).type</code></dt>
                                        <dd>
                                                This key may be a bit unusual but 
nevertheless completely
                                                valid. It selects the data types of 
the first fields in all
                                                tables. So here a collection would be 
returned with the
                                                values [<em>long, long</em>].
                                        </dd>
                                </dl>
                        </p>
                        <p>
                                These examples should make the usage of indices quite 
clear.
                                Because each configuration key can contain an 
arbitrary number
                                of indices it is possible to navigate through complex 
structures of
                                XML documents; each XML element can be uniquely 
identified.
                                So at the end of this section we can draw the 
following facit:
                                For simple XML documents that define only some simple 
properties
                                and do not have a complex structure the default XML 
configuration
                                class is suitable. If documents are more complex and 
their structure
                                is important, the hierarchy aware class should be 
used, which is
                                enabled by an additional <code>className</code> 
attribute as
                                shown in the example configuration definition file 
above.
                        </p>
                </subsection>
        </section>
        
        <section name="Union configuration">
                <p>
                        In an earlier section about the configuration definition file 
for
                        <code>ConfigurationFactory</code> it was stated that 
configuration
                        files included first can override properties in configuraton 
files
                        included later and an example use case for this behaviour was 
given.
                        There may be times when there are other requirements.
                </p>
                <p>
                        Let's continue the example with the application that somehow 
process
                        database tables and that reads the definitions of the affected 
tables from
                        its configuration. Now consider that this application grows 
larger and
                        must be maintained by a team of developers. Each developer 
works on
                        a separated set of tables. In such a scenario it would be 
problematic
                        if the definitions for all tables would be kept in a single 
file. It can be
                        expected that this file needs to be changed very often and 
thus can be
                        a bottleneck for team development when it is nearly steadily 
checked
                        out. It would be much better if each developer had an 
associated file
                        with table definitions and all these information could be 
linked together
                        at the end.
                </p>
                <p>
                        <code>ConfigurationFactory</code> provides support for such a 
use case,
                        too. It is possible to specify in the configuration definition 
file that
                        from a set of configuration sources a logic union 
configuration is to be
                        constructed. Then all properties defined in the provided 
sources are
                        collected and can be accessed as if they had been defined in a 
single
                        source. To demonstrate this feature let us assume that a 
developer of
                        the database application has defined a specific XML file with 
a table
                        definition named <code>tasktables.xml</code>:
                </p>
                <source>
  <![CDATA[
  <?xml version="1.0" encoding="ISO-8859-1" ?>
  
  <config>
    <table tableType="application">
      <name>tasks</name>
      <fields>
        <field>
          <name>taskid</name>
          <type>long</type>
        </field>
        <field>
          <name>name</name>
          <type>java.lang.String</type>
        </field>
        <field>
          <name>description</name>
          <type>java.lang.String</type>
        </field>
        <field>
          <name>responsibleID</name>
          <type>long</type>
        </field>
        <field>
          <name>creatorID</name>
          <type>long</type>
        </field>
        <field>
          <name>startDate</name>
          <type>java.util.Date</type>
        </field>
        <field>
          <name>endDate</name>
          <type>java.util.Date</type>
        </field>
      </fields>
    </table>
  </config>
  ]]>
                </source>
                <p>
                        This file defines the structure of an additional table, which 
should be
                        added to the so far existing table definitions. To achieve 
this the
                        configuration definition file has to be changed: A new section 
is added
                        that contains the include elements of all configuration 
sources which
                        are to be combined.
                </p>
                <source>
  <![CDATA[
  <?xml version="1.0" encoding="ISO-8859-1" ?>
  <!-- Configuration definition file that demonstrates the
       override and additional sections -->
  
  <configuration>
    <override>
      <properties fileName="usergui.properties"/>
      <dom4j fileName="gui.xml"/>
    </override>
    
    <additional>
      <dom4j 
className="org.apache.commons.configuration.HierarchicalDOM4JConfiguration"
       fileName="tables.xml"/>
      <dom4j 
className="org.apache.commons.configuration.HierarchicalDOM4JConfiguration"
       fileName="tasktables.xml" at="tables"/>
    </additional>
  </configuration>
  ]]>
                </source>
                <p>
                        Compared to the older versions of this file a couple of 
changes has been
                        done. One major difference is that the elements for including 
configuration
                        sources are no longer direct children of the root element, but 
are now
                        contained in either an <code>override</code> or 
<code>additional</code>
                        section. The names of these sections already imply their 
purpose.
                </p>
                <p>
                        The <code>override</code> section is not strictly necessary. 
Elements in
                        this section are treated as if they were children of the root 
element, i.e.
                        properties in the included configuration sources override 
properties in
                        sources included later. So the <code>override</code> tags 
could have
                        been ommitted, but for sake of clearity it is recommended to 
use them
                        when there is also an <code>additional</code> section.
                </p>
                <p>
                        It is the <code>additonal</code> section that introduces a new 
behaviour.
                        All configuration sources listed here are combined to a union 
configuration.
                        In our example we have put two <code>dom4j</code> elements in 
this area
                        that load the available files with database table definitions. 
The syntax
                        of elements in the <code>additional</code> section is 
analogous to the
                        syntax described so far. The only difference is an 
additionally supported
                        <code>at</code> attribute that specifies the position in the 
logic union
                        configuration where the included properties are to be added. 
In this
                        example we set the <code>at</code> attribute of the second 
element to
                        <em>tables</em>. This is because the file starts with a 
<code>table</code>
                        element, but to be compatible with the other table definition 
file should be
                        accessable under the key <code>tables.table</code>.
                </p>
                <p>
                        After these modifications have been performed the 
configuration obtained
                        from the <code>ConfigurationFactory</code> will allow access 
to three
                        database tables. A call of 
<code>config.getString("tables.table(2).name");</code>
                        will result in a value of <em>tasks</em>. In an analogous way 
it is possible
                        to retrieve the fields of the third table.
                </p>
                <p>
                        Note that it is also possible to override properties defined 
in an
                        <code>additonal</code> section. This can be done by placing a
                        configuration source in the <code>override</code> section that 
defines
                        properties that are also defined in one of the sources listed 
in the
                        <code>additional</code> section. The example does not make use 
of that.
                        Note also that the order of the <code>override</code> and
                        <code>additional</code> sections in a configuration definition 
file does
                        not matter. Sources in an <code>override</code> section are 
always treated with
                        higher priority (otherwise they could not override the values 
of other
                        sources).
                </p>
        </section>
        
  <section name="XML processing">
                <p>
                        We have now loaded some configuration sources and accessed 
some of the
                        properties. What else can we do? One additional feature 
provided by
                        Configuration is its support for XML-like processing of 
<code>Configuration</code>
                        objects that is implemented by the 
<code>ConfigurationXMLDocument</code>
                        class. The XML format for data exchange has become very 
popular, so there
                        are many use cases why you may want a XML-like view for your 
configuration.
                        This section shows how to make use of these features.
                </p>
                <subsection name="Basic XML access">
                        <p>
                                When it comes to XML processing of configuration 
sources the
                                <code>ConfigurationXMLDocument</code> class is usually 
involved.
                                When an instance of this class is created a 
<code>Configuration</code>
                                object is passed to the constructor. All operations 
executed on the
                                instance are then related to this configuration or a 
subset of it.
                        </p>
                        <p>
                                The most fundamental operation for treating a 
configuration source
                                as a XML document is to request a 
<code>ConfigurationXMLReader</code>
                                object from the <code>ConfigurationXMLDocument</code> 
instance.
                                The object returned by this method implements the
                                <code>org.xml.sax.XMLReader</code> interface and thus 
is a SAX 2
                                conform XML parser. This parser can then be passed to 
components
                                that can deal with SAX events. The following snippet 
shows how such
                                a SAX parser can be obtained:
                        </p>
                        <source>
  <![CDATA[
  ConfigurationXMLDocument configDoc = new ConfigurationXMLDocument(config);
  XMLReader reader = configDoc.createXMLReader();
  
  // or:
  // XMLReader reader = configDoc.createXMLReader("tables");
  // Now do something with the reader
  ...
  ]]>
                        </source>
                        <p>
                                As this example shows it is either possible to obtain 
a reader object
                                for the whole configuration or for a subset of it. The 
obtained object
                                can now be used where ever a SAX parser is supported. 
There is
                                only one thing to mention: The <code>XMLReader</code> 
returned
                                by <code>createXMLReader()</code> has been initialized 
with the
                                configuration source (or a subset of it) stored in the
                                <code>ConfigurationXMLDocument</code> instance. If now 
one of the
                                <code>parse()</code> methods is called, the passed in 
argument,
                                which usually specifies the input source to parse, is 
ignored. This
                                information is unnecessary because all parsing is 
always done on
                                the associated <code>Configuration</code> object. 
Later in this section
                                this fact should become clearer.
                        </p>
                </subsection>
                <subsection name="Working with documents">
                        <p>
                                SAX may be the most efficient, but it is surely not 
the most convenient
                                way of XML processing. If a document with a complex 
structure is to
                                be navigated, a DOM based approach is usually more 
suitable.
                        </p>
                        <p>
                                <code>ConfigurationXMLDocument</code> provides a 
couple of
                                overloaded <code>getDocument()</code> methods that 
return a dom4j
                                <code>Document</code> object. The arguments that can 
be passed to
                                these methods allow to select a subset of the 
configuration source and
                                to specify the name of the resulting document's root 
element. The following
                                code fragment provides an example:
                        </p>
                        <source>
  <![CDATA[
  ConfigurationXMLDocument configDoc = new ConfigurationXMLDocument(config);
  Document doc = configDoc.getDocument("tables", "database");
  ...
  ]]>
                        </source>
                        <p>
                                The <code>Document</code> returned here contains all 
data below the
                                <em>tables</em> section, i.e. it will have the root 
element <em>database</em>
                                with three <em>table</em> elements as children. In 
addition to the
                                <code>getDocument()</code> methods there is also a set 
of
                                <code>getW3cDocument()</code> methods. These methods 
act in an
                                analogous way, but return a 
<code>org.w3c.dom.Document</code> object
                                rather than a dom4j document.
                        </p>
                        <p>
                                Once a DOM document has been obtained the whole world 
of DOM processing
                                is open. Especially dom4j allows for powerful and 
convenient manipulations,
                                e.g. the document could be transformed using a 
stylesheet or written to
                                a file. If a configuration source or parts of it 
should simply be saved as
                                a XML document, there is an even easier way: the 
<code>write()</code>
                                methods of <code>ConfigurationXMLDocument</code>. 
Let's assume our
                                example application wants to send its database table 
definitions to an
                                external tool, maybe to initialize the database 
schema. The following code
                                shows how an XML file with this information could be 
written:
                        </p>
                        <source>
  <![CDATA[
  ConfigurationXMLDocument configDoc = new ConfigurationXMLDocument(config);
  Writer out = null;
  try
  {
      out = new BufferedWriter(new FileWriter("tabledef.xml"));
      configDoc.write(out, "tables", "database");
  }
  finally
  {
      out.close();
  }
  ]]>
                        </source>
                </subsection>
                <subsection name="Calling Digester">
                        <p>
                                <em>Commons Digester</em> is another Apache Jakarta 
project that
                                implements a powerful engine for processing XML 
documents. In this section
                                we will make use of Digester to transform the table 
definitions into a
                                corresponding object model. For this tutorial the 
interesting part is
                                the stuff related to the communication with Digester; 
more information
                                about Digester and its features can be found at the 
homepage of the
                                Digester project.
                        </p>
                        <p>
                                We start with with the definition of a set of beans 
that will later store
                                information about the database tables and their 
fields. To keep this
                                example short these are very simple classes with 
hardly more than
                                a couple of getter and setter methods. As you can see 
there are
                                corresponding properties for all elements that can 
appear in the
                                table configuration.
                        </p>
                        <source>
  <![CDATA[
  public class TestConfigurationXMLDocument
  {
      /** Stores the tables.*/
      private ArrayList tables;
      
      /**
       * Adds a new table object. Called by Digester.
       * @param table the new table
       */
      public void addTable(Table table)
      {
          tables.add(table);
      }
  
      /**
       * A simple bean class for storing information about a table field.
       * Used for the Digester test.
       */
      public static class TableField
      {
          private String name;
          private String type;
          
          public String getName()
          {
              return name;
          }
  
          public String getType()
          {
              return type;
          }
  
          public void setName(String string)
          {
              name = string;
          }
  
          public void setType(String string)
          {
              type = string;
          }
      }
      
      /**
       * A simple bean class for storing information about a database table.
       * Used for the Digester test.
       */
      public static class Table
      {
          /** Stores the list of fields.*/
          private ArrayList fields;
          
          /** Stores the table name.*/
          private String name;
          
          /** Stores the table type.*/
          private String tableType;
          
          public Table()
          {
              fields = new ArrayList();
          }
          
          public String getName()
          {
              return name;
          }
  
          public String getTableType()
          {
              return tableType;
          }
  
          public void setName(String string)
          {
              name = string;
          }
  
          public void setTableType(String string)
          {
              tableType = string;
          }
          
          /**
           * Adds a field.
           * @param field the new field
           */
          public void addField(TableField field)
          {
              fields.add(field);
          }
          
          /**
           * Returns the field with the given index.
           * @param idx the index
           * @return the field with this index
           */
          public TableField getField(int idx)
          {
              return (TableField) fields.get(idx);
          }
          
          /**
           * Returns the number of fields.
           * @return the number of fields
           */
          public int size()
          {
              return fields.size();
          }
      }
  }
  ]]>
                        </source>
                        <p>
                                While <code>TableField</code> is almost trivial, 
<code>Table</code>
                                provides the ability of storing a number of field 
objects in an internal
                                collection. The test class also defines a collection 
that will later
                                store the <code>Table</code> objects created from the 
configuration.
                        </p>
                        <p>
                                To pass the table configuration to Digester we have to
                                <ol>
                                        <li>
                                                Ask our 
<code>ConfigurationXMLDocument</code> instance for
                                                a <code>XMLReader</code> for the 
<em>tables</em> section.
                                        </li>
                                        <li>
                                                Construct a new Digester object and 
pass this XML reader to
                                                the constructor.
                                        </li>
                                        <li>
                                                Initialize the Digester instance with 
the necessary processing rules
                                                to create a corresponding object 
hierarchy for the table definitions.
                                        </li>
                                        <li>
                                                Call the Digester's 
<code>parse()</code> method to initiate
                                                processing.
                                        </li>
                                </ol>
                                The following listing shows the implementation of 
these steps:
                        </p>
                        <source>
  <![CDATA[
      public void testCallDigesterComplex() throws Exception
      {
          Digester digester = 
              new Digester(configDoc.createXMLReader("tables"));
          digester.addObjectCreate("config/table", Table.class);
          digester.addSetProperties("config/table");
          digester.addCallMethod("config/table/name", "setName", 0);
          digester.addObjectCreate("config/table/fields/field", TableField.class);
          digester.addCallMethod("config/table/fields/field/name",
              "setName", 0);
          digester.addCallMethod("config/table/fields/field/type",
              "setType", 0);
          digester.addSetNext("config/table/fields/field",
              "addField", TableField.class.getName());
          digester.addSetNext("config/table", "addTable", Table.class.getName());
          
          digester.push(this);
          digester.parse("config");
      }
  ]]>
                        </source>
                        <p>
                                At the beginning of this listing 
<code>createXMLReader()</code>
                                is called on the <code>ConfigurationXMLDocument</code> 
instance
                                to obtain a SAX parser for the configuration subset 
with the table
                                definitions. When the Digester object is created this 
parser is passed
                                to its constructor.
                        </p>
                        <p>
                                The major part of the fragment deals with setting up 
the necessary
                                Digester rules. Details for that can be found in the 
Digester documentation.
                                In short we define rules to create <code>Table</code> 
and
                                <code>TableField</code> objects when the corresponding 
XML elements
                                are detected. The newly created objects are 
initialized with the
                                properties defined in the XML code. Then it is ensured 
that a new
                                <code>TableField</code> object is added to a 
<code>Table</code>
                                and that for each new <code>Table</code> object the
                                <code>addTable()</code> method of the test class is 
invoked.
                                This all conforms to the default usage pattern of 
Digester, only two
                                points should be noticed:
                        </p>
                        <p>
                                <ul>
                                        <li>
                                                The match strings for all Digester 
rules have the prefix
                                                <em>config/table</em>. The reason for 
this is that the XML document
                                                that is generated by the SAX parser 
has a corresponding structure.
                                                Remember when we called 
<code>createXMLReader()</code>, we
                                                specified the string <em>tables</em> 
as argument. This means that
                                                the resulting document will have all 
the content that can be found
                                                in the configuration under the key 
<em>tables</em>, which happens
                                                to be three <em>table</em> elements 
with their corresponding
                                                children. The root element of this 
document is named <em>config</em>.
                                                This is the default name of the root 
element if no other name is
                                                specified. If we had called 
<code>createXMLReader("tables", "tabledef")</code>,
                                                the root element would have been named 
<em>tabledef</em> and
                                                we would have to use match strings of 
the form <em>tabledef/table</em>.
                                        </li>
                                        <li>
                                                The call of the Digester's 
<code>parse()</code> method is a little
                                                strange because we only pass in the 
string <em>config</em> as
                                                argument. Fact is that if a 
<code>XMLReader</code> obtained
                                                from 
<code>ConfigurationXMLDocument</code> is involved, the
                                                parameter of the <code>parse()</code> 
method is completely
                                                ignored. The reader always refers to 
the configuration source it
                                                was constructed for. So we could have 
used an arbitrary string.
                                        </li>
                                </ul>
                        </p>
                        <p>
                                After the <code>parse()</code> method returns the 
<code>tables</code>
                                collection of the test class contains three 
<code>Table</code> objects
                                with all information specified in the configuration.
                        </p>
                </subsection>
                <subsection name="Calling Digester for creating simple objects">
                        <p>
                                If an application's configuration defines complex 
objects that should
                                be instantiated using Digester it will usually be 
necessary to provide
                                specific Digester rules as shown in the last section. 
But another
                                use case is to create an object whose class name is 
defined in the
                                configuration and to perform some simple 
initialization on it.
                        </p>
                        <p>
                                Imagine the example database application wants to open 
a connection to
                                a database. Because it is a very sophisticated 
application it supports
                                lots of different databases. To achieve this there is 
an abstract
                                <code>ConnectionInfo</code> class that provides 
typical connect
                                properties (such as user name, password and the name 
of the database)
                                and an abstract <code>connect()</code> method, which 
establishes
                                the connection to the database:
                        </p>
                        <source>
  <![CDATA[
      public abstract class ConnectionInfo
      {
          private String dsn;
          private String user;
          private String passwd;
          
          public String getDsn()
          {
              return dsn;
          }
  
          public String getPasswd()
          {
              return passwd;
          }
  
          public String getUser()
          {
              return user;
          }
  
          public void setDsn(String string)
          {
              dsn = string;
          }
  
          public void setPasswd(String string)
          {
              passwd = string;
          }
  
          public void setUser(String string)
          {
              user = string;
          }
          
          /**
           * Creates a connection to a database.
           * Must be defined in sub classes.
           * @return the created connection
           */
          public abstract Connection createConnection() throws SQLException;
      }
  ]]>
                        </source>
                        <p>
                                There are now some sub classes of this class, one for 
each supported
                                database with a proper implementation of 
<code>createConnection()</code>.
                                When the example application is run it does not know 
from start,
                                which database it has to access. Instead it determines 
the database
                                driver class from a configuration property, creates an 
instance of it,
                                and uses it to obtain a connection. For this use case 
the
                                <code>callDigester()</code> method of 
<code>ConfigurationXMLDocument</code>
                                was designed.
                        </p>
                        <p>
                                To demonstrate this feature we start with an 
additional configuration source
                                that defines the connection of the database to be 
used. We call it
                                <code>connection.xml</code>.
                        </p>
                        <source>
  <![CDATA[
  <?xml version="1.0" encoding="UTF-8"?>
  <configuration>
        <connection>
                <class name="myapp.ConnectionInfoOracle">
                        <property name="dsn" value="MyData"/>
                        <property name="user" value="scott"/>
                        <property name="passwd" value="tiger"/>
                </class>
        </connection>
  </configuration>
  ]]>
                        </source>
                        <p>
                                This configuration file shows the typical structure of 
XML code
                                that defines an object to be created using Digester: 
There must be
                                a <code>class</code> element with a <code>name</code> 
attribute
                                defining the full qualified name of the class to be 
instantiated. In the
                                body of the <code>class</code> element there can be an 
arbitrary
                                number of <code>property</code> elements. Each element 
defines
                                the name and value of a property to be set, so 
Digester will call a
                                corresponding setter method on the bean just created. 
Here we set
                                properties with the names <em>dsn, user</em> and 
<em>passwd</em>.
                                As you can see, our <code>ConnectionInfo</code> base 
class has
                                getter and setter methods for exactly these 
properties. We have now to
                                include this additional configuration file in our 
configuration definition file:
                        </p>
                        <source>
  <![CDATA[
  <?xml version="1.0" encoding="ISO-8859-1" ?>
  <!-- Configuration definition file that demonstrates the
       override and additional sections -->
  
  <configuration>
    <override>
      <properties fileName="usergui.properties"/>
      <dom4j fileName="gui.xml"/>
    </override>
    
    <additional>
      <hierarchicalDom4j fileName="tables.xml"/>
      <hierarchicalDom4j fileName="tasktables.xml" at="tables"/>
      <hierarchicalDom4j fileName="connection.xml"/>
    </additional>
  </configuration>
  ]]>
                        </source>
                        <p>
                                After that it is now quite easy to obtain an object 
with information
                                about a database connection. The following code 
fragment shows
                                how a connection can be opened (it assumes that the 
<code>Configuration</code>
                                object obtained from the configuration factory is 
stored in a variable named
                                <code>config</code>).
                        </p>
                        <source>
  <![CDATA[
  ConfigurationXMLDocument configDoc = new ConfigurationXMLDocument(config);
  ConnectionInfo connInfo = (ConnectionInfo) configDoc.callDigester("connection");
  Connection conn = connInfo.createConnection();
  ]]>
                        </source>
                </subsection>
                <subsection name="Some caveats">
                        <p>
                                At the end of this section about 
<code>ConfigurationXMLDocument</code>
                                some notes about points a developer should be aware of 
are provided:
                        </p>
                        <p>
                                <ul>
                                        <li>
                                                The class should not be used on the 
<code>Configuration</code>
                                                object obtained from 
<code>ConfigurationFactory</code> as a
                                                whole. Because this configuration can 
contain multiple configuration
                                                sources including those that override 
other properties the results
                                                are probably not what you expect. You 
can of course pass such a
                                                composite configuration to the 
constructor of
                                                <code>ConfigurationXMLDocument</code>, 
but you should then,
                                                when you call methods on this 
instance, always provide a
                                                configuration key that selects certain 
parts of the configuration.
                                        </li>
                                        <li>
                                                The XML processing abilities of the 
class naturally work best
                                                when a 
<code>HierarchicalConfiguration</code> object is
                                                involved. There is also support for 
other types of configuration
                                                sources, but this will work well only 
for very simple properties.
                                                The problem here is the loss of 
information concerning the
                                                structure of the properties (as was 
explained in an earlier secion). 
                                                So if you read a configuration source
                                                using the <code>dom4j</code> element 
rather than the
                                                <code>hierarchicalDom4j</code> 
element, XML documents
                                                generated by 
<code>ConfigurationXMLDocument</code> may
                                                look wired; the same is true for 
<code>properties</code>
                                                elements.
                                        </li>
                                        <li>
                                                If you read a XML configuration file 
and then save it again
                                                using 
<code>ConfigurationXMLDocument.write()</code> the
                                                result is not guaranteed to be identic 
to the original file.
                                                While the document structure is kept 
(i.e. the relation between
                                                elements and their children), there 
may be differences in the
                                                order in which elements are written.
                                        </li>
                                </ul>
                        </p>
                </subsection>
        </section>      
  </body>
  
  </document>
  
  
  1.6       +1 -0      jakarta-commons/configuration/project.properties
  
  Index: project.properties
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/configuration/project.properties,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- project.properties        18 Feb 2004 04:11:35 -0000      1.5
  +++ project.properties        8 Mar 2004 23:21:31 -0000       1.6
  @@ -17,6 +17,7 @@
   maven.checkstyle.format = turbine
   
   maven.junit.fork=true
  +maven.test.failure.ignore=true
   
   #cactus settings.  Make sure to point to your Tomcat!
   cactus.home.tomcat4x = c:/java/tomcat
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to