subscribe: Posts | Comments

Jakarta Commons Digester

0 comments

I. Introduction
The API commons digester, originally an integral part of the Struts framework, aims to create objects from data contained in XML files, this is more commonly called a mapping XML / Object. The commons digester, you can create objects on the fly when a certain sequence tag is detected or call specific methods on these objects in their passing parameters from the XML file.

Initially, the commons digester that were altered by the programming must be added the rules one by one through method calls Java, from version 1.2, rules can be specified within d ' an XML file (which is read by the commons digester using … Digester), which offers greater flexibility and configuration non-intrusive.
In this article, based on version 1.7, we will present the general operating principles of the API and most of the rules.

II. General Principles
II.A. Organization

As suggested by the structure of an XML document, the commons digester operate on a logic tree. The result of the analysis of a file is a root from which we should be able to access other. The connection of objects between them and the root object is to be borne by the user. As we shall see, it is possible to ask the Digester to create objects, which are temporarily placed on a stack (the stack of objects) to be used, but at the end of the analysis of XML file , Only the root element is available (unless you manually stacked other objects beforehand).

Therefore, you arrange to recover all items from the root, it may simply be a collection to which we add the various elements as their creation.

II.B. Rules
The way will be interpreted the XML file is specified by a set of rules. A rule is a combination of a pattern (see below) and a type of rule. The creation of custom rules beyond the scope of this article and will therefore not be discussed here. We will describe the basic rules provided with commons digester, which already offer a wide variety of possibilities. We must bear in mind that the rules are called in the order they were added to Digester openings on the tags and in reverse order on the closure, the operation of certain rules based on this behavior.

When setting a rule whatsoever, it is necessary to indicate on what pattern it must be triggered. The definition of a pattern is relatively simple, it is the path to the element. Assuming that there is an XML file the following form:
<? xml version = "1.0"?>
<racine>
<premier-niveau>
<data/>
<data/>
</ first-level>
<data/>
</ root>
The definition of a rule should be triggered for the first two data will be done in showing the pattern root / first-level / data. If you want the rule is triggered regardless of the location data, you can specify * / data. In cases where a generic pattern and a particular pattern are both a road, this is the rule corresponding to the particular pattern that is used (the best matching pattern). Reprennant In the above example, if one defines a rule R1 associated with the root pattern, first-level / data and a rule R2 associated with the pattern * / data, R1 is to be used for the first two data and R2 in the second.

By default, commons digester bear two types of rules cited above. It is possible to extend the possibilities by calling the method of setRules Digester and passing it an instance of ExtendedBaseRules:
Digester digester = new Digester ();
digester.setRules (new ExtendedBaseRules ());

The use of such rules provides access to more features:
* If the pattern is predetermined by us, the principle of best matching pattern no longer applies, the rules corresponding to the exact pattern are applied, but also those corresponding to the generic pattern, which may be desirable in some cases;
* The generic patterns offer more opportunities, it is possible to specify a rule for all direct children of certain elements via the? (* first-level?) or for all descendants (direct or not) of some elements (root / first-level / *).

The types of rules available are presented in section Standards Rules

III. Using Digesters
III.A. Adding rules programmatically

The addition of rules is a programming first appeared, it is not very flexible but prevents the user from the application does change a configuration file by mistake. The addition of rules is done in two stages:
/ / 1) Declaration of Digester
Digester digester = new Digester ();
/ / 2) Adding rules
/ / Directly
digester.addRule ( "root", new ObjectCreateRule (Root.class));
/ / Or using the shortcut method for standard rules
digester.addObjectCreate ( "root / first-level", Child.class);
As can be seen in this example, the Digester has shortcut methods for the addition of standard rules. The different types of rules provided with commons digesters are described in section Rules standards.

III.B. Definition of rules in an XML file
The configuration using an XML file can use a simple standard rules but also rules customized. Every rule has its own standard tag. For simple cases, you can specify the pattern in full for each rule as in the following example:
<? xml version = "1.0"?>
<digester-rules>
<object-create-rule pattern="racine" classname="Root"/>
<object-create-rule pattern="racine/premier-niveau" classname="Child"/>
<set-next-rule pattern="racine/premier-niveau" methodname="addChild"/>
</ digester-rules>
This configuration creates an object when it meets Root root and it adds Child objects it creates as when it encounters the first-level tags.

When the number of rules increases, it may be useful to structure the document so stronger for it, you can use the tag pattern:
<? xml version = "1.0"?>
<digester-rules>
<pattern value="racine">
<object-create-rule Classname="Root"/>
<pattern Value="premier-niveau">
<object-create-rule classname="Child"/>
<set-next-rule methodname="addChild"/>
</ pattern>
</ pattern>
</ digester-rules>
Each pattern is concatenated to those higher levels, and the rules of creation and addition of children will be triggered on the pattern root / first-level.

Using custom rules can be done by amending the DTD as well as mechanisms for loading file. A better way may be to use the tag include. It has an attribute class which means a class implementing org.apache.commons.digester.xmlrules.DigesterRulesSource. This interface defines one method, getRules (Digester), which must add its own rules Digester passed as a parameter. When adding these rules, we must bear in mind that if include is made within a tag pattern, it is added to all the patterns specified in the DigesterRulesSource. So if you have a include in a pattern root and the addition of rules with the pattern first-level within the DigesterRulesSource, the pattern of these rules will actually root / first-level.

The loading of configuration file can be done in two ways, a simple version:
Digester digester DigesterLoader.createDigester = (getClass (). GetResource ( "/ rules.xml"));
rules.xml where is the XML file describing the rules. A more complex but can benefit from the extended rules by performing the appropriate initializations is also available:
Digester digester = new Digester ();
/ / This line can be removed if one does not use extended rules
digester.setRules (new ExtendedBaseRules ());
digester.addRuleSet (new FromXmlRuleSet (getClass (). getResource ( "/ rules.xml ")));

III.C. Launching the analysis
Once the Digester correctly configured, you have to start analysing the XML file, this is the method parse:
root = (Root) digester.parse (getClass (). getResourceAsStream ( "/" + FILE_NAME));
An exception is lifted if a problem occurs during the analysis of the file. The parse method returns the root element. When it ends, the stack of objects is empty, it is therefore necessary to have combined the elements of the intended manner by means of rules.

IV. Standards Rules
For every rule, the class name is given, followed by the shortcut method defined in the digester and tag for a configuration XML.

IV.A. ObjectCreateRule
* Class: org.apache.commons.digester.ObjectCreateRule
* Method: addObjectCreate
* Tag: object-create-rule
The rule ObjectCreateRule is one of the most useful. As its name suggests, it will create an object and then place it on the stack of objects when meeting the opening tag of its pattern. When the closing tag is encountered, the subject at the top of the stack, which normally corresponds to the one created dépilé is lost and therefore it was not attached to another. This behavior can create an object, to link the previous then treat girls tags to define properties of this object. Note that this rule uses the empty constructor of the class to create.

Consider the following set of rules:
Digester d = new Digester ();
d.addObjectCreate ( "root", Root.class);
d.addObjectCreate ( "root / first-level", Child.class);
/ / This rule is explained later
d.addSetNext ( "root / first-level", "addChild");
Root r = d.parse (getClass (). GetResourceAsStream ( "/ test.xml"));
He asks the Digester to create an object Root (class is indicated by the type attribute for a configuration XML) when it encounters the root tag. Presumably, as its name suggests, this tag is the root of the XML file and it appears only once, at the beginning. The object thus created will be returned by the method parse. The second rule request to create an object Child when the first-level tag is encountered within root, the object is placed on the stack and the Treaty. The final rule takes aim at the top of the pile and the argument goes to a method of the object "below" (the root here).

Here are the evolution of the stack during the analysis of the following file:
<racine>
<! – Note: The first-level tag is written in this
form a question for ease of explanation ->
<premier-niveau> </ first-level>
</ root>
Evolution of the pile
Evolution of the pile
The first-level tags could have written <premier-niveau/> would have changed nothing.

IV.B. SetNextRule, SetRootRule & SetTopRule
* Class: org.apache.commons.digester.SetNextRule
* Method: addSetNext
* Tag: set-next-rule
This rule is also frequently used in conjunction with ObjectCreateRule (see above example). It can be called a method of the object following that the top of the pile, passing on the subject of setting up battery (therefore calls on a method of the parent object to passing the subject child). The method to call is specified in the manufacturer (or via attribute methodname XML). It is triggered on the closing tag so that the subject at the top of the battery is fully initialized when the method is called.

If one takes the example of ObjectCreateRule you can add the following sequence:
Hold SetNextRule
Hold SetNextRule
Insofar SetNextRule was added after ObjectCreateRule and that the rules are called in reverse order of their addition on the closing tags, we have the method call on the parent object that is paid before the dépilage 'object child what is expected behavior.

SetRootRule The rule is similar to SetNextRule but the method is called on the root of the stack and not on the second object. SetTopRule about it makes Unlike SetNextRule it calls a method on the object from the top of the pile, passing the next object.

IV.C. FactoryCreateRule
* Class: org.apache.commons.digester.FactoryCreateRule
* Method: addFactoryCreate
* Tag: factory-create-rule
This rule is used in a manner similar to CreateObjectRule but allows manufacturers to plead with parameters. The parameters must be obtained via attributes of the tag for which is set rule.

To be able to recover these attributes and pass the manufacturer of the item, you must create a Factory which implements the interface org.apache.commons.digester.ObjectCreationFactory (you can extend the class org.apache.commons.digester. AbstractObjectCreationFactory which already defines the basic methods). The method central thereof is objectCreate (Attributes), it refers to the new object, here's an example:
public Object createObject (Attributes attributes) throws Exception (
String name = attributes.getValue ( "name");
if (name == null) (
return new Child ();
)
return new Child (name);
)
The Factory containing this method creates a new instance of the Child class by giving it a name if the name attribute is present. Assuming that the rule is attached to the same pattern as in the example of ObjectCreateRule, Meet the tag

<premier-niveau name="UnEnfant">

creates a new instance of class Child with the name UnEnfant and adds to the pile of objects which makes it possible for other rules.
In defining the rule, it is necessary to clarify at least the name of the class Factory (via the manufacturer or through the attribute classname XML). You can also specify the name of an attribute which, if present, indicate the class of the Factory to be used in place of that past the manufacturer (the attribute name is specified in the constructor or through attribute attrname XML).

IV.D. SetPropertyRule
* Class: org.apache.commons.digester.SetPropertyRule
* Method: addSetProperty
* Tag: set-property-rule
This rule sets a property (via a method setXXX) on the subject at the top of the pile (usually created by a ObjectCreateRule with the same pattern that SetPropertyRule). The name and value of the property to position are specified in attributes of the tag for which is triggered the rule.

Consider the sequence of adding rules follows:
Digester digester = new Digester ();
/ / Rules omitted for brevity
d.addObjectCreate ( "root / first-level", MaClasse.class);
d.addSetProperty ( "root / first-level", "prop-name", "value");
It calls for the creation of an object type MaClasse when the first-level tag is found then the positioning of the property whose name is given in the attribute prop-name and value in the attribute value. Thus, when analysing the XML file:
<racine>
<premier-niveau prop-name="name" value="Premier Level (1)"/>
</ root>

The sequence of events will look like the following:
Triggering of the rule SetPropertyRule
Triggering of the rule SetPropertyRule

IV.E. SetPropertiesRule
* Class: org.apache.commons.digester.SetPropertiesRule
* Method: addSetProperties
* Tag: set-properties-rule
This rule allows you to take the attributes of a tag as object properties located at the top of the pile (usually created by a ObjectCreateRule with the same pattern). It takes every attribute of the tag and calls the method setNomAttribut on the subject from the top of the pile. The attributes do not correspond to any property are ignored by default.

Assuming that we define the following rules:
Digester digester = new Digester ();
/ / Rules omitted for brevity
d.addObjectCreate ( "root / first-level", MaClasse.class);
d.addSetProperties ( "root / first-level");

With this XML file:
<racine>
<premier-niveau name="Premier Level (1)" priority="low"/>
</ root>
Once the object created, methods setName ( "Premier Level (1)") and setPriority ( "low") will be called.
It is possible to ensure that the names of attributes are not used directly as names of properties, why, we must spend two tables manufacturer, the first indicates the names of attributes, the latter corresponding properties (in the same order). The attributes unreported in this table are mapped directly. If you use an XML file to define the rules, it is necessary to tag alias:

<set-properties-rule pattern="racine/premier-niveau">
<alias attr-name="name" prop-name="nom">
</ set-properties-rule>
If we had this kind of definition in our example, methods setNom and setPriority will be called.

IV.F. CallMethodRule, CallParamRule & ObjectParamRule
* Class: org.apache.commons.digester.CallMethodRule
* Method: addCallMethod
* Tag: call-method-rule
Used alone, the rule CallMethodRule this relatively little interest, it merely to call a method on the subject at the top of the pile. The name of the method is passed as a parameter to the manufacturer or via attribute methodName for a configuration XML. Please note that the appeal of the method is carried out when the closing tag of the pattern is found which allows to set up parameters (see below).
* Class: org.apache.commons.digester.CallParamRule
* Method: addCallParam
* Tag: call-param-rule

CallMethodRule offers more interest when combined with CallParamRule. The latter will put parameters on the stack parameters that is distinct from that which are stacked objects created as the analysis. These parameters can come attributes of the element of the pattern, its contents or objects on the stack of objects. When the closing tag of the pattern of the CallMethodRule is encountered, the necessary parameters are dépilés and passed to the method specified.

We will not give an example here that for more complex form of the rule. Here is the set of rules used:
d.addObjectCreate ( "root", Root.class);
d.addCallMethod ( "root / first-level", "view", 2, new Class [] (Child.class, String.class));
/ / Creating a child
d.addObjectCreate ( "root / first-level", Child.class);
/ / Definition of properties (for display)
d.addSetProperties ( "root / first-level");
d.addCallParam ( "root / first-level", 0, true);
d.addCallParam ( "root / first-level", 1, "message");
The order reporting rules is very important. First, we declare a rule that creates an object Root when the root tag is encountered. The CallMethodRule being declared first, it will be executed last for the root pattern, first-level (it runs on the closing tag). It is the behaviour expected since it takes that can create all the parameters to pass the method before it is called.

The last two rules, CallParamRule, show the two possible uses of such rules. First, it indicates that the first parameter is the object perched atop the pile of objects (it is possible to indicate another specifying its index in the stack in place of boolean), it s is therefore in the instance of Child, which has just been created. In the second case, we give the name of the attribute of the pattern which will be transmitted as a parameter. There is another form for this rule, it only takes a pattern and the index parameter in the method, in which case the tag content of the pattern which is passed as a parameter.

In considering this XML file:
<racine>
<premier-niveau name="Premier level 1" message="Message 1"/>
<premier-niveau name="Premier level 2" message="Message 2"/>
</ root>
It will successively be created Root and placing on the stack object. Then, for each first-level tag, creating an object Child and placing on the stack of objects, defining its properties, put this item on the stack parameters (index 0), recovery of the value of the attribute message of the first tag-level and placing on the stack parameters (1 index). On the closure of the first tag-level, suppression of the object at the top of the pile (Child object created earlier) call to the post on the subject of the battery (which is now the Root originally created) which gets its parameters on the stack parameters (the Child and content of the message attribute).

When you want to pass an object constant defined by the programming method (for example, a chain version), you can go by the rule that requires ObjectParamRule index parameter and object to pass. For an XML configuration must specify the class of the object and provide the initial value (attributes type and value). You can condition the passage of this parameter in the presence of an attribute in the tag for which is triggered the rule (attrname attribute for a configuration XML).

info DTD file the corresponding definition of the rules indicates an attribute parameters required for object-param-rule, however, it is not used. This error will be corrected in the next version of commons digester.

IV.G. BeanPropertySetterRule & SetNestedPropertiesRule
* Class: org.apache.commons.digester.BeanPropertySetterRule
* Method: addBeanPropertySetter
* Tag: bean-property-setter-rule
The rule BeanPropertySetterRule positioning property of the object at the top of the pile of objects using a method setXXX. The name of the property is the name of the tag defined pattern in the past or that the manufacturer (owned property-name XML). The value of the property is the content of the tag.

Thus Rule

d.addBeanPropertySetter ( "root / first-level / name");
setName call the method of the object at the top of the pile, passing the contents of the tag name.
* Class: org.apache.commons.digester.SetNestedPropertiesRule
* Method: addSetNestedProperties
* Tag: set-nested-properties-rule
The rule setNestedPropertiesRule acts in a manner similar to BeanPropertySetterRule but called setters appropriate for all tags girls pattern for which it is defined. You must be careful when using it because it takes into account the subject at the top of battery power.

V. Games rules
Often there are sets of rules related. To simplify the application, it may be desirable, rather than make a configuration rule by rule, adding each set of rules without worrying about details. It is the goal of the game rules (specified by the interface org.apache.commons.digester.RuleSet). You create your own implementation of RuleSet (or lie RuleSetBase) which is in charge add the rules one by one. This addition is done within the method addRuleInstances by calling the appropriate methods (addRule and others) on the Digester passed as a parameter. At initialization Digester, it will have to replace the series of calls to methods such addRule a reduced number of calls the method addRuleSet for each RuleSet created. The code generated is more clear.

VI. Validation of material
It may be useful to validate the document analyzed by the Digester vis-à-vis a DTD (Document Type Definition). By default, inconsistencies are logged during the analysis but does not interrupt the process. To change this behavior, it must create its own implementation of the interface org.xml.sax.ErrorHandler. It defines three methods that are called according to the level of seriousness of the error encountered (noncompliance with a DTD is level error). To stop the analysis, you have to lift a SaxException.

VII. Conclusion
This article has provided the foundation to use most features of the commons digester. You can find the complete javadoc and various example on the project site Apache Jakarta Commons.