Install this theme

Posts tagged: bytecode

Creating class proxies with Javassist

Javassist is a really easy to use bytecode manipulation library. 

Creating class proxies with Javassist is straight forward, just create an instance of ProxyFactory, a corresponding MethodHandler, that handles the method invocation and the class proxy itself.

Find below a small proxy creation example, that traces the execution of all class methods:

public class ProxyFactoryExample {

    public void foo() {
		System.out.println("Foo method executed.");
	}

	public void bar() {
		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			// ignore
		}

		System.out.println("Bar method executed.");
	}

	public static void main(String[] args) throws Throwable {
		ProxyFactory proxy = new ProxyFactory();
		proxy.setSuperclass(ProxyFactoryExample.class);
		proxy.setInterfaces(new Class[] { Serializable.class });
		proxy.setFilter(new MethodFilter() {
			@Override
			public boolean isHandled(Method m) {
				// skip finalize methods
				return !(m.getParameterTypes().length == 0 && m.getName()
						.equals("finalize"));
			}
		});

		MethodHandler tracingMethodHandler = new MethodHandler() {

			@Override
			public Object invoke(Object self, Method thisMethod,
					Method proceed, Object[] args) throws Throwable {

				long start = System.currentTimeMillis();
				try {

					return proceed.invoke(self, args);

				} finally {

					long end = System.currentTimeMillis();
					System.out.println("Execution time: " + (end - start)
							+ " ms, method: " + proceed);
				}

			}
		};

		ProxyFactoryExample obj = (ProxyFactoryExample) proxy.create(
				new Class[0], new Object[0], tracingMethodHandler);

		obj.foo();
		obj.bar();
	}
}
How to create Java POJOs dynamically at runtime with Javassist

Imagine your application have to create Java POJO instances dynamically at runtime from some external configuration. This task can be easily done using one of the  bytecode manipualtion library. This post demonstrates how this can be done using Javassist library.

Assume we have following configuration for the properties our dynamically created POJO should contain:

Map<String, Class<?>> props = new HashMap<String, Class<?>>();
props.put("foo", Integer.class);
props.put("bar", String.class);

Let’s write a PojoGenerator, that dynamically generates a Class object for the given class name and a map, containing required properties:

import java.io.Serializable;
import java.util.Map;
import java.util.Map.Entry;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import javassist.NotFoundException;

public class PojoGenerator {

	public static Class generate(String className, Map<String, Class<?>>  properties) throws NotFoundException,
			CannotCompileException {

		ClassPool pool = ClassPool.getDefault();
		CtClass cc = pool.makeClass(className);

		// add this to define a super class to extend
		// cc.setSuperclass(resolveCtClass(MySuperClass.class));

		// add this to define an interface to implement
		cc.addInterface(resolveCtClass(Serializable.class));

		for (Entry<String, Class<?>> entry : properties.entrySet()) {

			cc.addField(new CtField(resolveCtClass(entry.getValue()), entry.getKey(), cc));

			// add getter
			cc.addMethod(generateGetter(cc, entry.getKey(), entry.getValue()));

			// add setter
			cc.addMethod(generateSetter(cc, entry.getKey(), entry.getValue()));
		}

		return cc.toClass();
	}

	private static CtMethod generateGetter(CtClass declaringClass, String fieldName, Class fieldClass)
			throws CannotCompileException {

		String getterName = "get" + fieldName.substring(0, 1).toUpperCase()
				+ fieldName.substring(1);

		StringBuffer sb = new StringBuffer();
		sb.append("public ").append(fieldClass.getName()).append(" ")
				.append(getterName).append("(){").append("return this.")
				.append(fieldName).append(";").append("}");
		return CtMethod.make(sb.toString(), declaringClass);
	}

	private static CtMethod generateSetter(CtClass declaringClass, String fieldName, Class fieldClass)
			throws CannotCompileException {

		String setterName = "set" + fieldName.substring(0, 1).toUpperCase()
				+ fieldName.substring(1);

		StringBuffer sb = new StringBuffer();
		sb.append("public void ").append(setterName).append("(")
				.append(fieldClass.getName()).append(" ").append(fieldName)
				.append(")").append("{").append("this.").append(fieldName)
				.append("=").append(fieldName).append(";").append("}");
		return CtMethod.make(sb.toString(), declaringClass);
	}

	private static CtClass resolveCtClass(Class clazz) throws NotFoundException {
		ClassPool pool = ClassPool.getDefault();
		return pool.get(clazz.getName());
	}
}

That’s it!

Using PojoGenerator is quite simple. Let’s generate some POJO, output via reflection all its methods, set and then get some property:

public static void main(String[] args) throws Exception {

	Map<String, Class<?>> props = new HashMap<String, Class<?>>();
	props.put("foo", Integer.class);
	props.put("bar", String.class);

	Class<?> clazz = PojoGenerator.generate(
			"net.javaforge.blog.javassist.Pojo$Generated", props);

	Object obj = clazz.newInstance();

	System.out.println("Clazz: " + clazz);
	System.out.println("Object: " + obj);
	System.out.println("Serializable? " + (obj instanceof Serializable));

	for (final Method method : clazz.getDeclaredMethods()) {
		System.out.println(method);
	}

	// set property "bar"
	clazz.getMethod("setBar", String.class).invoke(obj, "Hello World!");

	// get property "bar"
	String result = (String) clazz.getMethod("getBar").invoke(obj);
	System.out.println("Value for bar: " + result);

}

Executing above will result in the following console output:

Clazz: class net.javaforge.blog.javassist.Pojo$Generated
Object: net.javaforge.blog.javassist.Pojo$Generated@55571e
Serializable? true
public void net.javaforge.blog.javassist.Pojo$Generated.setBar(java.lang.String)
public java.lang.String net.javaforge.blog.javassist.Pojo$Generated.getBar()
public java.lang.Integer net.javaforge.blog.javassist.Pojo$Generated.getFoo()
public void net.javaforge.blog.javassist.Pojo$Generated.setFoo(java.lang.Integer)
Value for bar: Hello World!