Top Description Inners Fields Constructors Methods
org.droidparts.dexmaker.stock

public final Class ProxyBuilder<T>

extends Object
Class Inheritance
Imports
org.droidparts.dexmaker.*, java.io.File, .IOException, java.lang.reflect.*, java.util.*, java.util.concurrent.CopyOnWriteArraySet

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 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 internally 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.

Nested and Inner Type Summary

Modifier and TypeClass and Description
private static class
ProxyBuilder.MethodSetEntry

Wrapper class to let us disambiguate Method objects.

Field Summary

Modifier and TypeField and Description
private final Class<T>
private Class<?>[]
private Object[]
private File
private static final String
private static final String
private static final Map<Class<?>, Class<?>>
generatedProxyClasses

A cache of all proxy classes ever generated.

private InvocationHandler
private Set<Class<?>>
private ClassLoader
private static final Map<Class<?>, Class<?>>
private static final Map<Class<?>, MethodId<?, ?>>
PRIMITIVE_TO_UNBOX_METHOD

Map from primitive type to method used to unbox a boxed version of the primitive.

private static final Map<TypeId<?>, MethodId<?, ?>>
public static final int

Constructor Summary

AccessConstructor and Description
private
ProxyBuilder(Class<T> clazz)

Method Summary

Modifier and TypeMethod and Description
private static <T> Set<T>
asSet(T... array)

private static Local<?>
boxIfRequired(Code code, Local<?> parameter, Local<Object> temp)

public T
build()

Create a new instance of the class to proxy.

public Class<? extends T>
buildProxyClass()

Generate a proxy class.

public static Object
callSuper(Object proxy, Method method, Object... args)

private static void
check(boolean condition, String message)

private static TypeId<?>[]
public ProxyBuilder<T>
constructorArgTypes(Class<?>... constructorArgTypes)

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

public ProxyBuilder<T>
dexCache(File dexCacheParent)

Sets the directory where executable code is stored.

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

private static <T, G extends T> void
generateCodeForAllMethods(DexMaker dexMaker, TypeId<G> generatedType, Method[] methodsToProxy, TypeId<T> superclassType)

private static void
generateCodeForReturnStatement(Code code, Class<T> methodReturnType, Local<T> localForResultOfInvoke, Local<T> localOfMethodReturnType, Local<T> aBoxedResult)

Calculates the correct return statement code for a method.

private static <T, G extends T> void
generateConstructorsAndFields(DexMaker dexMaker, TypeId<G> generatedType, TypeId<T> superType, Class<T> superClass)

private static <T> Constructor<T>[]
private TypeId<?>[]
public static InvocationHandler
getInvocationHandler(Object instance)

Returns the proxy's InvocationHandler.

private static <T> String
private void
private Method[]
getMethodsToProxyRecursive()

Gets all Method objects we can proxy in the hierarchy of the supplied class.

private static MethodId<?, ?>
getUnboxMethodForPrimitive(Class<?> methodReturnType)

public ProxyBuilder<T>
public ProxyBuilder<T>
implementing(Class<?>... interfaces)

private static void
invokeSuper(MethodId<D, R> superMethod, Code superCode, Local<T> superThis, Local<T>[] superArgs, Local<T> superResult)

public static boolean
isProxyClass(Class<?> c)

Returns true if c is a proxy class created by this builder.

private static RuntimeException
private Class<? extends T>
loadClass(ClassLoader classLoader, String generatedName)

public ProxyBuilder<T>
parentClassLoader(ClassLoader parent)

Specifies the parent ClassLoader to use when creating the proxy.

public static void
setInvocationHandler(Object instance, InvocationHandler handler)

Sets the proxy's InvocationHandler.

private static void
setMethodsStaticField(Class<?> proxyClass, Method[] methodsToProxy)

private static String
superMethodName(Method method)

The super method must include the return type, otherwise its ambiguous for methods with covariant return types.

Inherited from java.lang.Object:
cloneequalsfinalizegetClasshashCodenotifynotifyAlltoStringwaitwaitwait

Field Detail

baseClassback to summary
private final Class<T> baseClass
constructorArgTypesback to summary
private Class<?>[] constructorArgTypes
constructorArgValuesback to summary
private Object[] constructorArgValues
dexCacheback to summary
private File dexCache
FIELD_NAME_HANDLERback to summary
private static final String FIELD_NAME_HANDLER
FIELD_NAME_METHODSback to summary
private static final String FIELD_NAME_METHODS
generatedProxyClassesback to summary
private static final Map<Class<?>, Class<?>> generatedProxyClasses

A cache of all proxy classes ever generated. At the time of writing, Android's runtime doesn't support class unloading so there's little value in using weak references.

handlerback to summary
private InvocationHandler handler
interfacesback to summary
private Set<Class<?>> interfaces
parentClassLoaderback to summary
private ClassLoader parentClassLoader
PRIMITIVE_TO_BOXEDback to summary
private static final Map<Class<?>, Class<?>> PRIMITIVE_TO_BOXED
PRIMITIVE_TO_UNBOX_METHODback to summary
private static final Map<Class<?>, MethodId<?, ?>> PRIMITIVE_TO_UNBOX_METHOD

Map from primitive type to method used to unbox a boxed version of the primitive.

This is required for methods whose return type is primitive, since the InvocationHandler will return us a boxed result, and we'll need to convert it back to primitive value.

PRIMITIVE_TYPE_TO_UNBOX_METHODback to summary
private static final Map<TypeId<?>, MethodId<?, ?>> PRIMITIVE_TYPE_TO_UNBOX_METHOD
VERSIONback to summary
public static final int VERSION

Constructor Detail

ProxyBuilderback to summary
private ProxyBuilder(Class<T> clazz)

Method Detail

asSetback to summary
private static <T> Set<T> asSet(T... array)
boxIfRequiredback to summary
private static Local<?> boxIfRequired(Code code, Local<?> parameter, Local<Object> temp)
buildback to summary
public T build() throws IOException

Create a new instance of the class to proxy.

Exceptions
IOException:
if an exception occurred writing to the dexCache directory.
UnsupportedOperationException:
if the class we are trying to create a proxy for is not accessible.
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.
buildProxyClassback to summary
public Class<? extends T> buildProxyClass() throws IOException

Generate a proxy class. Note that new instances of this class will not automatically have an an invocation handler, even if handler(InvocationHandler) was called. The handler must be set on each instance after it is created, using setInvocationHandler(Object, InvocationHandler).

callSuperback to summary
public static Object callSuper(Object proxy, Method method, Object... args) throws Throwable
checkback to summary
private static void check(boolean condition, String message)
classArrayToTypeArrayback to summary
private static TypeId<?>[] classArrayToTypeArray(Class<?>[] input)
constructorArgTypesback to summary
public ProxyBuilder<T> constructorArgTypes(Class<?>... constructorArgTypes)
constructorArgValuesback to summary
public ProxyBuilder<T> constructorArgValues(Object... constructorArgValues)
dexCacheback to summary
public ProxyBuilder<T> dexCache(File dexCacheParent)

Sets the directory where executable code is stored. See DexMaker.generateAndLoad() for guidance on choosing a secure location for the dex cache.

forClassback to summary
public static <T> ProxyBuilder<T> forClass(Class<T> clazz)
generateCodeForAllMethodsback to summary
private static <T, G extends T> void generateCodeForAllMethods(DexMaker dexMaker, TypeId<G> generatedType, Method[] methodsToProxy, TypeId<T> superclassType)
generateCodeForReturnStatementback to summary
private static void generateCodeForReturnStatement(Code code, Class<T> methodReturnType, Local<T> localForResultOfInvoke, Local<T> localOfMethodReturnType, Local<T> aBoxedResult)

Calculates the correct return statement code for a method.

A void method will not return anything. A method that returns a primitive will need to unbox the boxed result. Otherwise we will cast the result.

Annotations
@SuppressWarnings:rawtypes, unchecked
generateConstructorsAndFieldsback to summary
private static <T, G extends T> void generateConstructorsAndFields(DexMaker dexMaker, TypeId<G> generatedType, TypeId<T> superType, Class<T> superClass)
getConstructorsToOverwriteback to summary
private static <T> Constructor<T>[] getConstructorsToOverwrite(Class<T> clazz)
Annotations
@SuppressWarnings:unchecked
getInterfacesAsTypeIdsback to summary
private TypeId<?>[] getInterfacesAsTypeIds()
getInvocationHandlerback to summary
public static InvocationHandler getInvocationHandler(Object instance)

Returns the proxy's InvocationHandler.

Exceptions
IllegalArgumentException:
if the object supplied is not a proxy created by this class.
getMethodNameForProxyOfback to summary
private static <T> String getMethodNameForProxyOf(Class<T> clazz)
getMethodsToProxyback to summary
private void getMethodsToProxy(Set<ProxyBuilder.MethodSetEntry> sink, Set<ProxyBuilder.MethodSetEntry> seenFinalMethods, Class<?> c)
getMethodsToProxyRecursiveback to summary
private Method[] getMethodsToProxyRecursive()

Gets all Method objects we can proxy in the hierarchy of the supplied class.

getUnboxMethodForPrimitiveback to summary
private static MethodId<?, ?> getUnboxMethodForPrimitive(Class<?> methodReturnType)
handlerback to summary
public ProxyBuilder<T> handler(InvocationHandler handler)
implementingback to summary
public ProxyBuilder<T> implementing(Class<?>... interfaces)
invokeSuperback to summary
private static void invokeSuper(MethodId<D, R> superMethod, Code superCode, Local<T> superThis, Local<T>[] superArgs, Local<T> superResult)
Annotations
@SuppressWarnings:unchecked, rawtypes
isProxyClassback to summary
public static boolean isProxyClass(Class<?> c)

Returns true if c is a proxy class created by this builder.

launderCauseback to summary
private static RuntimeException launderCause(InvocationTargetException e)
loadClassback to summary
private Class<? extends T> loadClass(ClassLoader classLoader, String generatedName) throws ClassNotFoundException
Annotations
@SuppressWarnings:unchecked
parentClassLoaderback to summary
public ProxyBuilder<T> parentClassLoader(ClassLoader parent)

Specifies the parent ClassLoader to use when creating the proxy.

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

setInvocationHandlerback to summary
public static void setInvocationHandler(Object instance, InvocationHandler handler)

Sets the proxy's InvocationHandler.

If you create a proxy with build(), the proxy will already have a handler set, provided that you configured one with handler(InvocationHandler).

If you generate a proxy class with buildProxyClass(), instances of the proxy class will not automatically have a handler set, and it is necessary to use this method with each instance.

Exceptions
IllegalArgumentException:
if the object supplied is not a proxy created by this class.
setMethodsStaticFieldback to summary
private static void setMethodsStaticField(Class<?> proxyClass, Method[] methodsToProxy)
superMethodNameback to summary
private static String superMethodName(Method method)

The super method must include the return type, otherwise its ambiguous for methods with covariant return types.

org.droidparts.dexmaker.stock back to summary

private Class ProxyBuilder.MethodSetEntry

extends Object
Class Inheritance

Wrapper class to let us disambiguate Method objects.

The purpose of this class is to override the equals(Object) and hashCode() methods so we can use a Set to remove duplicate methods that are overrides of one another. For these purposes, we consider two methods to be equal if they have the same name, return type, and parameter types.

Field Summary

Modifier and TypeField and Description
private final String
private final Method
private final Class<?>[]
private final Class<?>

Constructor Summary

AccessConstructor and Description
public

Method Summary

Modifier and TypeMethod and Description
public boolean
equals(Object
the reference object with which to compare.
o
)

Overrides java.lang.Object.equals.

Indicates whether some other object is "equal to" this one.
public int
hashCode()

Overrides java.lang.Object.hashCode.

Returns a hash code value for this object.
Inherited from java.lang.Object:
clonefinalizegetClassnotifynotifyAlltoStringwaitwaitwait

Field Detail

nameback to summary
private final String name
originalMethodback to summary
private final Method originalMethod
paramTypesback to summary
private final Class<?>[] paramTypes
returnTypeback to summary
private final Class<?> returnType

Constructor Detail

MethodSetEntryback to summary
public MethodSetEntry(Method method)

Method Detail

equalsback to summary
public boolean equals(Object o)

Overrides java.lang.Object.equals.

Doc from java.lang.Object.equals.

Indicates whether some other object is "equal to" this one.

The equals method implements an equivalence relation on non-null object references:

  • It is reflexive: for any non-null reference value x, x.equals(x) should return true.
  • It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
  • It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
  • It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
  • For any non-null reference value x, x.equals(null) should return false.

An equivalence relation partitions the elements it operates on into equivalence classes; all the members of an equivalence class are equal to each other. Members of an equivalence class are substitutable for each other, at least for some purposes.

Parameters
o:Object

the reference object with which to compare.

Returns:boolean

true if this object is the same as the obj argument; false otherwise.

Annotations
@Override
hashCodeback to summary
public int hashCode()

Overrides java.lang.Object.hashCode.

Doc from java.lang.Object.hashCode.

Returns a hash code value for this object. This method is supported for the benefit of hash tables such as those provided by java.util.HashMap.

The general contract of hashCode is:

  • Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
  • If two objects are equal according to the equals method, then calling the hashCode method on each of the two objects must produce the same integer result.
  • It is not required that if two objects are unequal according to the equals method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.
Returns:int

a hash code value for this object

Annotations
@Override