Configuration Variable Capture Using Java and XML
|
by Manoj Sharma Abstract This article shows how XML can be used to capture configuration variable information, to facilitate application systems integration and deployment. Taking a sample Risk Management System with several component applications that need to be integrated as an example, the article shows how XML is well suited to encapsulate the configuration metadata. The article proceeds to show how simple parsing Programs based on the SAX and DOM interface and Data Binding can be used, to extract the configuration information from the XML file, and populate the desired configuration files with the relevant configuration variables. It then goes on to show how Data Binding can be used to achieve programmatic configuration setup, and modification of the same XML file. The Data Binding Utility used to illustrate Data Binding and its application in configuration management is Java Architecture for XML Binding (JAXB). The article concludes by listing the advantages and disadvantages of the XML approach to Configuration Management, and its impact on the role of the Application Systems Integration Engineer. The simple prototype explained in the article can be used as the foundation of a more sophisticated Software Configuration Management tool and the target audience for this article would be anyone involved in, or with an interest, in Software Applications Systems Integration and Java/XML related development and testing. Introduction In this article, a case is made for the capture of configuration variable data using XML, to facilitate application Systems Integration and Deployment. An XML based approach for managing configuration variables, and the advantages and disadvantages of such an approach are discussed, with the help of simple example implementations using Java and XML. Application Integration Example For the purposes of this article, consider a Risk Management System, which needs to be setup at a client site. The System has several applications (implemented on different platforms and using different languages) that need to be integrated at the client site. Amongst other things, a host of configuration variables and a number of different startup scripts would need to be managed by the Integration Engineer. In most cases, the configuration variables and the startup scripts would have dependencies on each other, and the startup scripts may need to be run in a certain order to enable all the applications within the System to run in synchronization. Let us say our system has the following Applications - A Core Application
Engine (CAE), a Data Server (DS), an Aggregation Engine (AE), a Data Warehouse
(DW) and a few miscellaneous units like a Mapping Engine (ME) all of which
need to be setup and run in a specific order and configuration. Typically, the configuration variable information for these different applications would be stored in different configuration files depending on the platform they are being implemented on. For e.g. the .env and .cshrc files if using a UNIX System. In addition, there could be custom configuration files that are platform independent. Also depending on the platform there would be either batch/shell startup scripts for each of these applications. In most real world application deployment cases, considerable end user knowledge is required to setup and start a relatively complex System with multiple applications of this nature. It would entail setting up the relevant configuration variables in the appropriate configuration files and running the startup scripts in the right order. Further, any changes to be made in the configuration would require at the very least manual removal or modification of these configuration variables. More often than not there are dependencies within the applications and the modification of the configuration variables may not be an intuitive or simple task and will require a fairly in-depth knowledge of the system operation. We will demonstrate with the use of a few simple programs how XML can be used to capture the structure and content of the different configuration variables. Let us consider our sample Risk Management System once again. Take a few moments to study Table 1, which shows a listing of the Applications contained within and the configuration files, configuration variables and startup scripts that are needed by each of these Applications. Consider the following Application configuration variable information for our Risk Management System (RMS)
Table 1. Product and Configuration Variable Information for the Sample Risk Management System A study of Table 1. shows that there are dependencies between the configuration variables. For e.g.. PORT_1 needs to have the same value in three configuration files. Also, any changes in the configuration would necessitate the removal of configuration variables for e.g.. if the System were to be (temporarily) run without the Mapping Engine(ME) (i.e. not to be uninstalled, but simply not used in the new configuration) the configuration variables associated with it would need to be deleted. Any new Applications to be integrated would also entail at the very least addition of new configuration variables, which in turn may be linked to some of the existing variables. XML Configuration Files While any text file like a comma separated value (csv) file can be used to store the configuration information shown above, a brief look at the table above shows that it has a structure of its own. XML lends itself very well to structuring such content as configuration variable metadata. Further, XML is platform and language independent, and it is therefore possible using the XML file, to link together applications written in different languages for different platforms, using a Configuration Manager implemented using Java and XML technology. An interesting point regarding the use of XML configuration files is that they lend themselves well to remote configuration management. A DTD (configuration.dtd) is suggested in Listing 1. to capture the structure of the configuration information shown in Table 1. The "productlist" element corresponds to the list of products listed above and it can contain one or more products that correspond to the "product" elements. Each product has a configuration, which in turn can contain one or more configuration files corresponding to the "configfile" elements. Each configuration file can contain a set of configuration variables viz. the "cfgvariables" element, which in turn contains one or more configuration variables corresponding to the "variable" elements. Each variable has a variable name "vname" element and a variable value "vvalue" element. The "product", "configfile" and "variable" elements have attributes associated with them. These attributes uniquely identify each product, configuration file and variable respectively within their respective contexts. Listing 2. is an outline of the actual XML file (configuration.xml) that is generated based on configuration.dtd (Listing 1.)and encapsulates the information shown in Table 1. above. <!ELEMENT productlist (product)+> <!ELEMENT configuration (configfile)+> <!ELEMENT configfile (cfgvariables)> <!ELEMENT cfgvariables (variable)+> <!ELEMENT variable (vname,vvalue)> <!ELEMENT vname (#PCDATA)> Listing 1. The DTD (configuration.dtd) for the Configuration Variable data
<?xml version="1.0"
encoding="UTF-8" ?> <!DOCTYPE productlist
SYSTEM "configuration.dtd"> <productlist> <product pname="CoreApplicationEngine"> <configuration> <configfile fname="C:\\CoreApplicationEngine\\.caerc"> <cfgvariables> <variable number = "1"> <vname> CAE_HOME </vname> <vvalue> valuevariable11 </vvalue> </variable> <variable number = "2"> <vname> CAEM_PATH </vname> <vvalue> valuevariable12 </vvalue> </variable> </cfgvariables> </configfile> </configuration> </product> <product pname="DataWarehouse"> <configuration> <configfile fname="C:\\DW\\.cshrc"> <cfgvariables> <variable number = "1"> <vname> DW_DIR </vname> <vvalue> valuevariable21 </vvalue> </variable> </cfgvariables> </configfile> <configfile fname="C:\\DW\\.env"> <cfgvariables> <variable number = "1"> <vname> LD_LIBRARY_PATH </vname> <vvalue> valuevariable22 </vvalue> </variable> <variable number = "2"> ….. …. </variable> <variable number = "3"> …… …… </variable> </cfgvariables> </configfile> </configuration> </product> …. ….
</productlist>
Listing 2. The outline of the XML file (configuration.xml) based on configuration.dtd The configuration information of our System as shown in Table 1. is captured by configuration.xml shown in Listing 2. The dependency information between the configuration variables and the configuration files containing them is captured by the XML document, and all such checks are now made automatically within the XML file ,without any checking required by the Integration Engineer at the time of Integration. Configuration Management using DOM and SAX based Parsers Using the above configuration.xml file based on configuration.dtd we
proceed to demonstrate two simple applications BasicDOM.java (Outline
in Listing 3.), BasicSAX.java (Outline in Listing 4.) based on the DOM
and SAX interface using the Apache Xerces Java parser for XML. These programs
extracts the configuration variable information from the XML file shown
above, populates the desired configuration files with the relevant configuration
variables and their appropriate values and also makes a conditional check
for the PORT_1 variable and ensure that it has the required value. Using
a relatively simple set of programs as the one shown below, it is apparent
that the onus on the Integration Engineer to configure the System is considerably
reduced. All that he/she has to do, is modify a single XML file if the
need to change a "Variable" value or add a "Product"
arises.
public
class BasicDOM { //Global variable(s) RandomAccessFile File1;
public BasicDOM (String xmlFile){
DOMParser parser = new DOMParser();
// Parse the Document // and traverse the DOM try { parser.parse(xmlFile); Document document = parser.getDocument(); traverse (document); } catch …… //Error Handling logic } }
//
Traverse DOM Tree recursively private
void traverse (Node node) { try{ int
type = node.getNodeType();
//Start
of Core Element Node Processing Logic if
(type == Node.ELEMENT_NODE) { if
((node.getNodeName()).equals("cfgvariables")) { int length = (node.getChildNodes()).getLength(); String FileNameandPath = // Value of “fname” attribute of configfile
for (int i =0; i < length; i++) { // Check the value of the "number" attribute of the
"variable" node // If it equals "1" open the RandomAccessFile File1 = new RandomAccessFile(FileNameandPath,
"rw"); } //
Loop until all the "variable" child nodes have been written //
to the RandomAccessFile just
opened for
(int j =0; j < innerlength; j++) { //Store the values of the “vname” and “vvalue” //nodes
in a string “TheNodeValue” } //
Write to the specified configuration file
if (TheNodeName.equals("vname"))
{ File1.writeBytes(TheNodeValue); File1.writeBytes("="); } if (TheNodeName.equals("vvalue"))
{ File1.writeBytes(TheNodeValue); File1.writeBytes("\n"); } …… …… }//
End of Core Element Processing Logic
…… …… NodeList
children = node.getChildNodes(); if
(children != null) { for (int i =0; i< children.getLength();
i++) traverse (children.item(i)); } }
catch ( …… }
}//end
of traverse method
//
Main Method public static void main (String[] args) { BasicDOM
basicDOM = new BasicDOM (args[0]); } }
Listing 3. Outline of the BasicDOM program logic to parse configuration.xml based on the DOM Interface As seen in Listing 3. the recursive "traverse" method forms the core of the BasicDOM program. It contains all the relevant logic required to traverse the XML document and write the appropriate configuration information to the relevant configuration files.
public
class BasicSAX extends DefaultHandler
{ //Global Variables String GlobalFileName; RandomAccessFile File1; ……
/** The BasicSAX Constructor * @param xmlFile The URI of the configuration.xml
file to be parsed */
//Constructor public BasicSAX (String xmlFile) { //
Create a new SAX Parser SAXParser
parser = new SAXParser(); //Set
a Content Handler parser.setContentHandler(this); //Set
the Error Handler parser.setErrorHandler(this); //parse
the XML file try
{ parser.parse(xmlFile); }
catch //Error
Handling Logic …… } } // Event Handlers
/** Custom Implementation of the startElement
method provided by DefaultHandler * The GlobalFileName variable is initialised
with the value of the "fname" attribute * A RandomAccessFile is opened using the value
stored in the GlobalFileName variable */
// Start the Event Handler public void startElement(String uri, String
local, String qname, Attributes atts) { try
{ //
Logic introduced here to store the name of the Configuration File
Name and Path // and open
it when the first configuration variable (“number” = 1) to be written
is // encountered …… …… //Logic introduced here to capture the name
of the element for later processing ElementName = new String(local); }catch
(FileNotFoundException e) { …… } } /** Custom Implementation of the characters
method provided by DefaultHandler * The Configuration Files are populated with
the configuration variables indicated * by the content of the "vname" nodes
and their corresponding values indicated by * the values of the "vvalue" nodes */
// Character Scanner public void characters(char ch[], int start,
int length) { try
{ String
str = new String(ch, start, length); //Logic
introduced to write the configuration information to the Configuration
file opened in “StartElement” ……
} }catch (IOException e) { System.err.print(e); } }
/** This method provides a command line
entry to the BasicSAX program * @param args The URI of the XML file to be
parsed */
// Main Method public static void main (String[ ] args)
{
BasicSAX basicSAX = new BasicSAX (args[0]
); } } Listing 4. Outline of the BasicSAX program logic to parse configuration.xml based the SAX interface. The bulk of the Configuration Variable processing logic in BasicSAX.java is contained in the overridden "characters" event handler method wherein the node values corresponding to the "vname" and "vvalue" elements are written out to the appropriate configuration file. Data Binding Parsing XML documents at the level of the SAX or DOM interface rapidly tends to become a tedious chore. Using SAX or DOM parsers ,there is a level of indirection, which makes the code relatively complex. For example, it would be preferable to say Variable.getNumber() rather than saying something like Node.getAttributes().getNamedItem("number").getNodeValue(), which is usually the case in DOM. This is achieved by using Data Binding. XML Data Binding is the binding of XML documents to objects designed especially for the data in those documents. This allows applications (usually data-centric) to manipulate data that has been serialized as XML in a way that is more natural than using DOM or SAX. The Data Binding Utility we use is JAXB (Java Architecture for XML Binding), which provides a schema compiler and a run-time framework, which supports a two-way mapping between XML documents and Java Objects. We define a Binding Schema configuration.xjs (Listing 5.)using the configuration.dtd and compile this schema to generate schema classes. These classes form the basis of ConfigurationApp.java (Outline in Listing 6.), which is an application that does what BasicSAX/BasicDOM achieved albeit in a much simpler manner using Data Binding techniques.
<?xml version="1.0"
encoding="ISO-8859-1" ?>
<xml-java-binding-schema
version="1.0-ea"> <element name="productlist" type="class"
root="true"> <content> <element-ref name="product"/> </content> </element> <element name="product" type="class"> <content> <element-ref name="configuration"/> </content> <attribute name="pname"/> </element> <element name="configuration" type="class"> <content> <element-ref name="configfile"/> </content> </element> <element name="configfile"
type="class"> <content> <element-ref name="cfgvariables"/> </content> <attribute name="fname"/> </element> <element name="cfgvariables" type="class"> <content> <element-ref name="variable"/> </content> </element> <element name="variable" type="class"> <content> <element-ref name="vname"/> <element-ref name="vvalue"/> </content> <attribute name="number"/> </element> <element name="vname" type="value"/> <element name="vvalue" type="value"/> </xml-java-binding-schema>
Listing 5. The Binding Schema(configuration.xjs) used by the JAXB Schema compiler based on configuration.dtd By running the schema compiler supplied with JAXB on the above binding schema (configuration.xjs) and the DTD it is derived from (configuration.dtd) we get the source code for Java objects corresponding to the elements defined in the DTD viz. Productlist.java, Product.java, Configfile.java, Configuration.java, Cfgvariables.java and Variable.java. On compiling the source code we get the classes corresponding to these objects. A complete listing of the source code for these objects can be found in the in the XMLMeta.zip file in the Resources Section.
public
class ConfigurationApp {
//Global variable(s) static RandomAccessFile File1; public static Productlist firstOne = new Productlist(); public static Productlist secondOne = new Productlist(); /** This method builds the content trees corresponding
to each of the Productlist class * instances by unmarshalling the XML document
*/ public static void buildTrees() throws Exception
{
File
first = new File("configuration.xml"); FileInputStream
fln = new FileInputStream(first);
try
{ //Content
Tree corresponding to firstOne built by unmarshalling the XML document firstOne = Productlist.unmarshal(fln); } finally { fln.close(); } }
/** This method accesses and modifies the content
trees as desired */
public static void accessContent() throws
Exception {
List firstProductList = firstOne.getProduct(); for (ListIterator i = firstProductList.listIterator();
i.hasNext(); ) { Product product = (Product)i.next(); //Logic to drill down to the “vname”
and “vvalue” elements // and write the configuration variable information
stored in them to // to the Configuration File opened …… …… } }
/** This method ensures the content tree (after
modification) is valid with * respect to the DTD */
public static void validateTrees() throws Exception
{ firstOne.validate(); }
/** The content tree is marshalled to a
new XML document */
public static void marshalTrees() throws
Exception { …… …… }
public static void main(String args[ ] )
throws Exception {
buildTrees(); accessContent(); validateTrees(); marshalTrees();
} }
Listing 6. Outline of the ConfigurationApp program logic based on JAXB. The Java objects obtained by running the Schema Compiler form the basis for the ConfigurationApp program. The ConfigurationApp program contains the methods to
The accessContent() method contains the core configuration file management logic wherein the content tree is accessed (and modified as desired) and the information is used to populate the relevant Configuration files with the appropriate configuration variable names and values. Using our approach so far, the Integration Engineer no longer has to modify several configuration files on different platforms and/or client Machines. Instead he has to simply modify a single XML file, which supplies the different configuration variables, and their respective Files along with their Locations in the File System. The Application logic then calls upon the relevant parser or Data Binding Logic, which populates the appropriate files, with the relevant configuration variables. However, in the event that changes need to be made to the System (say a new product needs to be added or a Product name is changed) ,the above approach places the onus of creating and modifying the XML file to reflect these changes, on the Integration Engineer or the client. In a relatively simple example such as ours, it is not too difficult a task but in more complex Real Time systems, which need Application configuration more rapidly, and/or on a larger scale it would be preferable to achieve the same programmatically. We can use Data Binding to replace the tedious chore of manually building XML documents tag by tag as illustrated by ExtConfigurationApp.java (Outline in Listing 7). ExtConfigurationApp.java modifies the configuration.xml file by changing the name of the "DataWarehouse" product to "NewDataWarehouse" and adds a new product "TrialProduct" to the list of products, which are to be integrated automatically. The modified XML file new_configuration.xml is then used as the input for the configuration logic.
public
class ExtConfigurationApp {
//Global Variable(s) static RandomAccessFile File1; public static Productlist firstOne = new Productlist(); public static Productlist secondOne = new Productlist(); /** This method builds the content trees corresponding
to each of the Productlist class * instances by unmarshalling the XML document
for the first instance "firstOne" * and Instantiation in the case of the second
instance "secondOne" */ public static void buildTrees() throws Exception
{
File
first = new File("configuration.xml"); FileInputStream
fln = new FileInputStream(first);
try
{ //Content Tree corresponding to firstOne
built by unmarshalling the XML document firstOne = Productlist.unmarshal(fln); //Content Tree corresponding to secondOne
built by Instantiation List secondProductList = secondOne.getProduct(); Product newProduct = new Product(); newProduct.setPname("TrialProduct"); secondProductList.add(newProduct); …… }
finally { fln.close(); } }
/** Append content trees * The second content tree created by Initialization
is appended to the first one. */ public static void appendTrees() { List firstProductList = firstOne.getProduct(); List secondProductList = secondOne.getProduct(); firstProductList.addAll(secondProductList); }
/** This method accesses and modifies the content
trees as desired * It also populates the Configuration files
with the relevant Configuration variables */ public static void accessContent() throws Exception
{
List firstProductList = firstOne.getProduct(); for (ListIterator i = firstProductList.listIterator();
i.hasNext(); ) { Product product = (Product)i.next(); // Logic to rename the “DataWarehouse”
Product to “NewDataWarehouse” …… } for (ListIterator i = firstProductList.listIterator();
i.hasNext(); ) { Product product = (Product)i.next(); //Logic to drill down to the “vname”
and “vvalue” elements // and write the configuration variable information
stored in them to // to the Configuration File opened ……
} }
/** This method ensures the content trees (after
modification) are valid with * respect to the DTD */ public static void validateTrees() throws Exception
{ firstOne.validate(); secondOne.validate(); }
/** The content trees are marshalled to new
XML documents */ public static void marshalTrees() throws Exception
{ …… }
public static void main(String args[ ] ) throws
Exception {
buildTrees(); appendTrees(); accessContent(); validateTrees(); marshalTrees();
} }
While the overall structure of ExtConfigurationApp.java is very similar
to ConfigurationApp.java(Listing 6.), it contains an additional appendTrees()
method, which is called before the accessContent() method to incorporate
the additional Product "TrialProduct" which needs to be integrated
into the existing system. The accessContent() method also contains additional
logic to change the "DataWarehouse" Product Name to "NewDataWarehouse". Pros and Cons of the XML ApproachThis article has shown how configuration variable information can be captured in an XML file and how XML parsers based on the DOM and SAX interface and Data Binding can be used to integrate different Applications. Using this approach any changes in the setup configuration would automatically get picked up by the relevant configuration files. The primary benefits of the XML Approach are:
Limitations
ConclusionWith an ever-increasing number of software applications and legacy systems, it is impractical if not impossible to reengineer all of them to facilitate System Integration and Data Transfer. XML facilitates the decoupling of Application Logic from Integration Issues that might arise from such complex system integration needs. Using XML parsers and Data Binding Utilities, we have shown how it is possible for a client to configure a system with many different Applications, without requiring an in-depth knowledge of the system and its workings on his/her part. While this article is not intended to be exhaustive, it strives to show how a judicious use of XML and Java can help ease the burden on the Application Systems Integration Engineer. Resources
Manoj Sharma is a Software Architect with more than six years professional experience in the IT Industry in various development and consulting roles. He is also a regular contributor to Industry Magazines and Trade Journals and can be reached at sharma.manoj@rogers.com . |
|||||||||||||||||||