Java.lang.reflect.Proxy retourner un proxy à partir des résultats d'invocation dans ClassCastException en mission

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

Question

Alors je joue avec GeoTools et je pensais avoir une procuration à un de leurs classes de données d'accès et trace la façon dont il a été utilisé dans leur code.

Je codé un proxy dynamique et enroula une FeatureSource (interface) dans et hors il est allé joyeusement. Ensuite, je voulais regarder quelques-uns des transitif objets retournés par le featureSource aussi bien, puisque la chose principale un FeatureSource n'est de retour d'un FeatureCollection (FeatureSource est analogue à un sql DataSource et FeatureCollection à une instruction SQL).

dans mon InvocationHandler Je viens de passer l'appel par le biais de l'objet sous-jacent, l'impression de la classe cible / méthode / args et le résultat que je suis allé, mais pour les appels qui sont retournés une FeatureCollection (une autre interface), j'enveloppé cet objet dans mon proxy (la même classe, mais une nouvelle instance, ne devrait pas devrait-il? question) et a renvoyé. BAM! exception Classcast:

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

le code d'appel:

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);
}

}

Est-il possible de revenir dynamiquement des interfaces proxy de une interface proxy ou ceux que je fais quelque chose de mal? cheers!

Était-ce utile?

La solution

renvoie Class.getInterfaces () seules les interfaces DIRECTEMENT mises en œuvre par la classe. Vous avez besoin d'une fermeture transitive optain toutes les interfaces.

UPDATE

Exemple:

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);
        }
    }
}

Vous devrez peut-être « unwrapp » les mandataires qui sont passés comme arguments.

Autres conseils

@ solution de maurice-perry fonctionnait très bien pour moi et je l'ai voté pour, mais je ne voulais aussi souligner qu'il existe des implémentations bibliothèque de la méthode nécessaire.

J'ai fini par la mise en œuvre de cette solution avec la méthode de la bibliothèque 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()]);
}

Il fonctionne très bien pour ce second argument magique dans newProxyInstance :

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

Il y a aussi une approche Goyave en utilisant:

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

Mais alors vous devez comprendre howto convertir Set<TypeToken> à Class<?>[]. Trivial peut-être, si vous êtes un mordu goyave, mais Apache est prêt à être utilisé.

Ces deux ont été notés dans ce fil lié, obtenir tous (dérivé) interfaces d'un de classe.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top