Java News Brief
                                        October 2000 Issue

Headlines:
Java Technical Insight of the Month  -   "Building Classes Dynamically with Java2"
Visit The Java News Brief Archive for past issues of the JNB
OCI Educational Services

Java Technical Insight of the Month  -  Building Classes Dynamically with Java2
By Jean-Cédric Desrochers, Software Engineer, Object Computing, Inc. (OCI)

One of the new additions to the Java 2 Standard Edition (J2SE) version 1.3 release earlier this year is the Dynamic Proxy API.  This mechanism allows programs to dynamically create bytecode that represents classes and create new instances of those classes.  A dynamic proxy class is a class that implements a list of interfaces specified at runtime.  A method invocation through an interface implemented by the dynamic proxy will be encoded and dispatched to another object through a uniform interface.  This article will briefly go into the specifications of the new Dynamic Proxy API and will explain how to create a proxy class and where it should be used.

The Dynamic Proxy API

The concept of dynamically creating classes was first introduced in an article of "The Swing Connection" to create event listeners on the fly with a GenericListener class that creates the bytecode of the class.  This functionality was reworked and brought into the J2SE 1.3. Modification was needed first to extend the concept to more than just event listeners.  Also, modification was needed due to the security features of J2SE 1.3 that prevent applications from creating new classes in the core Java packages.

UML Class Diagram of the Dynamic Proxy API
Fig 1 - UML Class Diagram of the Dynamic Proxy API

The API to create proxy classes can be found in the java.lang.reflect package and consists of only two classes and one interface.

Creation of a Proxy Class

To create a proxy class we need to use the getProxyClass() static method of the Proxy class.  This method takes as an input argument a ClassLoader to use to load the created bytecode into the Java Virtual Machine (JVM) and an array of Class objects that represent the interfaces implemented by this new proxy class. There are some restrictions on the parameters that can be passed to the method:

The result of the getProxyClass() method call is a Class object that extends the Proxy class and implements all the interfaces passed in.  The proxy class created is a public final class and it’s unqualified name starts with $Proxy.  If one of the interfaces implemented by the proxy class is not public, the proxy class will be generated in the same package as that interface.  The created class will be cached, so that it can be reused to create additional objects that support the same list of interfaces.

Creation of a Proxy Instance

Now that we have a class that represents our proxy, let’s create an instance of that proxy class. Each proxy class is generated with one public constructor that takes as argument an InvocationHandler object.  This object is the focal point of all the method invocations that are made on the proxy instance.  To create an instance we can use reflection or the convenience static method newProxyInstance() of the Proxy class.  This convenience method takes as an argument a ClassLoader, the list of interfaces to implement and the InvocationHandler of the proxy instance. It will create the new proxy class if needed and will return a new proxy instance.

Method Invocation on a Proxy Instance

Now that we have an instance of our proxy, let’s call methods on it.  Every method call on the proxy will be encoded and dispatched by the proxy itself to the InvocationHandler of the proxy.  The invoke() method of the handler will be called with the following arguments:

The return value of the invoke() method will become the return value of the proxy instance.  If an exception is thrown by the invoke() method, it will also be thrown by the proxy instance.  If the type of the exception is not declared as a checked exception by the interface method or if it’s not an unchecked RuntimeException, the proxy instance will throw an UndeclaredThrowableException.

Example

To show the dynamic proxy in action, we’ll look at a simple Swing application that allows the user to change the title of the frame.  The demo consists of a text field in which they can enter the new title name of the frame and two buttons: one to set the title to the typed text and one to clear the text of the text field.  We’ll use a proxy instance to manage all the events that could occur in the frame. Here's a list of the listeners used and their functionality in the demo:

The first step is to create a proxy class that implements all the listeners that we need.  Next, we create an instance of that proxy class.  We perform those two operations by using the Proxy.newProxyInstance() method:

    ...
    // Create the dynamic proxy instance
    aListenerProxy = (Proxy) Proxy.newProxyInstance(
                    getClass().getClassLoader(),
                    new Class[] { WindowListener.class,
                                  ActionListener.class,
                                  FocusListener.class,
                                  DocumentListener.class },
                    this);
    ...

In order to create the proxy instance, we need to pass an InvocationHandler object that will receive all the method invocations.  For convenience in our example, the demo class implements that interface.  Once we have the proxy instance, we can then add the proxy as the listener to our Swing objects:

    ...
    // Adding the proxy on the Swing components
    addWindowListener((WindowListener) aListenerProxy);
    btnOk.addActionListener((ActionListener) aListenerProxy);
    btnClear.addActionListener((ActionListener) aListenerProxy);
    txfName.addFocusListener((FocusListener) aListenerProxy);
    txfName.getDocument().addDocumentListener((DocumentListener) aListenerProxy);
    ...

Now that everything is hooked up, we just put the code in the invoke() method to implement the behavior we want. Here's the source code of the invoke() method:

    /**
     * Implementation of the InvocationHandler interface.
     */
    public Object invoke(Object aProxy, Method aMethod, Object[] someArguments) {
        // If a method invocation was done on the WindowListener interface
        if (aMethod.getDeclaringClass() == WindowListener.class) {
            // Close the demo and exit the VM
            if (aMethod.getName().equals("windowClosing")) {
                System.exit(0);
            }
    
        // If a method invocation was done on the ActionListener interface
        } else if (aMethod.getDeclaringClass() == ActionListener.class) {
            // Perform the action of the buttons
            ActionEvent anEvent = (ActionEvent) someArguments[0];
            if (anEvent.getSource() == btnOk) {
                setTitle(txfName.getText());
            } else {
                txfName.setText("");
            }
    
        // If a method invocation was done on the FocusListener interface
        } else if (aMethod.getDeclaringClass() == FocusListener.class) {
            // Set the background color in yellow if the text field has the focus
            if (aMethod.getName().equals("focusGained")) {
                txfName.setBackground(Color.yellow);
            } else {
                txfName.setBackground(Color.white);
            }
    
        // If a method invocation was done on the DocumentListener interface
        } else if (aMethod.getDeclaringClass() == DocumentListener.class) {
            // Enable / Disable the clear button
            if (txfName.getText().equals("")) {
                btnClear.setEnabled(false);
            } else {
                btnClear.setEnabled(true);
            }
        }
    
        return null;
    }

Why use dynamic classes?

This concept of dynamically creating classes that implement a list of interfaces is increasingly popular but why and where should we use it? Definitively, creating Class objects at runtime and using reflection to dispatch the method invocation is more CPU intensive and slower; we can’t deny it. But on the other hand, the runtime generation of the class provides an alternative to reading .class files from the file system or from a remote server.  It also provides the possibility of having a single point of method dispatching, the InvocationHandler interface.

The first benefit can be used in an applet that utilizes RMI where the time to download the classes is critical.  To talk to the server, the client needs the stubs of all the services it will use and the interfaces that are implemented by those stubs.  With the Dynamic Proxy API, we can create a proxy class that implements the interfaces of all the services used by the applet and the InvocationHandler can serialize and send the method invocations to the server side.  That way the applet doesn’t need to load the stub.

The second benefit of the Dynamic Proxy can be explained with a Swing application.  To add behavior to GUI Swing controls, we add listeners (ActionListener, KeyListener, DocumentListener, ...) as anonymous inner classes.  The problem is that those inner classes tend to consume more class file space than they should.  Most of the time those anonymous inner classes delegate their execution to another method. One way to avoid those inner classes is to use a dynamic proxy class.  This approach simulates the inner classes that are there only to act as a pass-through to the method that contains the code to be executed. It avoids downloading all the anonymous classes in the case of an applet. It also allows tools that load classes dynamically (like IDEs) to generate event binding for listener interfaces that are not known until runtime.

Learning More

This article provided an overview of the Dynamic Proxy API provided with the J2SE version 1.3.  There are some restrictions and issues that where not covered for simplicity.  To learn more about them, see the J2SE reflection documentation.  The source code for the example in this article can be found here: ProxyDemo.java.  Please note that J2SE version 1.3 is required to run the demo.


We welcome any comments and feedback on this article.  Comments and suggestions may be sent to
jnb@ociweb.com.  


OCI Educational Services               

OCI has one of the most comprehensive OO training curricula in the country.  Clients contract group training that is either taught at the client site or at the OCI Education Center in St. Louis, MO.  We encourage you to check our Object-Oriented Technology Curriculum show below:   (click on course titles for online descriptions)  

For more information or to register, email training@ociweb.com.


Object Computing, Inc. is a Sun Authorized Java Center in St. Louis and a Member of the Object Management Group, OMG.  OCI specializes in distributed computing using object-oriented and web-enabled technologies and provides Consulting, Education, and Product Development services to clients nation-wide.  For more information contact us at 314-579-0066 (St. Louis), 480-752-0042 (Tempe) or email info@ociweb.com.

Click here OCI CAREER OPPORTUNITIES or email hr@ociweb.com.

The Java News Brief is a monthly newsletter.  The purpose and intent of this publication is to advance Java, provide technical value, and to announce available OCI Java services.  If you would prefer to not receive this newsletter or would like to subscribe please
click here

Copyright (c) 2000.  Object Computing, Inc.   All rights reserved.   Java and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries.  Object Computing, Inc. is independent of Sun Microsystems, Inc.