Skip to main content

skip to main content

developerWorks  >  XML | Java technology  >

Java configuration with XML Schema

A sample XML Schema and Java class show you how

developerWorks
Document options

Document options requiring JavaScript are not displayed

Sample code


Rate this page

Help us improve this content


Level: Introductory

Marcello Vitaletti (Marcello_Vitaletti@tivoli.com), Software engineer, IBM - Tivoli

01 Nov 2001

This article shows how to use a Java XML parser together with the Java language reflection features to create an arbitrary set of named objects according to the content of an XML file. Objects created by the proposed initialization process live in a hierarchical, global namespace. References to these objects anywhere in the code can be obtained by a simple query. The configuration-file XML schema discussed in this article is designed to specify the creation of "parameter objects" as instances of arbitrary Java classes.

One thing developers usually ponder when starting a new project is the strategy they need to employ to write good parametric code. The behavior of good parametric code can be influenced by parameters that can be set at run time. As an example, a graphics format converter may implement a wide range of different transformations depending on the specified values of several parameters. At run time, this application requires the names of the two files holding the original and the transformed image, respectively. This application may read the transformation parameters from an additional input file.

There are obvious advantages in keeping parameters distinct from other input data sources: The same set of parameters may apply to many different runs, or the specification of working parameters may require an in-depth knowledge of the application's internal structure and algorithms. As an extreme case, parameters could be undocumented and the code distributed together with a default parameter set. By this approach, the authors may enable a different behavior of an application by distributing a different set of parameters.

In the code of a complex application, it should be possible to clearly identify the scope, or context, associated with any given parameter specification. A natural solution for Java applications is to associate a parameter with the program scope where it is being used. The name should then reflect the parameter's position in the hierarchy, which includes:

  • Application
  • Package
  • Class

A parameter whose name is not prefixed by a context qualifier would be assumed to have global scope and refer to the higher level in the hierarchy (Application).

Parameters applying to a specific context (Package or Class) in the code can be constructed by the ordinary Java naming rules. For example,

solver.rk3.p1

and

solver.rk3.timestep.c1

would be the names used to identify a parameter p1 relative to the solver.rk3 package and a parameter c1 relative to the class timestep within the same package.

This naming strategy can be applied to any type of nested logical contexts, not just program unit contexts. This is helpful for maintaining an orderly namespace that avoids name clashes. A logical context that is not associated with a code context will be referred to as a topic. A topic may contain an arbitrary number of nested topics. At the higher level in the name hierarchy (Application), names have no qualifier. Therefore, there is no distinction at the Application level between program-scope parameters and topic parameters. A topic-scoped parameter has a qualified name with topic names running from left to right in descending order of containment, and separated by the "|" character. For example:

Literature|Classics|Odyssey|author

would be the name used to identify a parameter author within a library name context whose topics, from the most to the least general, are Literature, Classics, and Odyssey.

Parameters have an associated type. The following eight basic types correspond to Java built-in types and are also defined as simple types in the XML Schema standard:

  • Boolean
  • Byte
  • Short
  • Integer
  • Long
  • Float
  • Double
  • String

Parameters belonging to the eight basic types require only two character strings for their specification: one for name, and one for value representation. More complex constructs are necessary to define generic Java objects.

XML Schema for configuration files

XML is the natural choice for representing the hierarchical structure of the parameter space. An XML schema file is used to formally define the containment relationships between the program contexts (Package and Class) and the topic contexts. The schema also defines complex types that are necessary to define instances of arbitrary Java classes.

At the root of the proposed schema is the Application element, while the elements mapping the eight basic types (Boolean, String, and Number types) are at the bottom. Basic type parameters are defined within a program scope (Package or Class) or within a Topic by the Parameter complex type. This is illustrated in Figure 1.

The Parameter type

A basic type element surrounded by a Parameter specification defines a new object of the corresponding type within the current context. Package, Class, and Topic are valid contexts that may contain a Parameter specification. You can see from Figure 1 that a Parameter can contain one single element whose type can be any of the eight basic types, or the Reference type. The latter provides the ability to refer to a previously defined object by its name. The semantic of this specification is that a copy of the referenced object should be created, and assigned another name. This is useful when the same complex object (value) is used with different names in different parts of an application. Note that a referenced object can be of any Java type, not just one of the eight basic types.


Figure 1. The Parameter type
The Parameter type

The Object type

Object is a complex XML type defined by the schema. It creates a new instance of an externally specified Java class. This type actually defines the signature of a constructor for the specified class and a matching sequence of values. At parsing time, Java reflection features are exploited to determine the class constructor matching the specified signature and its invocation. As shown in Figure 2, the signature is specified as a sequence. The type of each element in the sequence can be either one of the eight basic types, or the Reference type.


Figure 2. The Object type
The Object type

The ClassMethod and InstanceMethod types

Invocation of a class constructor is not the only way to create instances of a Java class. Generally, object references can be obtained by the invocation of a class method or instance method. As for the Object type, the specification of a ClassMethod or InstanceMethod defines the signature of a valid method for the specified class and a matching sequence of values. However, not all methods return an object reference. The capability of defining method invocations in the configuration file is very important and should not be restricted to methods that return an object reference. For instance, the configuration file could specify the creation of an auxiliary Log object, which all the logging methods in the code will refer to by name. The Log object could require its own initialization, to be performed by an init() instance method. The complete initialization can be performed during the XML configuration file processing where an Object type definition for the Log object would be followed by an InstanceMethod definition, the latter causing the execution of the init() method on the previously defined Log object.

In summary, the semantic of a ClassMethod or InstanceMethod specification implies that the method be called during the configuration file parsing. A new name-object association will be introduced in the current naming context (Package, Class, or Topic) in case an object is returned by the method.

The Property type

Some Java classes are designed to retrieve run-time information from the Properties object returned by System.getProperties(). The specification of a Property element in the configuration file causes the setting of a new name-value property in the Properties table managed by the System class. The Property element definition is conveniently located within the program context (Package) of the Java classes that use them. However, this definition does not create a new named object in the program and topic namespaces. Changing the location of a Property element within a configuration file only affects the relative order by which the element is processed with respect to the other elements.

The Application element

Application is the root element of the schema. A Parameter or Object element defined directly within the Application element has a global scope; its specified name does not inherit any prefix qualification from the container element. The Application element, as shown in Figure 3, can also contain Property specifications and nested containers (Topic and Package).


Figure 3. The Application element
The Application element

The graphic symbols in Figure 3 indicate that an Application element contains an unbounded sequence of elements, the type of each contained element being Parameter, Object, Property, etc. The Topic and Package elements define the respective namespace hierarchies. These namespaces are distinguished by the use of a different separator character ("|" for topic names and "." for Package/Class names). The rules defining the allowed nesting of Topics, Package, and Class elements are illustrated in Figures 4 through 6. The complete schema is reproduced in Appendix A, while a sample configuration file is reproduced in Appendix B.

The Topic type

An element of the Topic type, as shown in the following figure, contains an unbounded sequence of elements. The fully qualified name of the Topic is constructed by adding the "|" separator and the Topic's name attribute to the fully qualified name of the parent context. (Application, which is the root, has a null qualified name.) Each contained element can be:

  • A nested Topic.
  • An element of Parameter, Object, ClassMethod, or InstanceMethod type, whose definition introduces a name-object association relative to the current Topic. The fully qualified name of the new object is constructed by adding the "|" separator and the object's name attribute to the fully qualified name of the current Topic.
  • An element of Property type introducing a name-object association within the System Properties hash table. The name of a property is not prefixed with the fully qualified name of the current Topic.

Figure 4. The Topic type
The Topic type

The Package type

The structure of a Package type, as shown in the following figure, is similar to the structure of a Topic type. The only difference is that a Package can contain nested Class elements in addition to Package elements. The fully qualified name of a Package is constructed by adding the "." separator and the Package's name attribute to the fully qualified name of the parent context. Each contained element can be:

  • A nested Package or Class.
  • An element of Parameter, Object, ClassMethod, or InstanceMethod type, whose definition introduces a name-object association relative to the current Package. The fully qualified name of the new object is constructed by adding the "." separator and the object's name attribute to the fully qualified name of the current Package.
  • An element of Property type introducing a name-object association within the System Properties hash table. The name of a property is not prefixed with the fully qualified name of the current Package.

Figure 5. The Package type
The Package type

The Class type

A Class type terminates the hierarchy of program scopes, as a Class cannot include a nested container element. Otherwise, a Class may include the same elements found in a Package and the same rules apply for the construction of qualified names.


Figure 6. The Class type
The Class type


Back to top


XMLConfigurator: A configuration utility class

The XMLConfigurator class is designed to provide a convenient interface for handling configuration files that conform to the XML schema reproduced in Appendix A. The class implements a static initialization method that parses one or more configuration files and builds the name-object associations into a TreeMap object. The initialization process may also introduce name-object associations in the System Properties hash table. Other static methods are provided that allow user classes to easily retrieve objects from the TreeMap.

The XMLConfigurator Java sample code, the schema, and a sample configuration file are available for download in Resources.

The class extends org.xml.sax.helpers.DefaultHandler and it implements a customized version of a few methods handling SAX parsing events. The most important ones are startDocument(), startElement(), and endElement(). Default implementations provided by DefaultHandler are retained for other event processing methods.

Static methods are also provided to retrieve parameter objects by their name:

public static Object getParameter     (String key){}
public static Object getClassParameter(Class  obj, String pname){}
public static Object getClassParameter(Object obj, String pname){}

The first method provides the maximum level of generality, requiring that the input string contain the fully qualified name of the parameter object to be returned. The following code fragment shows how to retrieve a string parameter pp relative to sub-topic subTopicB of a topic TopicA:

String p = (String)getParameter("TopicA|subTopicB|pp");

An integer parameter xx relative to the class myapp.util.UtilClass is obtained by:

int x = ((Integer)getParameter("myapp.util.UtilClass.xx")).intValue();

The second and third forms are provided to facilitate the retrieval of program-unit scoped parameters associated to a specific class. In both these forms, the second input parameter, a text string, contains the name of the parameter without qualification. The prefix that is needed to obtain the fully qualified parameter name is internally computed from the input Class object (second signature) or from an instance object of the class (third signature). The following is a sample usage of the third form by the instance method of a class retrieving a parameter yy of type Double:

double y = ((Double)getClassParameter(this, "yy")).doubleValue();

Other convenience methods are implemented for retrieving object instances of the numeric and boolean types by means of their fully qualified name.

public static boolean booleanValue(String key)
public static byte byteValue(String key) {}
public static double doubleValue(String key) {}
public static float floatValue(String key) {}
public static int intValue(String key) {}
public static long longValue(String key) {}
public static short shortValue(String key) {}

The main advantage is that catching exceptions and performing the conversion to the primitive type is done inside the method. Listing 1 shows the retrieval of a b Double whose fully qualified name is mycompany.myproduct.myguess:

Running the code with the sample initialization file

The XMLConfigurator class exploits a logging tool, Log4j, that can be downloaded from jakarta.apache.org (see Resources). Log4j itself can exploit an XML file for its own configuration, so it was interesting to put logging to work in the earliest phase of the XMLConfigurator class static initialization and ensure that there were no "chicken-egg" problems. Logging is a critical function that needs to be active during the initialization phase. Therefore, it seems to be a natural choice to have the logging itself started and initialized by XMLConfigurator. The sample code was developed using version 1.1.3 of Log4j.

There is one line of code that makes the class work only with the Apache XML parser. This can be changed, of course. However, the sample code was developed using version 1.4.3 of Xerces downloaded from xml.apache.org (see Resources).

Once both Log4j and Xerces are installed and the downloaded zip file (see Resources) has been expanded, you can examine the sample batch file run.bat under the path Resources\Export. This file should be changed to match the installation paths of Log4j and Xerces.

The sample code is also available in the form of a VisualAge for Java Version 4.0 repository. Use of the sample code in VisualAge requires that Log4j and Xerces also be available in that environment. This can be done by creating a project for each of these two packages and by importing the jar file into their respective project. (Caveat: you cannot have the IBM XML Parser for Java and Xerces 1.4.3 loaded in the workspace at the same time).

The example XML configuration file in Appendix B specifies the creation of a Locale object that is referenced in the successive instantiation of ResourceBundle objects: something definitely close to the power of an object-oriented scripting language!




Back to top


Download

DescriptionNameSizeDownload method
Sample code, XML Schema, and configuration filex-jschema-code.zip67KBHTTP
Information about download methods


Resources



About the author

Marcello Vitaletti works at IBM/Tivoli on software development projects. He cultivated an interest in programming and OO technology during his past experience as a researcher in the area of parallel numerical algorithms for engineering simulations. Contact Marcello at Marcello_Vitaletti@tivoli.com.




Rate this page


Please take a moment to complete this form to help us better serve you.



YesNoDon't know
 


 


12345
Not
useful
Extremely
useful
 


Share this....

digg Digg this story del.icio.us del.icio.us Slashdot Slashdot it!



Back to top