2010-04-15 12 views
7

Quindi sto giocando con i geotools e ho pensato di eseguire il proxy di una delle loro classi di accesso ai dati e di tracciare il modo in cui sono stati utilizzati nella loro codice.Java.lang.reflect.Proxy che restituisce un altro proxy dai risultati di chiamata in ClassCastException sull'assegnazione

Ho codificato un proxy dinamico e racchiuso una FeatureSource (interfaccia) al suo interno e ne è uscito felicemente. Quindi ho voluto esaminare alcuni degli oggetti transitivi restituiti da featureSource, poiché la caratteristica principale di FeatureSource è restituire un FeatureCollection (FeatureSource è analogo a un sql DataSource e featurecollection a un'istruzione sql).

nel mio invocationhandler ho appena passato la chiamata attraverso l'oggetto sottostante, stampando la classe/metodo/args di destinazione e il risultato come sono andato, ma per le chiamate che hanno restituito un FeatureCollection (un'altra interfaccia), ho spostato quell'oggetto in il mio proxy (la stessa classe ma una nuova istanza, non dovrebbe importare, dovrebbe?) e lo ha restituito. BAM! eccezione Classcast:

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

il codice chiamante:

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

}

E 'possibile restituire dinamicamente proxy di interfacce da un proxy interfaccia o sto facendo qualcosa di sbagliato? evviva!

risposta

10

Class.getInterfaces() restituisce solo le interfacce DIRETTAMENTE implementate dalla classe. Hai bisogno di una chiusura transitiva per ottenere tutte le interfacce.

UPDATE

Esempio:

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

Potrebbe anche essere necessario a "unwrapp" le deleghe che vengono passate come argomenti.

+2

funziona perfettamente !!wow grazie per quello, a ben vedere è ovvio che avrei dovuto immaginarlo, per qualche ragione pensavo che getInterfaces() avrebbe restituito * tutte * le interfacce implementate, non solo quelle implementate direttamente dalla classe. – matao

0

La soluzione di @ maurice-perry ha funzionato benissimo per me e ho votato a favore, ma volevo anche sottolineare che esistono implementazioni di libreria del metodo necessario.

ho finito per implementare questa soluzione con il metodo di Apache Commons libreria 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()]); 
} 

E le grandi opere per questo secondo argomento magico in newProxyInstance:

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

C'è anche un approccio Guava utilizzando :

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

Ma poi devi capire come convertire Set<TypeToken> a Class<?>[]. Forse banale, se sei un appassionato di Guava, ma Apache è pronto per l'uso.

Entrambe sono state annotate in questa filettatura correlata, get all (derived) interfaces of a class.

Problemi correlati