2016-12-04 7 views
1

私は次の操作を実行しようとしたとき、私はロード・コールで例外を取得:MethodDelegationまたはForwardingでByte Buddyプロキシを作成するには?

Field datasourceExtensionField = Grid.class.getDeclaredField("datasourceExtension"); 
    datasourceExtensionField.setAccessible(true); 
    RpcDataProviderExtension rpcDataProviderExtension = (RpcDataProviderExtension) datasourceExtensionField.get(grid); 

    Field activeItemHandlerField = RpcDataProviderExtension.class.getDeclaredField("activeItemHandler"); 
    activeItemHandlerField.setAccessible(true); 
    Object activeItemHandler = activeItemHandlerField.get(rpcDataProviderExtension); 

    Field keyMapperField = activeItemHandler.getClass().getDeclaredField("keyMapper"); 
    keyMapperField.setAccessible(true); 
    KeyMapper original = (KeyMapper) keyMapperField.get(activeItemHandler); 

    KeyMapper wrapper = new ByteBuddy() // 
     .subclass(KeyMapper.class) // 
     .defineField("original", KeyMapper.class, Visibility.PUBLIC) // 
     .method(ElementMatchers.any()) // 
     .intercept(Forwarding.toField("original")) // 
     .method(ElementMatchers.named("get")) // 
     .intercept(MethodDelegation.to(new KeyMapperWrapper(grid, original))) // 
     .make() // 
     .load(KeyMapperWrapper.class.getClassLoader()) // 
     .getLoaded() // 
     .newInstance(); 

    // give wrapper the reference to the original 
    wrapper.getClass().getDeclaredField("original").set(wrapper, original); 

    // replace original with wrapper 
    keyMapperField.set(activeItemHandler, wrapper); 

例外:

java.lang.VerifyError: Bad access to protected data in invokevirtual 
Exception Details: 
    Location: 
    com/vaadin/server/KeyMapper$ByteBuddy$WlWljaQa.clone()Ljava/lang/Object; @4: invokevirtual 
    Reason: 
    Type 'com/vaadin/server/KeyMapper' (current frame, stack[0]) is not assignable to 'com/vaadin/server/KeyMapper$ByteBuddy$WlWljaQa' 
    Current Frame: 
    bci: @4 
    flags: { } 
    locals: { 'com/vaadin/server/KeyMapper$ByteBuddy$WlWljaQa' } 
    stack: { 'com/vaadin/server/KeyMapper' } 
    Bytecode: 
    0x0000000: 2ab4 000c b600 1cb0      

    at java.lang.Class.getDeclaredFields0(Native Method) 
    at java.lang.Class.privateGetDeclaredFields(Class.java:2583) 
    at java.lang.Class.getDeclaredField(Class.java:2068) 
    at net.bytebuddy.implementation.LoadedTypeInitializer$ForStaticField.onLoad(LoadedTypeInitializer.java:101) 
    at net.bytebuddy.implementation.LoadedTypeInitializer$Compound.onLoad(LoadedTypeInitializer.java:180) 
    at net.bytebuddy.dynamic.TypeResolutionStrategy$Passive.initialize(TypeResolutionStrategy.java:75) 
    at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:4525) 
    at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:4514) 
    at test.KeyMapperWrapper.patch(KeyMapperWrapper.java:62) 

私は明らかにForwardingが動作するようになっているか理解していないよ、何午前私は間違っている?

私の意図は、既存のKeyMapperを1つの単一の方法をに上書きするプロキシに置き換え、残りを元に委譲することです。

編集:私は今も同じ例外がスローされ、MethodDelegationで試してみました:

.method(ElementMatchers.any()) // 
.intercept(MethodDelegation.to(original)) // 
.method(ElementMatchers.named("get")) // 
.intercept(MethodDelegation.to(new KeyMapperWrapper(grid, original))) // 

答えて

1

これはByte Buddyのバグです。あなたが作成しようとしているタイプは合法ではありません.Byte Buddyはあなたに適切なエラーメッセージを提供しません。サブクラス化するときは、protectedメソッドをオーバーライドすることが正当です。しかし、可視性の制約のために、これらのメソッドを別の型で呼び出すことは必ずしも正当ではありません。転送を転送する場合は、publicメソッドのみを上書きできます。あなたがしたいのは、一致することです:

.method(ElementMatchers.isPublic()) 

ここでエラーは発生しません。私はすでにByte Buddyの次のリリースバージョン(1.5.8)でこのエラーメッセージを提供する修正を追加しました。

+0

ありがとうございました!私がサブクラス化しようとしているのはpublicメソッドしかないので、合法ではありませんか?しかし、私が必要とする次のプロキシは、すべてのパブリックメソッドを持っていない可能性がありますので、おそらくInvocationHandlerAdapterを使用する方がよいでしょうか? (呼び出す前にmethod.setAccessible(true)を使用します) – Zalumon

+1

'Object'からメソッドを継承します。問題のある問題のメソッドは、検証者のエラーからわかるように 'Object :: clone'でした。 –

+0

私は、ありがとう!私はInvocationHandlerAdapterアプローチに固執すると思います。 – Zalumon

0

はInvocationHandlerAdapterと解決策​​を見つけました。しかし、なぜ私の元の試みがうまくいかなかったのかまだ理解していない。

public class KeyMapperProxyHandler implements InvocationHandler 
{ 
    @SuppressWarnings("unused") 
    private static final Logger LOGGER    = Logger.getLogger(KeyMapperProxyHandler.class.getName()); 

    private Grid    m_grid    = null; 
    private KeyMapper   m_originalKeyMapper = null; 

    public KeyMapperProxyHandler(Grid grid, KeyMapper originalKeyMapper) 
    { 
    m_grid = grid; 
    m_originalKeyMapper = originalKeyMapper; 
    } 

    /** 
    * call after container data source has been set 
    */ 
    public static void patch(Grid grid) 
    { 
    try 
    { 
     Field datasourceExtensionField = Grid.class.getDeclaredField("datasourceExtension"); 
     datasourceExtensionField.setAccessible(true); 
     RpcDataProviderExtension rpcDataProviderExtension = (RpcDataProviderExtension) datasourceExtensionField.get(grid); 

     Field activeItemHandlerField = RpcDataProviderExtension.class.getDeclaredField("activeItemHandler"); 
     activeItemHandlerField.setAccessible(true); 
     Object activeItemHandler = activeItemHandlerField.get(rpcDataProviderExtension); 

     Field keyMapperField = activeItemHandler.getClass().getDeclaredField("keyMapper"); 
     keyMapperField.setAccessible(true); 
     KeyMapper original = (KeyMapper) keyMapperField.get(activeItemHandler); 

     KeyMapperProxyHandler proxyHandler = new KeyMapperProxyHandler(grid, original); 

     KeyMapper proxy = new ByteBuddy() // 
      .subclass(KeyMapper.class) // 
      .method(ElementMatchers.any()) // 
      .intercept(InvocationHandlerAdapter.of(proxyHandler)) // 
      .method(ElementMatchers.named("get")) // 
      .intercept(MethodDelegation.to(proxyHandler)) // 
      .make() // 
      .load(KeyMapperProxyHandler.class.getClassLoader()) // 
      .getLoaded() // 
      .newInstance(); 

     keyMapperField.set(activeItemHandler, proxy); 
    } 
    catch(Throwable t) 
    { 
     throw new RuntimeException(t); 
    } 
    } 

    /** 
    * override for get method 
    */ 
    public Object get(String key) 
    { 
    Object staleItemId = m_originalKeyMapper.get(key); 
    Optional freshItemId = m_grid.getContainerDataSource().getItemIds().stream().filter(i -> i.equals(staleItemId)).findAny(); 
    // LOGGER.log(Level.INFO, "intercept: stale=" + staleItemId + ", fresh=" + freshItemId); 
    return freshItemId.isPresent() ? freshItemId.get() : null; 
    } 

    /** 
    * proxy all other methods 
    */ 
    @Override 
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
    { 
    return method.invoke(m_originalKeyMapper, args); 
    } 
} 
0
本当に

ない私は尋ねた質問への答えではなく、私の問題には2つの以上の代替ソリューション:

私はそれが使用されていない時点での目的のオブジェクト参照を置き換えることができると自分を納得させまだ。だから私は/プロキシ何を委任しないことを決めた、と離れて元のオブジェクトのインスタンスをスローし、完全にバイト仲間ダイナミックタイプでそれを置き換える:

public class GridKeyMapperPatch 
{ 
    @SuppressWarnings("unused") 
    private static final Logger LOGGER = Logger.getLogger(GridKeyMapperPatch.class.getName()); 

    private Grid    m_grid = null; 

    public GridKeyMapperPatch(Grid grid) 
    { 
    m_grid = grid; 
    } 

    /** 
    * call immediately after setting container data source 
    */ 
    public static void patch(Grid grid) 
    { 
    try 
    { 
     GridKeyMapperPatch gridKeyMapperPatch = new GridKeyMapperPatch(grid); 

     KeyMapper patchedKeyMapper = new ByteBuddy() // 
      .subclass(KeyMapper.class) // 
      .method(ElementMatchers.named("get")) // 
      .intercept(MethodDelegation.to(gridKeyMapperPatch)) // 
      .make() // 
      .load(GridKeyMapperPatch.class.getClassLoader()) // 
      .getLoaded() // 
      .newInstance(); 

     Field datasourceExtensionField = Grid.class.getDeclaredField("datasourceExtension"); 
     datasourceExtensionField.setAccessible(true); 
     RpcDataProviderExtension rpcDataProviderExtension = (RpcDataProviderExtension) datasourceExtensionField.get(grid); 

     Field activeItemHandlerField = RpcDataProviderExtension.class.getDeclaredField("activeItemHandler"); 
     activeItemHandlerField.setAccessible(true); 
     Object activeItemHandler = activeItemHandlerField.get(rpcDataProviderExtension); 

     Field keyMapperField = activeItemHandler.getClass().getDeclaredField("keyMapper"); 
     keyMapperField.setAccessible(true); 
     keyMapperField.set(activeItemHandler, patchedKeyMapper); 
    } 
    catch(Throwable t) 
    { 
     throw new RuntimeException(t); 
    } 
    } 

    public Object get(@SuperCall Callable superCall, String key) throws Exception 
    { 
    Object staleItemId = superCall.call(); 
    Optional freshItemId = m_grid.getContainerDataSource().getItemIds().stream().filter(i -> i.equals(staleItemId)).findAny(); 
    LOGGER.log(Level.INFO, "intercept: stale=" + staleItemId + ", fresh=" + freshItemId); 
    return freshItemId.isPresent() ? freshItemId.get() : null; 
    } 
} 

はバックステップを取ると、私は、クラスとメソッド以来、ことがわかりました一般的にサブクラス化できます:

public class GridTools 
{ 
    @SuppressWarnings("unused") 
    private static final Logger LOGGER = Logger.getLogger(GridTools.class.getName()); 

    /** 
    * call immediately after setting container data source 
    */ 
    public static void replaceKeyMapper(Grid grid) throws Exception 
    { 
    Field datasourceExtensionField = Grid.class.getDeclaredField("datasourceExtension"); 
    datasourceExtensionField.setAccessible(true); 
    RpcDataProviderExtension rpcDataProviderExtension = (RpcDataProviderExtension) datasourceExtensionField.get(grid); 

    Field activeItemHandlerField = RpcDataProviderExtension.class.getDeclaredField("activeItemHandler"); 
    activeItemHandlerField.setAccessible(true); 
    Object activeItemHandler = activeItemHandlerField.get(rpcDataProviderExtension); 

    Field keyMapperField = activeItemHandler.getClass().getDeclaredField("keyMapper"); 
    keyMapperField.setAccessible(true); 
    keyMapperField.set(activeItemHandler, new NonCachingKeyMapper(grid)); 
    } 

    private static class NonCachingKeyMapper extends KeyMapper 
    { 
    private Grid m_grid = null; 

    public NonCachingKeyMapper(Grid grid) 
    { 
     m_grid = grid; 
    } 

    @Override 
    public Object get(String key) 
    { 
     Object staleItemId = super.get(key); 
     Optional freshItemId = m_grid.getContainerDataSource().getItemIds().stream().filter(i -> i.equals(staleItemId)).findAny(); 
     LOGGER.log(Level.INFO, "intercept: stale=" + staleItemId + ", fresh=" + freshItemId); 
     return freshItemId.isPresent() ? freshItemId.get() : null; 
    } 
    } 
} 
関連する問題