Dynamic Java beans



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





 

What did you think of this article?




Trackbacks
  • No trackbacks exist for this post.
Comments
  • No comments exist for this post.
Leave a comment

Submitted comments are subject to moderation before being displayed.

 Enter the above security code (required)

 Name

 Email (will not be published)

 Website

Your comment is 0 characters limited to 3000 characters.