Uploaded image for project: 'RESTEasy'
  1. RESTEasy
  2. RESTEASY-685

Generic Types don't work with Java Proxy

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Done
    • Icon: Critical Critical
    • 2.3.3.Final
    • 2.3.2.Final
    • jaxrs
    • None

      This worked with 2.2.2.GA, with 2.3.2.Final it is broken. I didn't check the versions in between since we can't use them due to other issues.

      All of our resources are are created with Spring. We are using AOP in Spring for security and other things. In this case we have a first level resource which constructs a sub resource using Spring. Due to the AOP stuff the returned object is a proxy. Additionally the jax-rs annotations are in an interface.

      If one of the methods has a generic parameter, let's say List<Doulbe> RESTeasy 2.3.2.Final will not recognize the generic part (Double) and create a List<String> which causes a java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Double Exception and a 500 - Internal Server Error response.

      Here are some classes to reproduce. Spring is not necessary. Just register TestResource with RESTeasy somehow:

      import java.lang.reflect.InvocationHandler;
      import java.lang.reflect.Method;
      import java.lang.reflect.Proxy;
      
      import javax.ws.rs.Path;
      
      @Path("test")
      public class TestResource {
      
          @Path("sub")
          public TestSubResource getSubResource(){
              return (TestSubResource) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class<?>[]{TestSubResource.class}, new InvocationHandler() {
                  @Override
                  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                      return method.invoke(new TestSubResourceImpl(), args);
                  }
              });
          }
      }
      
      import java.util.List;
      
      import javax.ws.rs.GET;
      import javax.ws.rs.Produces;
      import javax.ws.rs.QueryParam;
      
      public interface TestSubResource {
          @GET
          @Produces("text/plain")
          public String getQueryParams(@QueryParam("param") List<Double> param);
      }
      
      import java.util.List;
      
      public class TestSubResourceImpl implements TestSubResource {
          @Override
          public String getQueryParams(List<Double> params) {
              String ret = "";
              for (Double param : params) {
                  ret += param + "; ";
              }
              return ret;
          }
      }
      

      I've searched around and found some differences in org.jboss.resteasy.core.MethodInjectorImpl.MethodInjectorImpl(Class, Method, ResteasyProviderFactory). The constructor of MethodInjectorImpl is analyzing the method and it's parameter. If there is a generic type it is identified and a corresponding ValueInjector is created. The two lines before the for loop have changed since 2.2.2.GA but that doesn't matter. The constructor has a parameter Class root. With 2.2.2.GA this was the interface with the jax-rs annotations. With 2.3.2.Final it is the Proxy. It seems like the Proxy can't provide generic type information and due to this a List<String> is created instead of List<Doulbe>.

      So I searched why it is the Proxy now and found another difference in org.jboss.resteasy.core.ResourceLocator.invokeOnTargetObject(HttpRequest, HttpResponse, Object):
      2.2.2.GA - line 133: registry.addResourceFactory(null, null, subResourceClass);
      2.3.2.Final - line 134: registry.addResourceFactory(null, null, target.getClass());//subResourceClass);
      Notice in 2.3.2.Final the subResourceClass is still at the end of the line as comment.
      In 2.2.2.GA subResourceClass was assigned by a call to GetRestful.getSubResourceClass(target.getClass()) (line 127).

      I ran a short test where I stopped the processing at the beginning of the constructor of MethodInjectorImpl (line 37) with a debugger. Then I ran root = org.jboss.resteasy.util.GetRestful.getSubResourceClass(root) in the expression evaluator and continued the processing. This worked with 2.3.2.Final.

      So the question is: Why has ResourceLocator.invokeOnTargetObject been changed? Can this be reverted? Or how to deal with Java Proxy?

              patriot1burke@gmail.com Bill Burke (Inactive)
              NvtMorch Holger Morch (Inactive)
              Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

                Created:
                Updated:
                Resolved: