Configuration Service
Some Introduction goes here.
7.2 Building the Configuration Service
Typically applications read information from external sources either to configure themselves or use the data for lookup later. This information could be present in different repositories, local or remote and in different formats – XML, properties and so on. In the absence of a standard mechanism for reading this information, application developers tend to implement this in different ways. Hence there is a need for a standard mechanism to configure and lookup information. We will develop a Configuration Service in this section that will address the problem.
7.2.1 Configuration Service as a subset of Transformer Service
Configuration Service concepts can be best understood by treating it as a subset of Transformer Service. A Transformer Service transforms data from one format into another. For example, it could read data in XML format from a file, create equivalent Java objects, or vice versa. Figure 7.2 shows the conceptual view of the Transformer Service.

Figure 7.1 Conceptual representation of the transformer service
The Transformer Service has two kinds of inputs, one Data Input(Configurable Content), one or more Control Inputs and exactly one data output – the Configured Content.
The Transformer Service applies the control inputs on the data input to create the output data in the desired format. The output format can be anything: java objects, XML documents etc. For instance, if XSLT Transformation were to be represented by a Transformer Service, then the XML document is the data input, XSL Style sheet is the control input and the transformed XML is the output of this process. Another example is creating Java Objects from a XML file. The Transformer Service can also have the capability of doing the reverse transformation, i.e. to convert Java Objects into XML. Thus, transformation is a bi-directional process.
Now, it is easy to see Configuration Service as a specialized
subset of Transformer Service - something that does only the unidirectional
transformation. The source of the data (Configurable Content) can be a file or
it can be a URL stream from a remote secure server. Same thing goes for the
control input. In other words, Configuration Service is not tied to a specific
location.
In summary, conceptually, configuration service is a subset of transformer service. In the following sections we will use Castor XML framework to convert XML into Java objects. Then we will develop an Adapter API for the Configuration Service and build a reference implementation for the configuration service using Castor XML. Castor XML framework is a representative of other XML binding implementations and can be plugged in without affecting the application code. We will cover Castor XML in the next section.
Choices for implementing the configuration service – Castor, JAXB, Digester. Cover each of them
7.2.2 An overview of Castor
As mentioned earlier in Section 7.1.1, you can take different
approaches for XML to Java conversion. You can use low level APIs such as SAX
or DOM and do it yourself the hard way or you can one of the other high level
approaches like Castor, JAXB or Commons Digester to name a few. Since we will
develop the configuration service with Castor as the underlying implementation,
it is a good idea to go over Castor quickly.
In order to convert the XML into Java, Castor needs a XML file containing the data, optionally a mapping file to specify the mapping between the XML and the Java objects. Consider the XML file describing a customer and the java classes describing the Customer and Address class.
<customer>
<name>John Doe</name>
<age>28</age>
<address street="100 George St." city="Chicago" zip="60106" />
</customer>
public class Customer {
private String completeName;
private int ageInYears;
private Address address;
}
public class Address {
private String streetAddress;
private String cityName;
private int zipCode;
}
If the XML element names matched the java class names, Castor can figure out the mapping automatically. However in this case, the names differ and you need to provide Castor with a mapping file. The mapping file tells Castor, which XML element/attribute needs to be mapped to which Java attribute. Listing 7.1 shows the mapping for the scenario we’ve just laid out. For each class that needs to be populated with data from the XML, a <class> block has to be defined. In the listing 7.1 we defined two <class> blocks, one for Customer and the other for Address. Note that Customer is the top level element in the XML and a <map-to> element is specified. For other attributes, <bind-xml> is specified. For primitives, String and classes like Integer etc. a simple inline <bind-xml> suffices. For classes like Address, another <class> block is provided which further defines the mappings for attributes in that class.
Listing 7.1 Sample Castor Mapping file, mapping.xml
<!DOCTYPE databases PUBLIC
"-//EXOLAB/Castor Mapping DTD Version 1.0//EN"
"http://castor.exolab.org/mapping.dtd">
<mapping>
<description> Customer and Address Castor Mapping</description>
<class name="abcbank.service.samples.Customer"> |#1
<map-to xml="customer"/> |#1
<field name="completeName"> |#1
<bind-xml name="name"/> |#1
</field> |#1
<field name="ageInYears"> |#1
<bind-xml name="age"/> |#1
</field> |#1
<field name="address" |#1
type="abcbank.service.samples.Address"> |#1
<bind-xml name="address"/> |#1
</field> |#1
</class> |#1
<class name="abcbank.service.samples.Address"> |#2
<field name="streetAddress"> |#2
<bind-xml name="street" node="attribute"/> |#2
</field> |#2
<field name="cityName"> |#2
<bind-xml name="city" node="attribute"/> |#2
</field> |#2
<field name="zipCode"> |#2
<bind-xml name="zip" node="attribute"/> |#2
</field> |#2
</class> |#2
</mapping>
(annotation)< #1 Define mapping for Customer>
(annotation)< #2 Define mapping for Address>
Its time to look at the java code using Castor to convert the xml file, customer.xml into a Customer java object using the mapping.xml. Listing 7.2 shows how.
Listing 7.2 Using Castor to convert xml to java objects
InputStream xmlMapping = new FileInputStream(“mapping.xml”); |#1
Reader mappingReader = new InputStreamReader(xmlMapping); |#1
InputSource mappingSource = new InputSource(mappingReader); |#1
Mapping mapping = new Mapping(); |#1
mapping.loadMapping(mappingSource); |#1
Unmarshaller unmarshaller = new Unmarshaller(mapping); |#2
InputStream xmlData = new FileInputStream(“customer.xml”); |#3
Reader dataReader = new InputStreamReader(xmlData); |#3
InputSource dataSource = new InputSource(dataReader); |#3
Customer customer = (Customer) unmarshaller.unmarshal(dataSource);
(annotation)< #1 Loads the mapping.xml into Castor specific Mapping class>
(annotation)< #2 Creates a new Unmarshaller using the Mapping>
(annotation)< #3 Reads customer.xml as a data stream >
(annotation)< #4 Invokes unmarshal method to get the Java object>
7.2.3 Using the Configuration Service: A Client’s perspective
Now that we know what a
configuration service is and how to use Castor, let’s look at how a client
interacts with the generic Configuration Service. The IConfiguration interface represents the Configuration
Service from the client’s perspective. By invoking the configure method on this interface, the client
transforms the data from the source format into Java objects and retrieves it.
This interface could have multiple implementations based on where the data
comes from or the type of the data. The data could reside in a Directory server
organized as a tree, in a relational database, in an XML file or in a ASCII
file. Listing 7.3 shows how to use the
Configuration Service.
Listing 7.3 Using Configuration Service
IConfiguration conf = |#1
ConfigurationFactory.getConfiguration(“xml”); |#1
IData data = new Data(“DATA--001”, |#2
new FileInputStream(“customer.xml”); |#2
IControl ctl = new Control(“mapping”, “STREAM”, |#3
new FileInputStream(“control.xml”); |#3
IControl ctlArray = new IControl[1]; |#3
ctlArray[0] = ctl; |#3
Object configuredData = conf.configure(data, ctlArray); |#4
ConfigDataRepository.setConfigurationData(data.getId(), |#4
configuredData); |#4
(annotation)< #1 Gets instance of Configuration Service>
(annotation)< #2 Creates Data Input>
(annotation)< #3 Creates Control Inputs>
(annotation)< #4 Configures and Caches the java object for further use>
§ 1 Get an instance of the Configuration Service from the ConfigurationFactory class for the specified source data type. In this case, the source data type is XML. The ConfigurationFactory returns an instance of type IConfiguration using the standard component discovery mechanism.
§ 2 Construct a IData object with customer.xml file as the “source” and assign a unique identifier to the data (Line 02)
§ 3 Construct a IControl object with control.xml file as the “source”. The first parameter to the constructor is a unique identifier to the control and the second parameter is the type, which is implementation class specific. In this case the value of “STREAM” indicates that the control input is also stream based. Construct an array of Control Inputs with single element.
§ 4 Invoke the configure method on the IConfiguration service instance. This method returns a configured data object. Cache the configuration object into a ConfigDataRepository class based on the id obtained from the data. The cached data can be looked up later using the static getConfigurationData method on ConfigDataRepository and passing the unique identifier.
After looking at the code in listing 7.3, it is easy to see how Castor fits into the picture. An adapter class is created which implements the IConfiguration interface and uses Castor to perform the unmarshalling. It is also registered with the ConfigurationFactory. When the client asks for XML Configuration Service, the ConfigurationFactory returns the Castor Adapter implementation previously registered with it. The client remains unaware of the underlying implementation. At this point you might be wondering:
1. Why are Data and Control classes needed? After all, one can directly pass the InputStream to the Configuration Service
2. Even better - Specify the name of the file containing the data and control information.
To answer the second question, consider a
J2EE EAR based scenario. To be truly portable, you cannot create a stream from
a file name. You have to use the getResourceAsStream method on the ServletContext to access the file. By making the Configuration Service independent of
specifics of creating the stream, we have made it more usable.
To answer the first question, consider a
scenario where the data and controls are not stream based but repository based
(like RDBMS or LDAP) or of some other form. By abstracting the source of Data and Control in the getSource method, we have made the Configuration Service even more generic. For
e.g. the LDAP-Java transformer can internally cast the data and control sources
to appropriate type that it is alone aware of.
7.2.4 Castor based implementation for Configuration Service
Now, let us take the
Configuration Service one step further. Consider the case when Configuration
Service is used to load a lot of application data from XML files, which is
typically the case in enterprise applications. To do this from a servlet, you
will have to pass the names of each of the data and corresponding mapping files
(assuming Castor as your underlying implementation) as <init-param> in your web.xml file. The init params are
accessible in the servlet’s init method from the ServletConfig
object. Every time you want to configure new objects from XML, you will have to
add new init params. This is very troublesome. We have added another method to the IConfiguration interface to take some pain away. The new method has the signature public IContentIterator
getConfigurableContents(Idata data). This method aggregates the data and control information into a single
file. We call this a metadata file since it holds information about the data.
Listing 7.4 Sample Metadata file for Castor
<!DOCTYPE castor-metadata SYSTEM “castor-metadata.dtd”>
<content-descriptors>
<content-descriptor>
<data id="CustData" source="cust-data.xml" />
<control id="mapping" type="STREAM" source="cust-mapping.xml" />
</content-descriptor>
<content-descriptor>
<data id="AdminData" source="admin-data.xml" />
<control id="mapping" type="STREAM" source="admin-mapping.xml" />
</content-descriptor>
</content-descriptors>
The XML file shown in listing 7.4 is a collection of Content Descriptors. Each Content Descriptor “describes” the information needed to transform an individual XML into Java object. For Castor, the ContentDescriptor contains a data file and mapping file. Data information is shown by the <data> element and control information as shown by the <control> element. The <data> element has an id and a source attributes. The id uniquely identifies the data and the source provides the location of the data file. The <control> element has type and source attributes. The type attribute represents the “type” of the control information, for e.g. “STREAM”, if the information is being provided as an InputStream. The source attribute provides the location of the mapping XML file. You will immediately notice that the ContentDescriptor is implementation specific and hence the metadata file is also implementation specific. However, for every implementation of ContentDescriptor, two things are common. They have one data descriptor and a set of control descriptors (Notice that this was our definition of the Transformer Service). Accordingly we have an interface called IContentDescriptor, which should be implemented by every service provider to read the individual metadata entries from the metadata file. The following code shows the IContentDescriptor interface. Consider the following code snippet:
public interface IContentDescriptor {
public DataDescriptor getDataDescriptor();
public ControlDescriptor[] getControlDescriptors();
}
The IContentDescriptor interface definition also introduces two new
classes. DataDescriptor
and ControlDescriptor.
Figure 7.3 shows the relationship between all these classes. IContentIterator
and IContentDescriptor
classes need specific implementations. The rest of the classes are same for
every implementation.

Figure 7.3 Configurable Content Descriptor classes

Figure 7.4 Sequence of events in using the Configuration Service
Now let us get back to the getConfigurableContents method in IConfiguration interface mentioned earlier. We can consider the metadata file itself to be IData and create a Data as follows:
IData data = new Data(null, new FileInputStream("metadatafile.xml”));
For a specific implementation, the format of the Metadata file is predetermined, which means the control input is known. For Castor this translates to a known mapping.xml for the metadata file. With this information, it turns out that getConfigurableContents method for Castor actually invokes the configure method itself with a predefined mapping file. The method returns an Iterator for every ContentDescriptor. With the new getConfigurableContents method and the metadata file we have made life easier, when loading the configuration data. Figure 7.4 shows the sequence of events we have just explained.
Figure 7.5 shows the complete Configuration Service API class diagram. The highlighted classes show the Castor implementation classes.

Figure 7.5 Configuration Service API Class Diagram showing the relationship between the participating classes
7.2.4 Castor, JAXB and Digester: A comparison
By now, you are familiar with using Castor to implement Configuration Service. There are actually dozens of frameworks that do similar conversion from xml to java objects and vice versa but all of them take of the following two approaches
§ An “XML approach” where the area of concern is the XML document structure itself, i.e. the elements, attributes and the way the XML document is structured.
§ A “Data based approach” where the data contained in the document is given more importance than the document structure. Data Binding is of great use in this case.
Simply put, Data Binding deals with converting XML data to Java objects, known as “Unmarshalling” and converting Java objects to XML data, known as “Marshalling”. There are several tools that do this. Castor (http://www.castor.org), JAXB (Sun’s Java API for XML Binding) and the Jakarta Digester are by far, the most popular ones. By using these tools to marshal and unmarshal data, application developers don’t have to worry about writing code to read XML files, validate them against a given DTD (Document Type Definition) or XSD (XML Schema Document) and then having to change their code each time the structure of the document changes. Table 7.1 shows the pros and cons of each approach.
Table 7.1 Comparison of Data binding tools
|
JAXB |
Castor |
Digester |
|
Supports only generated bindings |
Supports both mapped bindings and generated bindings |
Supports binding based on patterns and rules |
|
Clients interact with interfaces through Factories which create appropriate instances of implementation classes |
Clients interact directly with generated implementation classes |
Clients interact directly with generated implementation classes |
|
Requires code generation at development time. Generated classes are tightly coupled to the Schema thereby providing less flexibility to change in document structure. |
Easier to integrate with existing classes. The mapping definition adjusts to minor changes in the document structure. |
Easy to integrate with existing classes. The xml mapping is under full control of the programmer. This comes at the cost of some extra java code that you have to write to map the xml and java using an intuitive API |
|
Best suited for applications where the structure of the documents have been established and are not liable to change often. Also suited in cases where there is little or no modification to be made to generated classes to work with existing code. |
Best suited for applications where complex classes have already been developed |
Provides extensive support for binding to interfaces, abstract classes that are part of the framework. |
|
Provides limited support for binding to multiple implementations of a base class. |
Support for binding to multiple implementations of a base class does not exist. |
Very good support for binding to specific implementations of a base class. |
|
Usage is quite complex due to repeated code generation in case of any changes to the document |
Very easy to use |
Usage is flexible but quite complex even for simple binding operations. |
There are several more XML based data
binding tools in the market. Among the more popular ones in the J2EE world are
JBind, Quick, and Zeus…. the list is long! A complete discussion of the pros
and cons of each approach is beyond the scope of this chapter. In section 7.5,
when we will develop Validator Service we need to be able to represent the
abstractions in terms of inheritance, abstract classes and interfaces. In that
case Commons Digester is the only feasible choice (see table 7.1).
More stuff here