Tuesday, January 29, 2013

Java assist to the rescue

Java assist to the rescue We use XStream (http://xstream.codehaus.org/) for serializing objects to xml. Our basic structure is:

 
 
 
  
  
 

We create annotations on our objects for the XStream converter, which worked fine. The problem we faced was how to give a proper name for the “object” node in the xml. Since we needed objects for the converter, we created a class GlobalInfo that accepted a base class of GenericObject. Each scenario that needed to transform an object to the xml, we inherited the GenericObject that had an internal field by the name that we wanted, this way the standard serializer used the field name for the xml. For example:
 public class TempObject extends GenericObject {
 private MyObject myName;
}
This would create the following xml: I looked for a generic way to create the “myName” node. Using XStream the standard way was to use annotations. The problem is that during run time you cannot change the annotation values. To overcome this I used java assit. With java assit I could create the class “TempObject” by code during runtime and then I would have a generic way to create the xml node without creating multiple classes during the coding phase. The idea is to create a class during runtime, and then assign the filed myName with the object that needs to be serialized. The following example will create the class:
@SuppressWarnings({ "rawtypes", "unchecked" })
public static GenericObject generate(Object source, String objectFieldName) {
 try {
  ClassPool pool = ClassPool.getDefault();
  // create the class
  String generatedClassName = String.format("TempObject.%s", objectFieldName);
  Class clazz = classFactory.get(objectFieldName);
  if (clazz == null) {
   CtClass transClass = null;
   try {
    transClass = pool.get(generatedClassName);
   } catch (NotFoundException e) {

    transClass = pool.makeClass(generatedClassName);
    CtClass superClass = pool.get("a.b.c.GenericObject");
    transClass.setSuperclass(superClass);

    CtField field = CtField.make(
      String.format("private Object %s;", objectFieldName), transClass);
    transClass.addField(field);
    transClass.addMethod(CtNewMethod.make(
      String.format("public void setObject (Object obj) { this.%s=obj; }",
        objectFieldName), transClass));

    clazz = transClass.toClass();
    classFactory.put(objectFieldName, clazz);
   }
  }

  Object obj = clazz.newInstance();
  Class[] formalParams = new Class[] { Object.class };
  Method meth = clazz.getDeclaredMethod("setObject", formalParams);
  meth.invoke(obj, source);
  // PropertyUtils.setProperty(obj, "obj", source);
  return (GenericObject) obj;

 } catch (Exception e) {
  throw new RuntimeException(e);

 }
}

No comments:

Post a Comment