Archive

Posts Tagged ‘dynamic’

A Framework Description for Dynamic Generation of XMLs

July 15th, 2008

Problem Statement: XML is an oft-used specification in enterprise-level application frameworks for specifying input/output sources. Many a times, there arises a need to extend or adorn the existing access end-points, for instance by placing a wrapper over the original frameworks or middle-ware. Such scenarios should be handled without the loss of generality and the key requirement is seamless matching with the input specification (XML) of the existing framework. In such cases, it becomes a tedious task for the writers of the wrapper to statically code the XMLs that are to be fed into the existing framework, while simultaneously conforming to the existing schema definitions (xsds / dtds).

Thus, a generic XML-generation framework is desirable that could return precise XML files by consuming a schema definition(specifying generic XML structure) and an input configuration (containing information specific to a particular XML-generation instance).

Input(s) to the framework: Schema Definition (xsd), Configuration (.xml), Input Parameters (HashMap ), Resource Specification [Specifies the system-specific entity for which the xml is to be generated].

Solution Design: The core idea is pretty straight-forward and comprises of the following steps: -

[1]. Create an in-memory parse-tree out of the given .xsd

[2]. Use the resource specification to refer the configuration and find out ‘the information’ specific to that particular XML-generation instance

[3]. Use the above ‘information’ to find out the scope of the current instance of xml generation from within the entire parse-tree

[4]. Adorn that scope with the input parameters and recursively apply ‘the information’ to generate the actual XML file

Description:

[1]. Create an in-memory parse-tree out of the given .xsd - For this, one can either go with any of the open-source xml-parsers, or write from scratch. Writing from scratch would generally require Element, Node, Attribute and ComplexType classes along with their respective handlers. Then, you would’ve a Schema Parser, which would utilize instances of those classes and generate the final tree object.

I won’t elaborate further on this here; if required, please drop me a note. In essence, the only contract to be followed is that the return object should contain a parse-tree structure.

[2, 3 & 4]. To understand these, let us delve a bit into the source.

Our generator would generally need to follow something similar to the following contract: -

/**
* IXMLGenerator represents the contract that all XML generators must adhere to
* in order to support the default logic of XML genertion. Default XML generation
* contract is based on the consumption of the starting XML element
* and the consolidatedMap of input and configuration parameters to
* generate the populated response XML for that particular element.
*
* @author sushain
*
* @version 1.0
*/

public interface IXMLGenerator
{ /**
* This method takes a resource name, corresponding method and a
* parameter map and builds a request xml
* which can be consumed by the a request handler.
*
* @param resource
* @param method
* @param paramMap
*
* @return Generated request xml
*/

public static String generateXml(final String resource,
final String method, final HashMap paramMap)
throws SchemaParsingException;
}

Here, generateXml would be the main method, inherently calling the tree-creation module, and effectively completing step (1). This method would utilize the input-parameter map (HashMap containing the name-value pairs for the elements to be populated in the xml), resource (the entity for which the xml is desired) and method (the type of xml desired {system-specific}) by referring the ‘Configuration’ in the following manner: -

Now, I’ve repeatedly referred to a ‘Configuration’ above, lets see how that actually looks like: -

<configuration>
<resource name=”<system-defined name>”>
<method name=”<resource access method / CRUD operation>”>

<param name = “aaa” value=”val1″ />

<param name = “bbb” value=”val2″ />

<param name = “ccc” value=”val3″ />

<!–param name = “<complex_aaa>” value=”<attr_val1>,<attr_val2>” /–>

<!–param name = “<complex_bbb>” value=”<attr_val3>,<attr_val4>” /–>

</method>

.
.
.

Some key-points about the above conf are: -

- Each resource-name has a list of parameters associated with it that would define what exactly its associated XML would consist of.

- In case of complex-type params, the value consists of a list of comma-separated attributes.

- Such a definition is readable, easily extensible and configurable.

We would also have a corresponding Config class to read the above XML configuration into a Document (org.w3c.dom.Document) and build a hashMap out of it.

The structure of such a class would be similar to: -

public class Configuration {

private File configFile;

private Document configDoc;

private String resultNodeName;

public Configuration(String configText){

this.configFile = new File (configText);

this.configDoc = getConfigDocument();
}

public Document getConfigDocument() {

Document XMLDoc = XMLutils.getXMLDocument(configFile);

if (XMLDoc == null) {

System.out.println(”XMLDoc null”);

System.exit(-1);
}

String query = “/configuration”;

NodeList nl = (NodeList) XMLutils.runXPathQuery(XMLDoc, query);

if(nl.getLength() !=1 ) {

return null;
}

Document responseObject = XMLutils.getNodeAsDocumentRoot(nl.item(0));

return (responseObject == null) ? null : responseObject;
}

public String getResultNodeName() {

return resultNodeName;
}

public HashMap getConfigurationMap (final String resourceName, final String methodName) {

HashMap configObject = new HashMap ();

Node temp = this.configDoc.getDocumentElement().getFirstChild();

while(temp != null) {

if(temp.getNodeType() == Node.ELEMENT_NODE) {

if (temp.getAttributes().item(0).getNodeValue().trim().equals(resourceName)) {

NodeList methodList = temp.getChildNodes();

int methodListIndex = 0;

while (methodListIndex < methodList.getLength()) {

if (methodList.item (methodListIndex).getAttributes() == null) {

methodListIndex = methodListIndex + 1;

continue;
}

if (methodList.item (methodListIndex).getAttributes().item(0).getNodeValue().trim().equals(methodName)) {

NodeList paramList = methodList.item(methodListIndex).getChildNodes();

int paramListIndex = 0;

while (paramListIndex < paramList.getLength()) {

if (paramList.item(paramListIndex).getAttributes() == null) {

paramListIndex = paramListIndex + 1;

continue;
}

configObject.put(paramList.item(paramListIndex).getAttributes().item(0).getNodeValue().trim(),
paramList.item(paramListIndex).getAttributes().item(1).getNodeValue().trim());

paramListIndex = paramListIndex + 1;
}
}

methodListIndex = methodListIndex + 1;
}
}
}

temp = temp.getNextSibling();
}

return configObject;
}

}

- Once such a setup is in place, the generateXml method would then build a ‘Consolidated Map’ by appending ParamMap to HashMap.

- It would then read the xsd definition and get the in-memory parse tree ready using the module discussed in step (1).

- After that the actual xml generation logic would be invoked, passing the above built Consolidated Map and the resource specifiction.

- The generation logic would iterate through the tree, until it locates the resource spec

- It would then start building the XML, simultaneously referring the Consolidated Map and populating elements whenever it finds a corresponding entry in the Map. The population logic would be written in the corresponding handlers for ’simple’ as well as ‘complex’ type nodes.

This would be something similar to: -

if(attList.size() > 0) {

XSDAttribute att = (XSDAttribute)attList.get(0);

if (consolidatedMap.containsKey(getName())) {

attributeString = consolidatedMap.get(getName()).toString();

StringTokenizer stok = new StringTokenizer (attributeString, “,”);

while (stok.hasMoreTokens()) {

buffer.append(”<”);

buffer.append (getName() + ” “);

buffer.append (att.getName());

buffer.append(”=”);

buffer.append(”"” + stok.nextToken() + “”");

buffer.append(”>”);

if (consolidatedMap.containsKey(att.getName())) {

buffer.append (consolidatedMap.get(att.getName()));
}

buffer.append(”

buffer.append(getName());

buffer.append(”>”);
}

}

return buffer;

Finally, this XML would be returned to the caller and subsequently consumed by the original framework. For extension purposes, the only file to be modified later would be the configuration, which could be easily achieved due to the uniformity and structured nature of the definition. Due to a generic contract specification that only mandates an input parameter-map with name-value pairs of population-parameters, such a framework could be utilized in applications with a consuming UI-front as well as headless command-based apps. We thus saw how an effective configuration-design simplifies a seemingly difficult extension & integration problem.

If you have any suggestions, please drop a note / post.

Share/Save/Bookmark

SociBook del.icio.us Digg Facebook Google Yahoo Buzz StumbleUpon

Programming , , , ,