REFLEX Logo

Support for evolution in the next generation of distributed information management systems

[ The REFLEX Proxy (RProxy) | Proxy Implementation | Running The Example Code | Download the RProxy and example code]

The REFLEX RProxy is an integral part of the implementation of RComponents. The RProxy forms the RComponent metainterface and specifically the RComponent dispatcher. This page shows how the RProxy can be used as a separate package to implement dynamic proxying for any arbitrary Java object outside of the rest of the RComponent API. Proxying is useful for such things as performance profiling, tracing, and debugging of objects as pre and post method code and exception traps can be dynamically placed around method invocations at runtime. The RProxy extends the support provided in the JDK 1.3 java.lang.reflect.Proxy API in that the RProxy also allows the trapping of unhandled exceptions raised within the proxied object. Such exceptions may be directed to the RProxy's InvocationHandler.trapException method. The API for the RProxy is largely carried over from that of the java.lang.reflect.Proxy package.

The REFLEX Proxy (RProxy)

The REFLEX proxy allows object's implementing a specific interface (in this example the interface Foo) to dynamically incorporate a trapping metalevel for the object. In our example we will proxy an Object implementing the Foo interface below.

public interface Foo {
    public void bar();
}
We can attach a proxy dynamically for the implementation of this interface at runtime. The proxy provides a metalevel whereby all invocations on the object's presented interface (i.e. invocations on the interface Foo) are dispatched through the proxy's invoke method.
public Object invoke(Object proxy, Method m, Object[] args)
	throws Throwable
{
        Object result;

	try {
	    System.out.println("***METALEVEL before method: "
	    	 + m.getName());
	    result = m.invoke(obj, args);
        } catch (InvocationTargetException e) {
	    throw e.getTargetException();
        } catch (Exception e) {
	    throw new RuntimeException
	    	("unexpected invocation exception: " +
				       e.getMessage());
	} finally {
	    System.out.println("***METALEVEL after method: "
	    	 + m.getName());
	}

	return result;
}
This allows code to be dynamically added before and after method implementations.

Proxy implementors using the RProxy are free to provide their own implementations for the invoke method as long as the proxy object implements the InvocationHandler interface (see the above example implementation of the invoke method from the file TProxy.java).

The proxy also traps any exceptions raised from the implementation of the proxied object's interface. Exceptions are directed to the trapException method of the InvocationHandler interface implemented within the proxy.

public void trapException(Method m, Exception e) {
    	System.out.println
    	  ("***METALEVEL - TRAPPED EXCEPTION : " + m);
    	System.out.println(e);
}
So in the implementation of the Foo interface below the proxy will trap the division by zero exception at the above trapException method implemented in the proxy. The code below provides the implementation of the Foo interface and is the Object that we are to proxy.
public class FooImpl implements Foo {
    public void bar() {
	System.out.println("CALLED bar()!");
	System.out.print("Doing something naughty");
	System.out.println(" to raise an exception 10/0: "
	 + 10/0);
    }
}
To invoke an object's methods via the proxy dynamically creating the metalevel (i.e. the invoke and trapException points) at runtime for the specified object, use the code,
Foo foo = (Foo) TProxy.newInstance(new FooImpl());
foo.bar();
whereas normally the following code is used to run the implementation of Foo without the proxy.
Foo foo = new FooImpl();
foo.bar();

Proxy Implementation

The RProxy package is implemented by examining the interfaces exposed by the proxied object and dynamically creating proxy classes (containing the dispatching metalevel) at runtime. This technique is commonly known as ByteCode manipulation. A proxy object will be created for each of the interfaces that the object implements. These proxies are defined within the classloader and so look like regular code and are therefore invisible to the programmer, requiring no special generation steps as is the case with traditional static proxying approaches.

Running the example code

To run the code use the command-line "java example.test" in the base directory where you unzipped the example. You will then see Foo's bar() method invoked both via the proxy and also normally.

In the case of executing bar() with the proxy pre and post execution of the method will be trapped by the proxy's invoke method allowing specified additional processing to be placed dynamically at runtime at these points. Proxies are also useful for debug tracing and program profiling as the execution path of the program may be easily logged by the proxy. When the code reaches an exception the proxy traps it and prints a message indicating the exception raised and in which delegated method.

Compare this to the non-proxy invocation of bar(). In this case no trapping of pre and post method execution or exceptions is available.

Download the RProxy and example code

Click here to download the RProxy package and example code described above.