Uploaded image for project: 'Aries'
  1. Aries
  2. ARIES-1342

Aries Proxy Impl fails to proxy a service with covariant type hierarchy in Java 8

    XMLWordPrintableJSON

Details

    • Bug
    • Status: Resolved
    • Critical
    • Resolution: Fixed
    • proxy-impl-1.0.3
    • proxy-impl-1.0.5
    • Blueprint
    • Karaf 3.0.3/Karaf 4.0.0, JDK 1.8.0_u45

    • Important

    Description

      I have a simple type hierarchy with a base interface and an extending interface with method overriding (covariant return types). A service implements the derived interface (ColumnDAO - see attached test case). The attached test case illustrates the scenario. In certain situations AriesProxy Impl (more specifically the InterfaceCombiningClassAdapter) fails to properly synthesize the proxy code. In particular it is a combination of the lexical naming of the classes in the hierarchy and Java 8 method access flags that does it. The naming of the classes determines the order in which they are processed since the ProxyClassLoader receives a sorted set of classes when building the proxy. If that order happens to be such that the types are processed in hierarchy order starting with the base type, then all is cool. If not then trouble arises.
      Why ? Well if a more derived type is processed then it instruments base methods which are marked (in Java 8) with Synthetic and Bridge access flags. In this case the visitMethod() in AbstractWovenProxyAdapter does not generate any code but records the fact that this method has been visited. When it subsequently visits the base type, the methods are skipped since they are considered already visited.

      In the test case running javap -verbose ColumnDAO.class yields the following (note the base type is named ZanyDAO to force the lexical ordering and thus the error) :

      public abstract org.deklanowski.aries.dao.ColumnBatch<R, C, V> prepareBatch();
          flags: ACC_PUBLIC, ACC_ABSTRACT
          Signature: #9                           // ()Lorg/deklanowski/aries/dao/ColumnBatch<TR;TC;TV;>;
      
        public abstract org.deklanowski.aries.dao.ColumnQuery<R, C, V> createQuery();
          flags: ACC_PUBLIC, ACC_ABSTRACT
          Signature: #12                          // ()Lorg/deklanowski/aries/dao/ColumnQuery<TR;TC;TV;>;
      
        public org.deklanowski.aries.dao.Batch prepareBatch();
          flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
          Code:
            stack=1, locals=1, args_size=1
               0: aload_0
               1: invokeinterface #1,  1            // InterfaceMethod prepareBatch:()Lorg/deklanowski/aries/dao/ColumnBatch;
               6: areturn
            LineNumberTable:
              line 3: 0
            LocalVariableTable:
              Start  Length  Slot  Name   Signature
                     0       7     0  this   Lorg/deklanowski/aries/dao/ColumnDAO;
            LocalVariableTypeTable:
              Start  Length  Slot  Name   Signature
                  0       7     0  this   Lorg/deklanowski/aries/dao/ColumnDAO<TR;TC;TV;>;
      
        public org.deklanowski.aries.dao.Query createQuery();
          flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
          Code:
            stack=1, locals=1, args_size=1
               0: aload_0
               1: invokeinterface #2,  1            // InterfaceMethod createQuery:()Lorg/deklanowski/aries/dao/ColumnQuery;
               6: areturn
            LineNumberTable:
              line 3: 0
            LocalVariableTable:
              Start  Length  Slot  Name   Signature
                     0       7     0  this   Lorg/deklanowski/aries/dao/ColumnDAO;
            LocalVariableTypeTable:
              Start  Length  Slot  Name   Signature
                  0       7     0  this   Lorg/deklanowski/aries/dao/ColumnDAO<TR;TC;TV;>;
      

      The logic in the aforementioned AbstractWovenProxyAdapter.visitMethod() on line 341 is as follows:

          if ((access & (ACC_STATIC | ACC_PRIVATE | ACC_SYNTHETIC 
              | ACC_NATIVE | ACC_BRIDGE)) == 0 && !!!name.equals("<init>") && 
              !!!name.equals("<clinit>")) {
      <snip>
      

      The if statement evaluates to false and no code is generated though the methods are recorded as having been visited.

      Attachments

        1. ARIES-1342.patch
          1 kB
          Łukasz Dywicki
        2. aries-proxy-issue.rar
          24 kB
          Declan Cox

        Issue Links

          Activity

            People

              gnodet Guillaume Nodet
              declancox Declan Cox
              Votes:
              3 Vote for this issue
              Watchers:
              5 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved: