Tuesday, October 6, 2009

Combination of XML and Reflection API

I am writing this blog because I am fond of Reflection API. The XML off course is interesting in itself.
I am working in a product based company. There are many versions of our product. And each version has lots of changes for a particular feature.
To support a feature for every version, these days I am involved in developing general tools which support all versions. I am using lot of XML to develop
such tools.
I want to share an example which uses XML and Reflection to solve many problems, especially in developing general tool and unit testing.

I have a set of methods to be executed with various parameters. Here is the operation.xml.
<root>
  <oper method="add">
    <param>10</param>
    <param>20</param>
  </oper>

  <oper method="sub">
    <param>50</param>
    <param>10</param>
  </oper>

  <oper method="getName">
    <param>Example</param>
  </oper>
</root>

My methods are defined in Operation.java

public class Operation {
  public int add(String a, String b) {
    return Integer.parseInt(a) + Integer.parseInt(b);
  }

  public int sub(String a, String b) {
    return Integer.parseInt(a) - Integer.parseInt(b);
  }

  public String getName(String name) {
    return "Hello " + name;
  }
}

Now my Main class, which will use XML, calls methods of Operation.java accordingly.

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

public class Main {
  public static void main(String[] args) {
    Document document = null;
    SAXReader reader = new SAXReader();
    try {
      document = reader.read("operation.xml");
    } catch (DocumentException e) {
      e.printStackTrace();
    }

    Element root = document.getRootElement();
    Iterator operIterator = root.elementIterator("oper");
    Main main = new Main();
    while(operIterator.hasNext()) {
      main.processOper(operIterator.next());
    }
  }

  private void processOper(Element oper) {
    String method = oper.attributeValue("method");
    List param = new ArrayList();
    int i = 0;
    Iterator paramIterator = oper.elementIterator("param");
    while(paramIterator.hasNext()) {
      Element paramEle = paramIterator.next();
      param.add(paramEle.getText());
      i++;
    }
    System.out.println("Processing method: " + method);
    callMethod(method, param);
  }

  private void callMethod(String methodName, Listparam) {
    Operation operObj = new Operation();

    for (Method xMet : operObj.getClass().getDeclaredMethods()) {
      if (xMet.getName().equals(methodName)) {
        try {
          System.out.println(xMet.invoke(operObj, param.toArray()));
        } catch (IllegalAccessException ex) {
          System.out.println(ex);
        } catch (IllegalArgumentException ex) {
          System.out.println(ex);
        } catch (InvocationTargetException ex) {
          System.out.println(ex);
        }
        break;
      }
    }
  }
}

The Result is:
Processing method: add
30
Processing method: sub
40
Processing method: getName
Hello Example

So, suppose in future you want to add more cases in the xml say,
  <oper method="add">
    <param>30</param>
    <param>100</param>
  </oper>

Then you need not have to change any java class. Also, if you want to add an extra method in Operation.java then you don't have to change the main class. You just add respective tags in the XML file.