Dynamic Java Beans

(Available under http://blogs.sun.com/adventures/)

Introduction

In this article, I show how to dynamically create Java bean classes and load them using application defined custom class loaders. Java beans are useful in a number of application domains [1,2]. Traditionally, Java beans are created by first writing source code for the desired bean and then compiling it. This works best for applications in which the Java bean classes to be used are known before hand. There are some applications, however, that need to create Java beans dynamically, that is, they need to create ad-hoc beans as they run.


There are two tasks to the problem of creating dynamic Java beans at runtime, namely:

  1. Generating the byte code of the Java bean classes.
  2. Loading the generated byte code via specified class loaders.

In this exercise, we accomplish these tasks as follows:

  1. Specify a name for the Java bean to be created (“com.subhajit.synthetic.Point”).
  2. Specify the fields (names and types) that the Java bean must contain (“x” of type “int” and “y” of type “int”).
  3. Generate the byte codes of the Java bean class.
  4. Load these byte codes via a specified class loader.

Here is a snippet for code that shows how the above steps are performed:


Here, “BeanCreator” is the “com.subhajit.superbean.BeanCreator” class which is provided in the source code accompanying this article (see download link below).

Having gone over what we accomplish in the provided source code, let us delve deeper into steps 3 (generating byte code) and 4 (loading byte code).

Generating byte code

Byte code may be generated by several means. With the advent of the compiler API in Java 6 [3], one might generate source code for the intended Java bean, compile it, and obtain the bytes of the resulting class [4]. On the other hand, one might choose to use a byte code generation library (such as ASM [5] or Apache BCEL [6]) to generate byte code directly.

Recognizing that byte code may be generated in a number of different ways, we define an interface named “ClassGenerationStrategy” to hide the actual strategy used to generate the byte code:

You may define your own byte code generation strategy by implementing this interface, and construct an instance of the “BeanCreator” class. Alternatively, you may use the default implementation (“BcelClassGenerator”).

The “generateClassBytes” method of “BcelClassGenerator” shows how to use BCEL to generate byte code for Java beans. There are methods to declare the Java bean class, add a default constructor, add private fields for the desired bean properties and add “getter” and “setter” methods for each property. The code is somewhat involved if (like me) you are not familiar with the JVM's byte code format. If you really want to dig deeper into these steps, see [7,8].

Loading byte code

Generating byte code for a class is well and good, but the byte code by itself of little use unless it can be loaded as a Java class. After all, it is only after we have a Java class is it possible to do useful things like instantiating the class to create new objects, setting up instances with different values, followed by presumably doing something useful with these instances.

The “standard” way to convert a byte array to a Java class is via one of the overloads of the “defineClass” method in the “java.lang.ClassLoader” class. This final method accepts a class name (of the resulting class), a byte array, an offset (within the byte array) and length of bytes (within the byte array, starting at the offset), and returns a Java class (see the Java doc for the “java.lang.ClassLoader” class for details). The “standard” way to get access to the “defineClass” method is to implement a custom class loader class, override the “findClass” method, and call “defineClass” therefrom.

In this exercise, we use a different technique. We modify the user defined URLClassLoader (passed in as a constructor argument to the “BeanCreator” class) by adding a special URL to its list of URL's. Generated byte code that we wish to load with this class loader is placed in this special URL. Subsequent requests to this class loader to load the class causes it to go through its built in class finding and loading mechanism, which ultimately results in the class being found and loaded.

The “special” URL added to the user defined URLClassLoader uses a special protocol (“mem”) made up to facilitate URL's that refer to memory locations instead of directories and files on disk. Basically, URL's using the “mem” protocol are backed by a singleton instance of a concurrent hash map in memory, which maps String names to byte arrays. Information is saved into memory URL's just as it would be saved into any other URL using the following steps:

  1. Connect to the URL and get an URLConnection object.
  2. Setup the URLConnection to perform output.
  3. Obtain an OutputStream from the URLConnection object.
  4. Write the byte array to the OutputStream.
  5. Flush and close the OutputStream.

The following code snippet illustrates these steps:



Before delving into the details of implementing this scheme, let us see what this scheme buys us from the perspectives of code that produces (generates) byte code, and code that consumes (uses) the generated byte code. Code that produces byte code “writes” the generated bytes to a special URL. Code that consumes byte code simply “loads” generated classes using the specified URLClassLoader.

To accomplish this scheme, we need to lay some groundwork:

  1. Make the “mem” URL protocol “known” to the Java runtime. Note that some protocols like “file” and “http” are built into the standard libraries, allowing the creation of new URL's using these protocols using code such as:



Trying to create an URL using the “mem” protocol results in an error (a MalformedURLException is thrown):

    1. Custom URL protocols (such as the “mem” protocol) are supported by user defined “url stream handler factories”, and “registering” instances of these factories to handle specific custom protocols. Accordingly, we create a user defined class named “com.subhajit.memoryurl.MemoryURLStreamHandler” that implements the “java.net.URLStreamHandlerFactory” interface, and registering a default instance thereof using the code:

      The “createURLStreamHandler” method of the “URLStreamHandlerfactory” interface is implemented as follows:



The URLStreamHandler returned by this method is an extension of the “java.net.URLConnection” class named “com.subhajit.memoryurl.MemoryURLConnection”.

The “BeanCreator” class modifies the URLClassLoader passed in by the user by appending a memory URL to the end of the URL's managed by the URLClassLoader. This memory URL uses the following scheme to identify classes : “mem://prefix/fullyQualifiedClassNameInJVMFormat.class” (eg. “mem://0/com/subhajit/beans/Point.class”). The prefix is uniquely created per BeanCreator instance so that different URLClassLoaders created by the application cannot “see” classes created on memory URL's added to other user defined URLClassLoader instances.

The following code (of the “setClassBytesInMemoryURL” method of the “BeanCreator” class) shows how a producer of Java byte code “publishes” classes:



The final question that remains to be answered is how the “BeanCreator” class modifies the list of URL's managed by user defined URLClassLoader instances. Looking at the implementation of the URLClassLoader class, it is apparent that the field named “ucp” (of type “sun.misc.URLClassPath”) is responsible for managing an URLClassLoader's URL's. Using reflection, the “ucp” field's “addURL” method is invoked (with the memory URL) to accomplish the modification. The “updateUrlClassPath” method in “BeanCreator” shows these steps:





In general, using undocumented functionality, especially when it is not intended to be used, is a very bad idea. In this case, we use it simply because it is a means to an end in accomplishing a dynamic activity (loading classes).

The “BeanCreator” class's “defineClass” method defines bean classes with the following twists:

  1. If the class that is sought to be defined has already been defined, the previously defined class is returned.
  2. If any of the constituent members of a bean that is being defined belong to classes that are not visible to this BeanCreator's modified class loader, the classes are read as resources and redefined in this class loader so that it can load the generated class.

Examples of use

The “com.subhajit.superbean.test.TestSuite” class illustrates the use of this code generation scheme. Some examples of use are presented below.

The following code snippet shows how to create a “Point” class containing two integer properties named “x” and “y”, followed by creating a “Line” class containing two “Point” properties named “start” and “end”:



References

  1. http://java.sun.com/javase/technologies/desktop/javabeans/index.jsp

  2. http://en.wikipedia.org/wiki/JavaBeans

  3. http://java.sun.com/javase/6/docs/technotes/guides/javac/index.html

  4. http://www.juixe.com/techknow/index.php/2006/12/13/java-se-6-compiler-api/

  5. http://asm.objectweb.org/

  6. http://jakarta.apache.org/bcel/

  7. http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html

  8. http://en.wikipedia.org/wiki/JVM



Code download

http://blogs.sun.com/adventures/resource/beancreator.zip



Comments:

Hey, great stuff, I like your adevntures :)

Posted by mio on May 18, 2009 at 07:01 PM CDT #

Post a Comment:
  • HTML Syntax: NOT allowed

This blog copyright 2009 by Subhajit Dasgupta