com.google.dexmaker.stock
Class ProxyBuilder<T>

java.lang.Object
  extended by com.google.dexmaker.stock.ProxyBuilder<T>

public final class ProxyBuilder<T>
extends Object

Creates dynamic proxies of concrete classes.

This is similar to the java.lang.reflect.Proxy class, but works for classes instead of interfaces.

Example

The following example demonstrates the creation of a dynamic proxy for java.util.Random which will always return 4 when asked for integers, and which logs method calls to every method.
 InvocationHandler handler = new InvocationHandler() {
     @Override
     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
         if (method.getName().equals("nextInt")) {
             // Chosen by fair dice roll, guaranteed to be random.
             return 4;
         }
         Object result = ProxyBuilder.callSuper(proxy, method, args);
         System.out.println("Method: " + method.getName() + " args: "
                 + Arrays.toString(args) + " result: " + result);
         return result;
     }
 };
 Random debugRandom = ProxyBuilder.forClass(Random.class)
         .dexCache(getInstrumentation().getTargetContext().getDir("dx", Context.MODE_PRIVATE))
         .handler(handler)
         .build();
 assertEquals(4, debugRandom.nextInt());
 debugRandom.setSeed(0);
 assertTrue(debugRandom.nextBoolean());
 

Usage

Call forClass(Class) for the Class you wish to proxy. Call handler(InvocationHandler) passing in an InvocationHandler, and then call build(). The returned instance will be a dynamically generated subclass where all method calls will be delegated to the invocation handler, except as noted below.

The static method callSuper(Object, Method, Object...) allows you to access the original super method for a given proxy. This allows the invocation handler to selectively override some methods but not others.

By default, the build() method will call the no-arg constructor belonging to the class being proxied. If you wish to call a different constructor, you must provide arguments for both constructorArgTypes(Class[]) and constructorArgValues(Object[]).

This process works only for classes with public and protected level of visibility.

You may proxy abstract classes. You may not proxy final classes.

Only non-private, non-final, non-static methods will be dispatched to the invocation handler. Private, static or final methods will always call through to the superclass as normal.

The Object.finalize() method on Object will not be proxied.

You must provide a dex cache directory via the dexCache(File) method. You should take care not to make this a world-writable directory, so that third parties cannot inject code into your application. A suitable parameter for these output directories would be something like this:

getApplicationContext().getDir("dx", Context.MODE_PRIVATE);
 

If the base class to be proxied leaks the this pointer in the constructor (bad practice), that is to say calls a non-private non-final method from the constructor, the invocation handler will not be invoked. As a simple concrete example, when proxying Random we discover that it inernally calls setSeed during the constructor. The proxy will not intercept this call during proxy construction, but will intercept as normal afterwards. This behaviour may be subject to change in future releases.

This class is not thread safe.


Method Summary
 T build()
          Create a new instance of the class to proxy.
static Object callSuper(Object proxy, Method method, Object... args)
           
 ProxyBuilder<T> constructorArgTypes(Class<?>... constructorArgTypes)
           
 ProxyBuilder<T> constructorArgValues(Object... constructorArgValues)
           
 ProxyBuilder<T> dexCache(File dexCache)
           
static
<T> ProxyBuilder<T>
forClass(Class<T> clazz)
           
static InvocationHandler getInvocationHandler(Object instance)
          Returns the proxy's InvocationHandler.
 ProxyBuilder<T> handler(InvocationHandler handler)
           
 ProxyBuilder<T> parentClassLoader(ClassLoader parent)
          Specifies the parent ClassLoader to use when creating the proxy.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Method Detail

forClass

public static <T> ProxyBuilder<T> forClass(Class<T> clazz)

parentClassLoader

public ProxyBuilder<T> parentClassLoader(ClassLoader parent)
Specifies the parent ClassLoader to use when creating the proxy.

If null, ProxyBuilder.class.getClassLoader() will be used.


handler

public ProxyBuilder<T> handler(InvocationHandler handler)

dexCache

public ProxyBuilder<T> dexCache(File dexCache)

constructorArgValues

public ProxyBuilder<T> constructorArgValues(Object... constructorArgValues)

constructorArgTypes

public ProxyBuilder<T> constructorArgTypes(Class<?>... constructorArgTypes)

build

public T build()
        throws IOException
Create a new instance of the class to proxy.

Throws:
UnsupportedOperationException - if the class we are trying to create a proxy for is not accessible.
IOException - if an exception occurred writing to the dexCache directory.
UndeclaredThrowableException - if the constructor for the base class to proxy throws a declared exception during construction.
IllegalArgumentException - if the handler is null, if the constructor argument types do not match the constructor argument values, or if no such constructor exists.

getInvocationHandler

public static InvocationHandler getInvocationHandler(Object instance)
Returns the proxy's InvocationHandler.

Throws:
IllegalArgumentException - if the object supplied is not a proxy created by this class.

callSuper

public static Object callSuper(Object proxy,
                               Method method,
                               Object... args)
                        throws SecurityException,
                               IllegalAccessException,
                               InvocationTargetException,
                               NoSuchMethodException
Throws:
SecurityException
IllegalAccessException
InvocationTargetException
NoSuchMethodException