views:

701

answers:

4

What is a use case for using a dynamic proxy?

How do they relate to bytecode generation and reflection?

Any recommended reading?

+2  A: 

The class java.lang.reflect.Proxy allows you to implement interfaces dynamically by handling method calls in an InvocationHandler. It is considered part of Java's reflection facility, but has nothing to do with bytecode generation.

Sun has a tutorial about the use of the Proxy class. Google helps, too.

Michael Borgwardt
+1  A: 

One use case is hibernate - it gives you objects implementing your model classes interface but under getters and setters there resides db related code. I.e. you use them as if they are just simple POJO, but actually there is much going on under cover.

For example - you just call a getter of lazily loaded property, but really the property (probably whole big object structure) gets fetched from the database.

You should check cglib library for more info.

miceuz
+3  A: 

A dynamic proxy class is a class that implements a list of interfaces specified at runtime such that a method invocation through one of the interfaces on an instance of the class will be encoded and dispatched to another object through a uniform interface. It can be used to create a type-safe proxy object for a list of interfaces without requiring pre-generation of the proxy class.Dynamic proxy classes are useful to an application or library that needs to provide type-safe reflective dispatch of invocations on objects that present interface APIs

Good answer, and a good example of this very thing is Spring Remoting, which has built in proxy capability for HTTP, RMI, EJB and JMS.
Robin
+1  A: 

I just came up with an interesting use for a dynamic proxy.

We were having some trouble a non-critical service that is coupled with another dependant service and wanted to explore ways of being fault-tolerant when that dependant service becomes unavailable.

So I wrote a LoadSheddingProxy that takes two delegates - one is the remote impl for the 'normal' service (after the JNDI lookup). The other object is a 'dummy' load-shedding impl. There is simple logic surrounding each method invoke that catches timeouts and diverts to the dummy for a certain length of time before retrying. Here's how I use it:

// This is part of your ServiceLocator class
public static MyServiceInterface getMyService() throws Exception
{
    MyServiceInterface loadShedder = new MyServiceInterface() {
        public Thingy[] getThingys(Stuff[] whatever) throws Exception {
         return new Thingy[0];
        }
        //... etc - basically a dummy version of your service goes here
    }         
    Context ctx = JndiUtil.getJNDIContext(MY_CLUSTER);
    try {
        MyServiceInterface impl = ((MyServiceHome) PortableRemoteObject.narrow(
          ctx.lookup(MyServiceHome.JNDI_NAME), 
          MyServiceHome.class)).create();
        // Here's where the proxy comes in
        return (MyService) Proxy.newProxyInstance(
            MyServiceHome.class.getClassLoader(),
     new Class[] { MyServiceInterface.class },
     new LoadSheddingProxy(MyServiceHome.JNDI_NAME, impl, loadShedder, 60000)); // 10 minute retry
 } catch (RemoteException e) {    // If we can't even look up the service we can fail by shedding load too
     logger.warn("Shedding load");
     return loadShedder;
    } finally {
        if (ctx != null) {
     ctx.close();
        }
    }
}

And here's the proxy:

public class LoadSheddingProxy implements InvocationHandler {

static final Logger logger = ApplicationLogger.getLogger(LoadSheddingProxy.class);

Object primaryImpl, loadDumpingImpl;
long retry;
String serviceName;
// map is static because we may have many instances of a proxy around repeatedly looked-up remote objects
static final Map<String, Long> servicesLastTimedOut = new HashMap<String, Long>();

public LoadSheddingProxy(String serviceName, Object primaryImpl, Object loadDumpingImpl, long retry)
{
    this.serviceName = serviceName;
    this.primaryImpl = primaryImpl;
    this.loadDumpingImpl = loadDumpingImpl;
    this.retry = retry;
}

public Object invoke(Object obj, Method m, Object[] args) throws Throwable
{
    try
    {
        if (!servicesLastTimedOut.containsKey(serviceName) || timeToRetry()) {
         Object ret = m.invoke(primaryImpl, args);
         servicesLastTimedOut.remove(serviceName);
         return ret;
        } 
        return m.invoke(loadDumpingImpl, args);
    }
    catch (InvocationTargetException e)
    {
        Throwable targetException = e.getTargetException();

        // DETECT TIMEOUT HERE SOMEHOW - not sure this is the way to do it???
        if (targetException instanceof RemoteException) {
            servicesLastTimedOut.put(serviceName, Long.valueOf(System.currentTimeMillis()));
        }
        throw targetException;
    }                    
}

private boolean timeToRetry() {
    long lastFailedAt = servicesLastTimedOut.get(serviceName).longValue();
    return (System.currentTimeMillis() - lastFailedAt) > retry;
}
}
Jim P