Skip to main content

Build your own interface - dynamic code generation

Posted by emcmanus on October 18, 2006 at 6:26 AM PDT

Sometimes static code isn't enough and you need to build code
dynamically, at run time. That's usually a hefty proposition,
but if the code you need to build is just an interface, it's
actually relatively simple. Here are some of the reasons you
might want to build interfaces at run time and how you might go
about it.

Static and dynamic code

The first thing to remember before launching into dynamic code
generation is that it is nearly always a much better idea to
use static code generation if you can. Static code
generation just means writing code as .java files
and compiling them with the Java compiler in the usual way.
Dynamic code generation means creating code at run time that
didn't exist when you compiled your program.
Dynamically-created code can often only be run using
reflection.

To make the distinction clearer, suppose you have an interface
that looks like this:

public interface SomeInterface {
    public void doSomething();
}

If SomeInterface is known at compile time, then
you can invoke its doSomething() just by writing Java
code:

    SomeInterface object = ...whatever...;
    object.doSomething();

Suppose, though, that your application creates the
SomeInterface class at run time. It might do this
by writing the code above to a file, invoking the Java compiler
on that file, then loading the resultant class. Then your
application couldn't contain the code above because
SomeInterface didn't exist when your application
was compiled. So you would have to use reflection instead:

    Class<?> someInterfaceClass =
    Class.forName("SomeInterface", false, someClassLoader);
    ...get an instance of someInterfaceClass somehow...
    Method doSomethingMethod = someInterfaceClass.getMethod("doSomething");
    doSomethingMethod.invoke(instance);

I'm deliberately omitting a lot of detail for the moment. The
point is just that the code to invoke the method looks
completely different, and a lot more complicated. Hence my
advice to avoid dynamic code generation if you can.

The class java.lang.reflect.Proxy

The Java SE platform already contains some support for dynamic
code generation, notably in the class href="http://download.java.net/jdk6/doc/api/java/lang/reflect/Proxy.html">java.lang.reflect.Proxy.
This class allows you to take any interface and produce a class
that implements that interface, where calling any method results in
a call to an href="http://download.java.net/jdk6/doc/api/java/lang/reflect/InvocationHandler.html#invoke(java.lang.Object,%20java.lang.reflect.Method,%20java.lang.Object[])">
invoke
method that you supply. So for example you could use this to
produce a dummy implementation of any interface, where every
method just returns null, or where every method throws an
exception. Or you could perform some action in every method
before calling the same method in a "real" implementation of the
same interface, such as logging the method call, performing
security checks, or setting a href="http://download.java.net/jdk6/doc/api/java/lang/ThreadLocal.html">ThreadLocal.

The way Proxy works might be clearer with an
example. Suppose you do something like this:

MBeanServer proxy =
    (MBeanServer) Proxy.newProxyInstance(...details omitted..., myHandler);
proxy.registerMBean(someObject, someObjectName);

Then the call to proxy.registerMBean will result
in a call like this:

myHandler.invoke(proxy, method, new Object[] {someObject, someObjectName});

where method is a href="http://download.java.net/jdk6/doc/api/java/lang/reflect/Method.html">java.lang.reflect.Method
representing href="http://download.java.net/jdk6/doc/api/javax/management/MBeanServer.html#registerMBean(java.lang.Object,%20javax.management.ObjectName)">MBeanServer.registerMBean.
This is where you could execute arbitrary code that throws an
exception or logs the call or whatever.

Dynamically generating an interface suddenly becomes more
interesting in conjunction with this Proxy class.
Once we have the interface, we can use Proxy to get
an implementation of it.

Dynamic code generation and the JMX API

The JMX API uses reflection heavily. If we can dynamically
generate an interface, then we can give that interface to a JMX
method that will use reflection on it. Here are a couple of
ways we can exploit this.

Recall that a Standard MBean is a Java class that implements an
interface called the MBean interface. That interface
determines which methods in the class are management
methods
. When you register an instance of the class with the
JMX Agent (MBean Server), these methods can be called by a
management client such as href="http://java.sun.com/developer/technicalArticles/J2SE/jconsole.html">JConsole.

Here's an example of an MBean class and its accompanying
interface:

public class Cache implements CacheMBean {
    public int getSize() {
    return size;
    }

    public void setSize(int size) {
    this.size = size;
    }

    public int getUsed() {
    return used;
    }
    ...
}

public interface CacheMBean {
    public int getSize();
    public void setSize(int size);
    public int getUsed();
}

It can be troublesome to maintain two different source files,
for the public class Cache and the public interface
CacheMBean. We often hear people ask for the ability
to specify the managed methods with an annotation instead. In
other words, to get rid of CacheMBean and instead
write Cache like this:

public class Cache {
    @Managed
    public int getSize() {
    return size;
    }

    @Managed
    public void setSize(int size) {
    this.size = size;
    }

    @Managed
    public int getUsed() {
    return used;
    }
    ...
}

How might we implement that?

Model MBeans

One possibility is to use Model MBeans. Model MBeans
allow you to link MBean attributes and operations to arbitrary
public methods of any class. We could use reflection to pick
out the methods with the @Managed annotation, and
give these methods to the href="http://download.java.net/jdk6/doc/api/javax/management/modelmbean/RequiredModelMBean.html">RequiredModelMBean
class to make an MBean out of them.

That's fine as far as it goes, but suppose we want to add a
further feature where you can add @Managed to a
field and have that field be exposed as a read-only MBean
attribute. In other words, we'd replace the
getUsed method above with this:

public class Cache {
    @Managed
    private int used;

    @Managed
    public int getSize() {
    return size;
    }

    @Managed
    public void setSize(int size) {
    this.size = size;
    }
    ...
}

RequiredModelMBean allows you to expose public methods of a
class, but not fields (public or otherwise). So it is not
powerful enough to handle this extension.

Annotation processors

A second possibility is to use an
annotation processor
to generate an MBean wrapper from the
Cache class. This wrapper might look something
like this:

public interface Cache$WrapperMBean {
    public int getSize();
    public void setSize(int size);
    public int getUsed();
}

public class Cache$Wrapper implements Cache$WrapperMBean {
    private final Cache wrapped;
    private final Field usedField;

    public Cache$Wrapper(Cache wrapped) {
    this.wrapped = wrapped;
    try {
        usedField = Cache.class.getDeclaredField("used");
        usedField.setAccessible(true);
    } catch (Exception e) {
        throw new IllegalArgumentException("Field 'used' inaccessible", e);
    }
    }

    public int getSize() {
    return wrapped.getSize();
    }

    public void setSize(int size) {
    wrapped.setSize(size);
    }

    public int getUsed() {
    try {
        return usedField.getInt(wrapped);
    } catch (IllegalAccessException e) {
        throw new IllegalArgumentException("Field 'used' inaccessible", e);
    }
    }
}

We use reflection to bypass the usual Java language checks that
would prevent the generated Cache$Wrapper class
from accessing the private used field of the
Cache class. This is not very good practice in
general but is harmless in this particular case because we have
explicitly annotated the field to make it happen, and we are
only reading the field.

We're still talking about static code generation here, because
annotation processors run at compile time.
Cache$Wrapper.java and
Cache$WrapperMBean.java would be generated by the Java
compiler via the annotation processor, and immediately compiled.

Dynamically generating the MBean interface

A third possibility, then, is to generate the equivalent of the
Cache$Wrapper and Cache$WrapperMBean
classes at run time. That is, when somebody asks to register
the Cache class, we use reflection to discover the
methods and fields that have the @Managed
annotation, and we generate the Cache$WrapperMBean
interface that we saw above. Once we have this interface, we
can use java.lang.reflect.Proxy to produce the equivalent of the
Cache$Wrapper class.

So how do we go about generating the interface dynamically?
There are at least three ways.

  1. Write the Java source code to a file and invoke the Java
    compiler on that file. You might do this using href="http://download.java.net/jdk6/doc/api/java/lang/Runtime.html#exec(java.lang.String)">Runtime.exec,
    for example. As of Java SE 6, you can use the href="http://download.java.net/jdk6/doc/api/javax/tools/package-summary.html">javax.tools
    package to invoke the compiler. Strictly speaking though,
    you're not absolutely guaranteed to find a Java compiler in
    either case.

  2. Use a standard byte-code generation library, such as href="http://jakarta.apache.org/bcel/">Apache BCEL. Pulling
    in the whole of BCEL just to generate an interface seems a bit
    like overkill, though.

  3. Generate the byte code yourself. This is usually a major
    undertaking. But in fact most of the difficulty in generating
    byte code is involved in generating executable code. Interfaces
    don't contain any executable code, so it is not unreasonably
    difficult to generate the byte code for an interface. Plus you
    get lots of extra Geek Cred, so that's what I'll show here.

Generating the byte code for an interface

(If you're not interested in the details of byte code
generation, you'll still need to know how to load the generated
class, which is the subject of the next
section
.)

Before generating byte code, you do need to have an
understanding of the class file format. The reference here is
href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html">Chapter
4 of the Java VM Spec. (There's an href="http://java.sun.com/docs/books/vmspec/2nd-edition/ClassFileFormat-Java5.pdf">update
containing the changes needed for Generics, but that doesn't
concern us here.)

The main difficulty is that the class file is in two main
parts, the constant pool, and "everything else".
Everything else here is the class properties (such as its name and
superclass) and the contained fields and methods. Every time
there is a class name, string, or other constant in this second
part, it is actually a reference to the constant pool, as
illustrated below.

The class file is in two main parts

So this means that when generating a class file, you are
actually generating two blocks of data in parallel, the constant
pool and the rest. Typically this means generating everything
in a buffer while recording the constant pool entries, then
writing the constant pool followed by the buffer.

Without further ado, let's look at the outline of the
InterfaceBuilder class.

public class InterfaceBuilder {
    public static byte[] buildInterface(String name, XMethod[] methods) {...}
}

The idea here is that you call
InterfaceBuilder.buildInterface with the name of
the interface you want to fabricate and the list of methods you
want it to contain. We're using our own class
XMethod here rather than
java.lang.reflect.Method, because there is no way
to fabricate a Method other than to reflect on a
class that contains it. In the case of @Managed
applied to the used field above, we want to
generate a getUsed() method even though no class
contains that method.

The XMethod class is just a tedious wrapper for a
bunch of values. Here's the outline.

/**
* Object encapsulating the same information as a Method but that we can
* instantiate explicitly.
*/
public class XMethod {
    public XMethod(Method m) {
        this(m.getName(), m.getParameterTypes(), m.getReturnType());
    }

    public XMethod(String name, Class<?>[] paramTypes, Class<?> returnType) {
        this.name = name;
        this.paramTypes = paramTypes;
        this.returnType = returnType;
    }

    public String getName() {
        return name;
    }

    public Class<?>[] getParameterTypes() {
        return paramTypes.clone();
    }

    public Class<?> getReturnType() {
        return returnType;
    }

    // ...define equals, hashCode, toString here...

    private final String name;
    private final Class<?>[] paramTypes;
    private final Class<?> returnType;
}

Now let's look at InterfaceBuilder in more detail.
When you call its buildInterface method, it will
construct a private instance of InterfaceBuilder that
it will use to store the constant pool. We can expand the outline
as follows.

public class InterfaceBuilder {
    private static final int CONSTANT_Utf8 = 1, CONSTANT_Class = 7;

    /**
     * Return the byte code for an interface called {@code name} that
     * contains the given {@code methods}.  Every method in the generated
     * interface will be declared to throw {@link Exception}.
     */
    public static byte[] buildInterface(String name, XMethod[] methods) {
        try {
            return new InterfaceBuilder().build(name, methods);
        } catch (IOException e) {
            // we're only writing arrays, so this "can't happen"
            throw new RuntimeException(e);
        }
    }

    private InterfaceBuilder() {
    }

    private byte[] build(String name, XMethod[] methods) throws IOException {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(bout);

        dout.writeInt(0xcafebabe);     // u4 magic
        dout.writeShort(0);            // u2 minor_version
        dout.writeShort(45);           // u2 major_version (Java 1.0.2)

        byte[] afterConstantPool = buildAfterConstantPool(name, methods);

        writeConstantPool(dout);
        dout.write(afterConstantPool);
    return bout.toByteArray();
    }

    // ...

    private final Map<List<?>, Integer> poolMap =
            new LinkedHashMap<List<?>, Integer>();
    private int poolIndex = 1;
}

This corresponds to the approach described above, where we
generate everything into a buffer (afterConstantPool)
while recording the constants, then write the constant pool, then
write the buffer.

I've somewhat capriciously chosen to write a class file in the
format from Java 1.0.2, way back when the Java platform didn't
even have reflection. In fact nothing we are going to use has
changed in the class file format since then. But you might prefer
to use the format from Java 1.n here, in which case you
should use 44+n instead of 45 here, for example 48 for Java
1.4.

Now let's take a look at buildAfterConstantPool,
which will generate the meat of the class file, and in particular
the methods. You might want to have a look at that href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html">chapter
from the JVM spec in another window if you really want to
understand what's going on here.

    private byte[] buildAfterConstantPool(String name, XMethod[] methods)
    throws IOException {
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        DataOutputStream dout = new DataOutputStream(bout);

        dout.writeShort(Modifier.PUBLIC|Modifier.INTERFACE|Modifier.ABSTRACT);
                                         // u2 access_flags
        dout.writeShort(classConstant(name));
                                         // u2 this_class
        dout.writeShort(classConstant(Object.class.getName()));
                                         // u2 super_class
        dout.writeShort(0);              // u2 interfaces_count
        dout.writeShort(0);              // u2 fields_count

        dout.writeShort(methods.length); // u2 methods_count
        for (int i = 0; i < methods.length; i++) {
            dout.writeShort(Modifier.PUBLIC|Modifier.ABSTRACT);
                                              // u2 access_flags
            dout.writeShort(stringConstant(methods[i].getName()));
                                              // u2 name_index
            dout.writeShort(stringConstant(methodDescriptor(methods[i])));
                                              // u2 descriptor_index
            dout.writeShort(1);               // u2 attributes_count
            dout.writeShort(stringConstant("Exceptions"));
                                                    // u2 attribute_name_index
            dout.writeInt(4);                       // u4 attribute_length:
            dout.writeShort(1);                     // (u2 number_of_exceptions
            dout.writeShort(classConstant(Exception.class.getName()));
                                                    //  + u2 exception_index)
        }

        dout.writeShort(0);              // u2 attributes_count (for class)
        return bout.toByteArray();
    }

We generate the equivalent of a
throws Exception clause in each method. This
is because if a Proxy handler throws a checked
exception that is not declared in the throws
clause, the exception will be wrapped in an href="http://download.java.net/jdk6/doc/api/java/lang/reflect/UndeclaredThrowableException.html">UndeclaredThrowableException.
With a bit more effort we could add the list of exceptions to
XMethod and generate it here, but it doesn't seem
to be worth that effort.

This method requires a couple of other methods to generate the
cryptic codes that are used for method signatures in the class
file format.

    private String methodDescriptor(XMethod method) {
        StringBuilder sb = new StringBuilder("(");
        for (Class<?> param : method.getParameterTypes())
            sb.append(classCode(param));
        sb.append(")").append(classCode(method.getReturnType()));
        return sb.toString();
    }

    private String classCode(Class<?> c) {
        if (c == void.class)
            return "V";
        Class<?> arrayClass = Array.newInstance(c, 0).getClass();
        return arrayClass.getName().substring(1).replace('.', '/');
    }

The Array.newInstance business is a simple if
extravagant way to get the right href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#14152">type
code for every Java type, primitive or otherwise. Due
presumably to a historical oversight, the JVM's internal codes
leaked out through href="http://download.java.net/jdk6/doc/api/java/lang/Class.html#getName()">Class.getName() for array classes.
This is usually a pain, but here it proves useful!

Finally, we need to manage the constant pool. There are href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#20080">11
different types of constant and the obvious approach would be
to define an interface or abstract class Constant
with 11 subclasses. However, it turns out that nearly all these
types of constant are only needed when generating code. Since
we're just generating an interface, we only need two of the types,
for href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#7963">strings
and href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#1221">class
names. So I've used a Lisp-like approach where each constant
is represented by a List. The first element of the List is the
constant tag, and the rest of the List is the data specific to
that tag. A switch statement does the right thing for each tag.
(This is href="http://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.html">not
very Objectly Correct. Sue me.)

The constants are stored in a Map so that if the same constant
is needed more than once we can reuse it. The use of List for
each constant gives us the right behaviour for lookups. A href="http://download.java.net/jdk6/doc/api/java/util/LinkedHashMap.html">LinkedHashMap
ensures that the constants come out in the right order when we
iterate through the Map to write the constant pool.

    private int stringConstant(String s) {
        return constant(CONSTANT_Utf8, s);
    }

    private int classConstant(String s) {
        int classNameIndex = stringConstant(s.replace('.', '/'));
        return constant(CONSTANT_Class, classNameIndex);
    }

    private int constant(Object... data) {
        List<?> dataList = Arrays.asList(data);
        if (poolMap.containsKey(dataList))
            return poolMap.get(dataList);
        poolMap.put(dataList, poolIndex);
        return poolIndex++;
    }

    private void writeConstantPool(DataOutputStream dout) throws IOException {
        dout.writeShort(poolIndex);
        int i = 1;
        for (List<?> data : poolMap.keySet()) {
            assert(poolMap.get(data).equals(i++));
            int tag = (Integer) data.get(0);
            dout.writeByte(tag);          // u1 tag
            switch (tag) {
                case CONSTANT_Utf8:
                    dout.writeUTF((String) data.get(1));
                    break;                // u2 length + u1 bytes[length]
                case CONSTANT_Class:
                    dout.writeShort((Integer) data.get(1));
                    break;                // u2 name_index
                default:
                    throw new AssertionError();
            }
        }
    }

    private final Map<List<?>, Integer> poolMap =
            new LinkedHashMap<List<?>, Integer>();
    private int poolIndex = 1;

Loading the generated interface

We now have a byte array containing a class file. How do we go
from that to an actual href="http://download.java.net/jdk6/doc/api/java/lang/Class.html">java.lang.Class?
This problem is the same whether the byte array came from the
code above, or was generated by invoking the compiler, or was
created by Apache BCEL.

We're going to need a href="http://download.java.net/jdk6/doc/api/java/lang/ClassLoader.html">ClassLoader.
The job of a ClassLoader is to take a class name (such as
com.example.Cache$WrapperMBean) and produce the
corresponding Class. It can do this by forwarding
the request to another ClassLoader or by constructing the Class
itself using the href="http://download.java.net/jdk6/doc/api/java/lang/ClassLoader.html#defineClass(java.lang.String,%20byte[],%20int,%20int)">defineClass
method.

The simplest way to manage this is to write the byte array to a
file with the appropriate name, say
/tmp/code/com/example/Cache$WrapperMBean.class, and
use a href="http://download.java.net/jdk6/doc/api/java/net/URLClassLoader.html">URLCLassLoader
that can find the file. Something like this:

    OutputStream out =
    new FileOutputStream("/tmp/code/com/example/Cache$WrapperMBean");
    out.write(bytes);
    out.close();

    URL codeDir = new File("/tmp/code").toURI().toURL();
    ClassLoader loader = new URLClassLoader(new URL[] {codeDir});
    Class<?> c =
    Class.forName("com.example.Cache$WrapperMBean", false, loader);

But in fact it is not much harder to create your own
ClassLoader that directly fabricates and loads the byte code
without requiring any files:

public class InterfaceClassLoader extends ClassLoader {
    public InterfaceClassLoader(ClassLoader parent) {
        super(parent);
    }
   
    public Class<?> findOrBuildInterface(String name, XMethod[] methods) {
        Class<?> c;
        c = findLoadedClass(name);
        if (c != null)
            return c;
        byte[] classBytes = InterfaceBuilder.buildInterface(name, methods);
        return defineClass(name, classBytes, 0, classBytes.length);
    }
}

MBeans from annotations

We now have nearly everything we need in order to be able to
build MBeans from @Managed annotations. A reminder
that given a class like this...

public class Cache {
    @Managed
    public int getSize() {
    return size;
    }

    @Managed
    public void setSize(int size) {
    this.size = size;
    }

    @Managed
    public int getUsed() {
    return used;
    }
    ...
}

...we want to build a Standard MBean interface like this...

public interface Cache$WrapperMBean {
    public int getSize();
    public void setSize(int size);
    public int getUsed();
}

...and implement that interface to make a Standard MBean that
we can register in the MBean Server. Rather than generating a
class Cache$Wrapper that implements
Cache$WrapperMBean, following the Standard MBean
conventions, we're going to create a java.lang.reflect.Proxy.
We can't make the class name of the proxy be
Cache$Wrapper, but the class href="http://download.java.net/jdk6/doc/api/javax/management/StandardMBean.html">javax.management.StandardMBean
exists for when the class name and the interface name don't
match.

The class MBeanBuilder can be used to make MBeans
in this way. The usage is similar to this:

    ClassLoader thisLoader = this.getClass().getClassLoader();
    MBeanBuilder builder = new MBeanBuilder(thisLoader);
    ...
    Object cacheMBean = builder.buildMBean(cache);
    ObjectName cacheMBeanName = ...;
    mbeanServer.registerMBean(cacheMBean, cacheMBeanName);

For best results, when a class constructs an
MBeanBuilder it should supply its own
ClassLoader as argument, as shown here.

Here's what MBeanBuilder looks like:

public class MBeanBuilder {
    private final InterfaceClassLoader loader;

    public MBeanBuilder(ClassLoader parentLoader) {
        loader = new InterfaceClassLoader(parentLoader);
    }

    public StandardMBean buildMBean(Object x) {
        Class<?> c = x.getClass();
        Class<?> mbeanInterface = makeInterface(c);
        InvocationHandler handler = new MBeanInvocationHandler(x);
        return makeStandardMBean(mbeanInterface, handler);
    }

    private static <T> StandardMBean makeStandardMBean(Class<T> intf,
                                                       InvocationHandler handler) {
        Object proxy =
                Proxy.newProxyInstance(intf.getClassLoader(),
                                       new Class<?>[] {intf},
                                       handler);
        T impl = intf.cast(proxy);
        try {
            return new StandardMBean(impl, intf);
        } catch (NotCompliantMBeanException e) {
            throw new IllegalArgumentException(e);
        }
    }

    private Class makeInterface(Class implClass) {
        String interfaceName = implClass.getName() + "$WrapperMBean";
        try {
            return Class.forName(interfaceName, false, loader);
        } catch (ClassNotFoundException e) {
            // OK, we'll build it
        }
        Set<XMethod> methodSet = new LinkedHashSet<XMethod>();
        for (Method m : implClass.getMethods()) {
            if (m.isAnnotationPresent(Managed.class))
                methodSet.add(new XMethod(m));
        }
        if (methodSet.isEmpty()) {
            throw new IllegalArgumentException("Class has no @Managed methods: "
                    + implClass);
        }
        XMethod[] methods = methodSet.toArray(new XMethod[0]);
        return loader.findOrBuildInterface(interfaceName, methods);
    }
}

The first time it sees a given class, such as
com.example.Cache, it will generate an interface,
com.example.Cache$WrapperMBean. Every other time, it
will reuse the previously-generated interface.

The @Managed annotation is defined like this:

@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD)
public @interface Managed {
}

In this version, we don't allow @Managed on
fields. That's certainly possible, but would complicate the code
considerably.

The MBeanInvocationHandler used in the constructed
Proxy looks like this:

public class MBeanInvocationHandler implements InvocationHandler {
    public MBeanInvocationHandler(Object wrapped) {
        this.wrapped = wrapped;
    }

    public Object invoke(Object proxy, Method method, Object[] args)
    throws Throwable {
        Class<?> wrappedClass = wrapped.getClass();
        Method methodInWrapped =
            wrappedClass.getMethod(method.getName(), method.getParameterTypes());
        try {
            return methodInWrapped.invoke(wrapped, args);
        } catch (InvocationTargetException e) {
            throw e.getCause();
        }
    }
   
    private final Object wrapped;
}

That's everything you need! I've collected the various classes
into a zip file
for convenience.

MXBean mappings

Another possible application of dynamic interface generation
appears in Java SE 6 with the addition of href="http://java.sun.com/developer/technicalArticles/J2SE/mxbeans/">MXBeans
to the platform. MXBeans map arbitrary Java types to a fixed
set of types, the so-called Open Types defined in href="http://download.java.net/jdk6/doc/api/javax/management/openmbean/package-summary.html">javax.management.openmbean.
But the MXBean framework operates on MXBean interfaces,
not on individual types. It sometimes happens that you need to
find out the mapping for a certain type.

If you know the type at compile time, you can just put it in an
interface, create an MXBean that implements that interface.
Suppose you want to know the mapping for href="http://download.java.net/jdk6/doc/api/java/lang/management/MemoryUsage.html">java.lang.management.MemoryUsage,
for example. Then you can create an interface like this...

public interface MemoryUsageTestMXBean {
    public MemoryUsage getMemoryUsage();
    public void setMemoryUsage(MemoryUsage x);
}

...and implement it like this...

public class MemoryUsageTest implements MemoryUsageTestMXBean {
    volatile MemoryUsage memoryUsage;

    public MemoryUsage getMemoryUsage() {
    return memoryUsage;
    }

    public void setMemoryUsage(MemoryUsage x) {
    memoryUsage = x;
    }
}

Then you can make an MXBean like this...

MemoryUsageTest memoryUsageTest = new MemoryUsageTest();
StandardMBean mxbean =
    new StandardMBean(memoryUsageTest, MemoryUsageTestMXBean.class, true);

...and use it like this...

// Discover the OpenType that MemoryUsage is mapped to
MBeanAttributeInfo ai = mxbean.getMBeanInfo().getAttributes()[0];
assert(ai.getName().equals("MemoryUsage");
OpenType<?> memoryUsageOpenType = (OpenType<?>)
    ai.getDescriptor().getFieldValue("openType");

// Convert a MemoryUsage into a value of this Open Type
MemoryUsage mu = ...something...;
memoryUsageTest.memoryUsage = mu;
Object openValue = mxbean.getAttribute("MemoryUsage");

// Convert a value of the Open Type back into a MemoryUsage
mxbean.setAttribute(new Attribute("MemoryUsage", openValue));
mu = memoryUsageTest.memoryUsage;

We can make this work for an arbitrary type discovered at run
time, by generating the equivalent of the
MemoryUsageTestMXBean for the type in question. The
code below shows this.

public class MXBeanMapper {
    private final StandardMBean mxbean;
    private final MXBeanInvocationHandler handler;

    public MXBeanMapper(Class<?> originalType) {
        InterfaceClassLoader loader =
                new InterfaceClassLoader(originalType.getClassLoader());
        XMethod getter = new XMethod("getX", new Class<?>[0], originalType);
        XMethod setter = new XMethod("setX", new Class<?>[] {originalType},
                                     void.class);
        Class<?> mxbeanInterface =
            loader.findOrBuildInterface("X", new XMethod[] {getter, setter});

        handler = new MXBeanInvocationHandler();
        mxbean = makeMXBean(mxbeanInterface, handler);
    }

    private static <T> StandardMBean makeMXBean(Class<T> intf,
                                                InvocationHandler handler) {
        Object proxy =
                Proxy.newProxyInstance(intf.getClassLoader(),
                                       new Class<?>[] {intf},
                                       handler);
        T impl = intf.cast(proxy);
        return new StandardMBean(impl, intf, true);
    }

    public OpenType<?> getOpenType() {
        MBeanAttributeInfo ai = mxbean.getMBeanInfo().getAttributes()[0];
        assert(ai.getName().equals("X"));
        return (OpenType<?>) ai.getDescriptor().getFieldValue("openType");
    }

    public synchronized Object toOpenValue(Object javaValue) {
        handler.javaValue = javaValue;
        try {
            return mxbean.getAttribute("X");
        } catch (Exception e) {
            throw new IllegalArgumentException(e);
        }
    }

    public synchronized Object fromOpenValue(Object openValue) {
        try {
            mxbean.setAttribute(new Attribute("X", openValue));
        } catch (Exception e) {
            throw new IllegalArgumentException(e);
        }
        return handler.javaValue;
    }

    private static class MXBeanInvocationHandler implements InvocationHandler {
        volatile Object javaValue;

        public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable {
            if (method.getName().equals("getX"))
                return javaValue;
            else if (method.getName().equals("setX")) {
                javaValue = args[0];
                return null;
            } else
                throw new AssertionError("Bad method name " + method.getName());
        }
    }
}

Conclusions

Generating interfaces is possible and allows us to solve some
interesting problems. This is especially true for APIs like the
JMX API that use reflection heavily.

The source code for the classes above is in this zip file, with the exception of
MXBeanMapper.java
, which I separated because it
requires the Java SE 6 platform.

Related Topics >>