| <?xml version="1.0"?> |
| |
| <document> |
| |
| <properties> |
| <title>Configuration Factory and Hierarchical Structured Data Howto</title> |
| <author email="oliver.heger@t-online.de">Oliver Heger</author> |
| </properties> |
| |
| <body> |
| <section name="Using 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 in the section about composite configuration 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"/> |
| <xml fileName="gui.xml"/> |
| <xml fileName="tables.xml"/> |
| </configuration> |
| ]]> |
| </source> |
| <p> |
| The additional <code>xml</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 legal to call <code>getString()</code> |
| or one of the other getter methods on a property with multiple |
| values; it returns the first element of the list. |
| </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"/> |
| <xml fileName="gui.xml"/> |
| <hierarchicalXml fileName="tables.xml"/> |
| </configuration> |
| ]]> |
| </source> |
| <p> |
| Note that one <code>xml</code> element was replaced by a |
| <code>hierarchicalXml</code> element. This element tells the configuration |
| factory that not the default class for processing XML documents |
| should be used, but the class <code>HierarchicalXMLConfiguration</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)[@tableType]</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"/> |
| <xml fileName="gui.xml"/> |
| </override> |
| |
| <additional> |
| <hierarchicalXml fileName="tables.xml"/> |
| <hierarchicalXml 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>xml</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"/> |
| <xml fileName="gui.xml"/> |
| </override> |
| |
| <additional> |
| <hierarchicalXml fileName="tables.xml"/> |
| <hierarchicalXml fileName="tasktables.xml" at="tables"/> |
| <hierarchicalXml 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>xml</code> element rather than the |
| <code>hierarchicalXml</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> |