Implementing an Interface Using a Proxy

   

Java 1.3 added the capability to dynamically create a proxy class at runtime that implements one or more interfaces. The Proxy class and the InvocationHandler interface were added to the java.lang.reflect package to support this functionality. With this capability, you can create a proxy that delegates the method calls associated with the interfaces it implements to a class that implements InvocationHandler. This interface defines a single method:

 public Object invoke(Object proxy, Method method, Object[] args)   throws Throwable 

You obtain a proxy to work with an InvocationHandler using a static method of the Proxy class:

 public static Object newProxyInstance(ClassLoader loader, Class[] interfaces,     InvocationHandler h) throws IllegalArgumentException 

The object returned by newProxyInstance can then be cast to any of the interfaces assigned as part of its creation. Calls are made to the proxy and then redirected to its InvocationHandler.

The subject of dynamic proxies is somewhat complex, so let's look at an example implementation. A natural use for a proxy is when you need to wrap one or more existing classes with some additional functionality. A typical approach for doing this is to first define an interface that includes each method of interest in the class to be wrapped. You can then declare a wrapper class that implements this same interface and delegates the execution of each method call to an instance of the original class. Any additional functionality can be executed by each wrapper class method either before or after the delegate's method is called. By coding to an interface, you can substitute the wrapper class for the original wherever the new functionality is needed.

This delegation approach works well, but it can be tedious to implement all the wrapper methods if an interface is substantial in size . The problem grows if the new functionality applies to multiple classes that are not related through inheritance or interface; this leads to the creation of many wrapper classes. Dynamic proxies offer a more flexible approach in this case.

The example in Listing 28.12 shows how a new capability can be added to classes that implement a particular interface. For this example, an existing interface and class were selected, namely Set and HashSet. The proxy created offers the capability to selectively allow or disallow calls to the methods defined by Set that remove elements from the collection. The DisallowRemoveSet class implements InvocationHandler by passing method calls to a delegate Set. If the method call would remove an element from the set, and this option is currently disabled, the call is rejected by throwing an UnsupportedOperationException.

Listing 28.12 Dynamic Proxy Creation
 import java.lang.reflect.*; import java.util.*; /*  * Implement InvocationHandler to turn on and off the ability  * to disable the methods in Set that remove elements from the  * collection  */ class DisallowRemoveSet implements InvocationHandler {   private Set delegate;   private boolean disallowRemove = true;   public DisallowRemoveSet(Set delegate) {     this.delegate = delegate;   }   public void setDisallowRemove(boolean disallowRemove) {     this.disallowRemove = disallowRemove;   }   public Object invoke( Object proxy, Method meth, Object[] args )     throws Throwable {     if ( disallowRemove && (meth.getName().startsWith("remove")       meth.getName().equals("retainAll")  meth.getName().equals("clear")) ) {       // trying to remove when the option is disabled       throw new UnsupportedOperationException(         "Set does not allow element removal"); }     // method call okay, send it on to the delegate Set implementation     return meth.invoke(delegate, args);   } } public class SetProxyExample {   public static void main(String[] args) {     Set delegate = new HashSet();     Class[] setInterface = new Class[] {  Set.class } ;     DisallowRemoveSet handler = new DisallowRemoveSet(delegate);     // create a proxy that wraps a HashSet implementation of the Set     // interface with a DisallowRemoveSet     Set setProxy = (Set)Proxy.newProxyInstance( Thread.currentThread().       getContextClassLoader(), setInterface, handler );     // build a set with 5 elements     String[] data = new String[] { "A","B","C","D","E"} ;     setProxy.addAll( Arrays.asList(data) );     try {       System.out.println("Enable removes and call for first data entry");       handler.setDisallowRemove(false);       setProxy.remove( data[0] );       handler.setDisallowRemove(true);       System.out.println("Disable removes and call for second data entry");       setProxy.remove( data[1] );     }     catch (UnsupportedOperationException e ) {       System.out.println("Exception thrown: " + e.getMessage());     }   } } 

The program in Listing 28.12, which should be saved as SetProxyExample.java, creates a proxy that implements Set using the Proxy.newProxyInstance method. This proxy is then used to add elements to the underlying set. Two removals are then attempted, one with the option to remove elements allowed and the other with it disallowed . If you execute the application, you'll get the following output:

 Enable removes and call for first data entry Disable removes and call for second data entry Exception thrown: Set does not allow element removal 

One of the strengths of this approach is that, even though this example used HashSet as the interface implementation, the same functionality would be obtained if another implementation of Set were substituted. The InvocationHandler does not impose any requirements on the implementation classes, so the existing class hierarchy is not intruded upon in any way.

   


Special Edition Using Java 2 Standard Edition
Special Edition Using Java 2, Standard Edition (Special Edition Using...)
ISBN: 0789724685
EAN: 2147483647
Year: 1999
Pages: 353

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net