3 Replies Latest reply on May 20, 2019 4:07 PM by arielcarrera

    InterceptionFactory from an Interface

    arielcarrera

      Hi, I'm trying to create a Cdi proxy from an Interface but when I get the methods from InterceptionFactory configuration it only returns "default" methods.

       

      Is this the expected behavior?

       

      It is filtered at class BackedAnnotatedMethods:

       

          private class BackedAnnotatedMethods extends EagerlyInitializedLazyValueHolder<Set<AnnotatedMethod<? super X>>> {

              @Override

              protected Set<AnnotatedMethod<? super X>> computeValue() {

                  ImmutableSet.Builder<AnnotatedMethod<? super X>> methods = ImmutableSet.builder();

                  Class<? super X> clazz = javaClass;

                  while (clazz != Object.class && clazz != null) {

                      for (Method method : SecurityActions.getDeclaredMethods(clazz)) {

                          methods.add(BackedAnnotatedMethod.of(method, BackedAnnotatedType.this, sharedObjectCache));

                      }

                      clazz = clazz.getSuperclass();

                  }

                  // Also add default methods

                  for (Class<?> interfaceClazz : Reflections.getInterfaceClosure(javaClass)) {

                      for (Method method : SecurityActions.getDeclaredMethods(interfaceClazz)) {

                         if (Reflections.isDefault(method)) {

                              methods.add(BackedAnnotatedMethod.of(method, BackedAnnotatedType.this, sharedObjectCache));

                          }

                      }

                  }

                  return methods.build();

              }

          }

       

      My use case is try to integrate Spring Data with CDI and intercept Transactional annotations from methods. Spring Data CDI Extension puts a Spring Proxy instance into CDI and I need to proxy it with CDI to Intercept or copy method annotations from Spring Data Repository Interfaces to the Weld proxy generated by InterceptionFactory.

       

      Thanks

        • 1. Re: InterceptionFactory from an Interface
          arielcarrera

          After read chapter "11.7. The InterceptionFactory interface" of "CDI 2.0 Spec", I think that it could be an unexpected behavior or bug for Interfaces support in InterceptionFactory.

           

          I know that CDI spec says that "If an injection point of type InterceptionFactory has a type parameter that is not a Java class, nonportable behavior results." but if Weld will support Interface proxies I think that all methods from interface should be returned unless you call "ignoreFinalMethods()" method.

          • 2. Re: InterceptionFactory from an Interface
            manovotn

            Hello,

             

            first of all, CDI doesn't require any support for InterceptionFactory on interfaces, this is, at the moment, purely Weld feature.

            It is also stated in Weld docs - here.

             

            Now, the actual point of allowing it on interfaces is to bypass unproxyable implementations that user cannot control/change.

            As WELD-2550 issue says in description, it behaves slightly differently from standard interception factory in a way that it only takes into consideration annotations directly on the interface. E.g if you have a class-level interceptor binding on the interceptor (or add it via configurator), it will only intercept methods this interface has; it will completely ignore any methods added by implementing class. This is what allows us to create a proxy from interface as all interface methods are by definition proxyable (non-final to start with).

             

            I am not sure I understand your use case, could you put together a simple reproducer? Ideally just with CDI, showing what you expect to have intercepted for certain hierarchy of classes and interfaces?

            • 3. Re: InterceptionFactory from an Interface
              arielcarrera

              Hello Matěj, it's correct, as you say I know that the support of Interfaces with InterceptionFactory is a functionality of Weld and the behavior is not portable.

               

              I understand that this functionality is targeted to bypass unproxyable implementations that user can not control / change.

               

              After read Weld core and with a minimum set of tests I also understand why it only includes the "default" methods of the interface since otherwise it would end in an InvocationTargetException or similar.

               

              Now, in certain situations the object that is wanted to put in the context of CDI is an object that is already a dynamic proxy (jdk, javassist / cglib, etc ...) as it happens with spring AOT objects.

               

              In my case I try to integrate a JavaEE project with "Spring Data Jpa". Spring Data Jpa is built with a CDI extension but it does not provide support for Transactions, fully delegating the transactions to the service layer and that is cumbersome.

               

              If CDI or Weld would support to intercept this kind of objects completely, the integration with this type of technologies could be easy.

               

              In this case I was able to solve the problem without using a CDI proxy, modifying the code of the Spring extension to allow me to add my own transaction interceptor, but this takes a greater effort compared to with the creation of a new CDI proxy (often the penalty is supported).

              Added to this, in many opportunities the third party code is not modifiable so it's not a viable alternative and you ends up implementing a custom proxy.

               

              I have two projects on github that you can check:

              - The first one has weld / cdi tests and interceptors https://github.com/arielcarrera/cdi-interception-factory-test

              - The second has weld / cdi tests with jta, jpa and spring data jpa. https://github.com/arielcarrera/cdi-spring-data-jpa-test

               

              In the first one you can run the tests of the class "InterceptionDynamicProxyTest" and you will see two tests that already fail when the proxy interface has annotated methods or when they are added through the InterceptionFactory.

               

              In the second project you can see how I had to solve the problem by creating my own interceptor, replicating transactional interceptor logic (:S), making it compatible with Spring's proxy and adding it to the CDI Extension. It also include some minor changes to Spring Data to discuss with they.

               

              Regards!