Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.java.programmer > #31325 > unrolled thread

Dynamic proxy causes downstream code to not find annotations. How to solve?

Started byrupertlssmith@googlemail.com
First post2015-03-16 05:10 -0700
Last post2015-03-17 14:24 +0000
Articles 4 — 3 participants

Back to article view | Back to comp.lang.java.programmer


Contents

  Dynamic proxy causes downstream code to not find annotations. How to solve? rupertlssmith@googlemail.com - 2015-03-16 05:10 -0700
    Re: Dynamic proxy causes downstream code to not find annotations. How to solve? rupertlssmith@googlemail.com - 2015-03-16 07:44 -0700
      Re: Dynamic proxy causes downstream code to not find annotations. How to solve? Robbie Brown <rob@example.com> - 2015-03-16 16:33 +0000
      Re: Dynamic proxy causes downstream code to not find annotations. How to solve? Steven Simpson <ss@domain.invalid> - 2015-03-17 14:24 +0000

#31325 — Dynamic proxy causes downstream code to not find annotations. How to solve?

Fromrupertlssmith@googlemail.com
Date2015-03-16 05:10 -0700
SubjectDynamic proxy causes downstream code to not find annotations. How to solve?
Message-ID<9a31318b-4de7-42cb-9d29-202a973ae557@googlegroups.com>
Hi,

Consider this class:

class MyClass {
    @MyAnnotation
    public void myMethod() { ... }
}

Which is coerced to this interface:

interface SomeIterface {
    public void myMethod() { ... }
}

If downstream code is looking for "MyAnnotation" it will not find it when using a dynamic proxy.

Here is a "default" dynamic proxy, which effectively does nothing except to call the method and report its exceptions as normal:

   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            return method.invoke(obj, args);
        } catch (InvocationTargetException e) {
            throw e.getCause();
        }
    }

The problem is that when invoke is called the Method passed to it is the one from the interface, not the object, so it is SomeInterface.myMethod(), which does not have any annotations.

What I want is to try to match the method on the actual object being proxied, or perhaps some specific interface that I have identified, and effectively "lift" the actual method being invoked, to being that one.

I am running into this problem because I am using the client proxy from Jersey, which uses a dynamic proxy to create a REST client for me, given an HTTP endpoint and an interface. The problem is that when you call it, it checks that there is a @Path annotation on the method being called, and in this case that method is on a different interface, and it fails to find the annotation. So I need to make sure that I call it with the method from the correct interface.

Any suggestions? A problem you have run into before? Thanks for your thoughts.

Rupert

[toc] | [next] | [standalone]


#31326

Fromrupertlssmith@googlemail.com
Date2015-03-16 07:44 -0700
Message-ID<4f429c73-7025-44e2-aadb-f971fbeba006@googlegroups.com>
In reply to#31325
On Monday, March 16, 2015 at 12:10:53 PM UTC, rupert...@googlemail.com wrote:
> Hi,
> 
> Consider this class:
> 
> class MyClass {
>     @MyAnnotation
>     public void myMethod() { ... }
> }
> 
> Which is coerced to this interface:
> 
> interface SomeIterface {
>     public void myMethod() { ... }
> }
> 
> If downstream code is looking for "MyAnnotation" it will not find it when using a dynamic proxy.
> 
> Here is a "default" dynamic proxy, which effectively does nothing except to call the method and report its exceptions as normal:
> 
>    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
>         try {
>             return method.invoke(obj, args);
>         } catch (InvocationTargetException e) {
>             throw e.getCause();
>         }
>     }
> 
> The problem is that when invoke is called the Method passed to it is the one from the interface, not the object, so it is SomeInterface.myMethod(), which does not have any annotations.
> 
> What I want is to try to match the method on the actual object being proxied, or perhaps some specific interface that I have identified, and effectively "lift" the actual method being invoked, to being that one.
> 
> I am running into this problem because I am using the client proxy from Jersey, which uses a dynamic proxy to create a REST client for me, given an HTTP endpoint and an interface. The problem is that when you call it, it checks that there is a @Path annotation on the method being called, and in this case that method is on a different interface, and it fails to find the annotation. So I need to make sure that I call it with the method from the correct interface.
> 
> Any suggestions? A problem you have run into before? Thanks for your thoughts.
> 
> Rupert

Looking into this some more, I ran this code:

public class QuickTest {
    public static void main(String[] args) {
        for (Method method : Test.class.getMethods()) {
            System.out.println(method);
        }
    }
}

class Test implements ITest<Integer> {
    public Integer someMethod() {
        return 1;
    }
}

interface ITest<T> {
    T someMethod();
}

And got this output:

public java.lang.Integer org.mygovscot.stars.client.Test.someMethod()
public java.lang.Object org.mygovscot.stars.client.Test.someMethod()
(... other methods on Object)

This is actually the problem in my code. The dynamic proxy gets the method with types erased, which corresponds to the one on the interface, so it fails to see the annotations.

I am surprised, because I thought that type erasure would mean that there would only be one method at run time, not two.

Is there some way to ensure the dynamic proxy picks up the right method?

Rupert

[toc] | [prev] | [next] | [standalone]


#31327

FromRobbie Brown <rob@example.com>
Date2015-03-16 16:33 +0000
Message-ID<me70ir$gjq$1@dont-email.me>
In reply to#31326
On 16/03/15 14:44, rupertlssmith@googlemail.com wrote:
> On Monday, March 16, 2015 at 12:10:53 PM UTC, rupert...@googlemail.com wrote:
>> Hi,
>>
>> Consider this class:
>>
>> class MyClass {
>>      @MyAnnotation
>>      public void myMethod() { ... }
>> }
>>
>> Which is coerced to this interface:
>>
>> interface SomeIterface {
>>      public void myMethod() { ... }
>> }
>>
>> If downstream code is looking for "MyAnnotation" it will not find it when using a dynamic proxy.
>>
>> Here is a "default" dynamic proxy, which effectively does nothing except to call the method and report its exceptions as normal:
>>
>>     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
>>          try {
>>              return method.invoke(obj, args);
>>          } catch (InvocationTargetException e) {
>>              throw e.getCause();
>>          }
>>      }
>>
>> The problem is that when invoke is called the Method passed to it is the one from the interface, not the object, so it is SomeInterface.myMethod(), which does not have any annotations.
>>
>> What I want is to try to match the method on the actual object being proxied, or perhaps some specific interface that I have identified, and effectively "lift" the actual method being invoked, to being that one.
>>
>> I am running into this problem because I am using the client proxy from Jersey, which uses a dynamic proxy to create a REST client for me, given an HTTP endpoint and an interface. The problem is that when you call it, it checks that there is a @Path annotation on the method being called, and in this case that method is on a different interface, and it fails to find the annotation. So I need to make sure that I call it with the method from the correct interface.
>>
>> Any suggestions? A problem you have run into before? Thanks for your thoughts.
>>
>> Rupert
>
> Looking into this some more, I ran this code:
>
> public class QuickTest {
>      public static void main(String[] args) {
>          for (Method method : Test.class.getMethods()) {
>              System.out.println(method);
>          }
>      }
> }
>
> class Test implements ITest<Integer> {
>      public Integer someMethod() {
>          return 1;
>      }
> }
>
> interface ITest<T> {
>      T someMethod();
> }
>
> And got this output:
>
> public java.lang.Integer org.mygovscot.stars.client.Test.someMethod()
> public java.lang.Object org.mygovscot.stars.client.Test.someMethod()
> (... other methods on Object)
>
> This is actually the problem in my code. The dynamic proxy gets the method with types erased, which corresponds to the one on the interface, so it fails to see the annotations.
>
> I am surprised, because I thought that type erasure would mean that there would only be one method at run time, not two.
>
> Is there some way to ensure the dynamic proxy picks up the right method?
>
> Rupert

Isn't type erasure more to do with replacing type parameters with 
'ordinary classes' at compile time to support generics?
In fact you can see this in action by virtue of the fact that the 
interface method has a return type of Object

Why do you think type erasure would 'hide' a method from the hierarchy?
In fact AIUI there needs to be two methods 'visible' to the VM as it is 
not possible to determine at compile time which one will be used at 
runtime, hence 'runtime', or 'dynamic' binding.

http://docs.oracle.com/javase/tutorial/java/generics/erasure.html

Or maybe I'm missing something.


-- 
Not floundering ... just fishing

[toc] | [prev] | [next] | [standalone]


#31328

FromSteven Simpson <ss@domain.invalid>
Date2015-03-17 14:24 +0000
Message-ID<o1qltb-jt8.ln1@s.simpson148.btinternet.com>
In reply to#31326
On 16/03/15 14:44, rupertlssmith@googlemail.com wrote:
> class Test implements ITest<Integer> {
>      public Integer someMethod() {
>          return 1;
>      }
> }
>
> interface ITest<T> {
>      T someMethod();
> }
>
> And got this output:
>
> public java.lang.Integer org.mygovscot.stars.client.Test.someMethod()
> public java.lang.Object org.mygovscot.stars.client.Test.someMethod()
> (... other methods on Object)

(I'm confused about your problem now, because I thought you didn't have 
anything explicitly implementing your interface...)

In the invocation handler, can you not look up the method on your target 
object?  The simple way would be:

   obj.getClass().getMethod(method.getName(), method.getParameterTypes())

However, it might not find a method if your interface declares:

   void myMethod(String s);

...while the class declares:

   void myMethod(T s); // in MyClass<T>

...and the instance is a MyClass<String>.

So, more generally, can you do your own look-up along these lines?:

     private static Method findMethod(Class<?> clazz,
                                      String name, Class<?>[] types)
         throws NoSuchMethodException {
         next_method:
         for (Method meth : clazz.getMethods()) {
             if (!meth.getName().equals(name)) continue;
             if (meth.getParameterCount() != types.length) continue;
             Class<?>[] params = meth.getParameterTypes();
             for (int i = 0; i < types.length; i++) {
                 if (!params[i].isAssignableFrom(types[i]))
                     continue next_method;
             }
             return meth;
         }
         throw new NoSuchMethodException();
     }



-- 
ss at comp dot lancs dot ac dot uk

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.java.programmer


csiph-web