Java.lang.reflect.Proxy volver otro proxy de los resultados de invocación en ClassCastException en la asignación

StackOverflow https://stackoverflow.com/questions/2642700

Pregunta

Así que estoy jugando con GeoTools y pensé que había un apoderado de sus clases de acceso a datos y traza la forma en que se está utilizando en su código.

I codificado un proxy dinámico y se envolvió en una FeatureSource (interfaz) en ella y fuera de él fue felizmente. Entonces quería mirar a algunos de los transitivos objetos devueltos por el featureSource también, ya que lo más importante de un FeatureSource hace es devolver un FeatureCollection (FeatureSource es análogo a un SQL DataSource y featurecollection a una instrucción SQL).

en mi invocationhandler me acaba de pasar la llamada a través del objeto subyacente, la impresión de la clase de destino / método / args y resultado que fui, pero para las llamadas que devuelven un FeatureCollection (otra interfaz), que envuelve ese objeto en mi proxy (la misma clase, pero una nueva instancia, no debería importar debería?) y lo devolvió. BAM! Classcast excepción:

java.lang.ClassCastException: $Proxy5 cannot be cast to org.geotools.feature.FeatureCollection  
    at $Proxy4.getFeatures(Unknown Source)  
    at MyClass.myTestMethod(MyClass.java:295)  

el Código de llamada:

FeatureSource<SimpleFeatureType, SimpleFeature> featureSource = ... // create the FS
featureSource = (FeatureSource<SimpleFeatureType, SimpleFeature>) FeatureSourceProxy.newInstance(featureSource, features);
featureSource.getBounds();// ok
featureSource.getSupportedHints();// ok

DefaultQuery query1 = new DefaultQuery(DefaultQuery.ALL);
FeatureCollection<SimpleFeatureType, SimpleFeature> results = featureSource.getFeatures(query1); //<- explosion here

Proxy:

public class FeatureSourceProxy  implements java.lang.reflect.InvocationHandler {

private Object target;
private List<SimpleFeature> features;

public static Object newInstance(Object obj, List<SimpleFeature> features) {
return java.lang.reflect.Proxy.newProxyInstance(
    obj.getClass().getClassLoader(), 
    obj.getClass().getInterfaces(), 
    new FeatureSourceProxy(obj, features)
);
}

private FeatureSourceProxy(Object obj, List<SimpleFeature> features) {
this.target = obj;
this.features = features;
}

public Object invoke(Object proxy, Method m, Object[] args)throws Throwable{
Object result = null;
try {
    if("getFeatures".equals(m.getName())){ 
        result = interceptGetFeatures(m, args);
    }
    else{
        result = m.invoke(target, args);
    }
} 
catch (Exception e) {
    throw new RuntimeException("unexpected invocation exception: " +  e.getMessage(), e);
} 
return result;
}

private Object interceptGetFeatures(Method m, Object[] args) throws Exception{
    return newInstance(m.invoke(target, args), features);
}

}

¿Es posible apoderados de las interfaces volver dinámicamente a partir de una interfaz de proxy o estoy haciendo algo mal? saludos!

¿Fue útil?

Solución

Class.getInterfaces () devuelve sólo las interfaces implementadas directamente por la clase. Es necesario un cierre transitivo a obtener en linea todas las interfaces.

Actualizar

Ejemplo:

private static Class<?>[] getInterfaces(Class<?> c) {
    List<Class<?>> result = new ArrayList<Class<?>>();
    if (c.isInterface()) {
        result.add(c);
    } else {
        do {
            addInterfaces(c, result);
            c = c.getSuperclass();
        } while (c != null);
    }
    for (int i = 0; i < result.size(); ++i) {
        addInterfaces(result.get(i), result);
    }
    return result.toArray(new Class<?>[result.size()]);
}

private static void addInterfaces(Class<?> c, List<Class<?>> list) {
    for (Class<?> intf: c.getInterfaces()) {
        if (!list.contains(intf)) {
            list.add(intf);
        }
    }
}

También es posible que tenga que "unwrapp" los proxies que se pasan como argumentos.

Otros consejos

La solución de

@-Maurice Perry funcionó muy bien para mí y he votado por él, pero yo también quiero señalar que existen implementaciones de la biblioteca del método necesaria.

Terminé la implementación de esta solución con el método de biblioteca Apache Commons ClassUtils.getAllInterfaces() :

...
import org.apache.commons.lang3.ClassUtils;
...

private static Class<?>[] getAllInterfaces(Object object) {
    final List<Class<?>> interfaces =
        ClassUtils.getAllInterfaces(object.getClass());

    return interfaces.toArray(new Class<?>[interfaces.size()]);
}

Funciona muy bien para ese segundo argumento mágico en newProxyInstance :

Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, 
                       InvocationHandler h)

También hay un enfoque de guayaba usando:

final Set<TypeToken> tt = TypeToken.of(cls).getTypes().interfaces();

Pero entonces usted tiene que averiguar Set<TypeToken> ¿Cómo convertir a Class<?>[]. Trivial quizás, si eres un aficionado a la guayaba, pero Apache está listo para su uso.

Ambos éstos se observaron en este hilo relacionada, get todo (derivado) interfaces de un clase.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top