8 Replies Latest reply on May 2, 2019 8:43 AM by manovotn

    Interception question

    ljnelson

      Suppose I have a situation like this:

       

      @ApplicationScoped

      public class Foo {

        private static void onStartup(@Observes @Initialized(ApplicationScoped.class) final Object event) {

           this.frobnicate();

        }

        

        @Transactional

        private void frobnicate() {

        

        }

      }

       

      …where @Transactional is an interceptor binding.

       

      Should the interceptor designated by the @Transactional annotation fire?

       

      Or must I do something like this instead:

       

      @ApplicationScoped

      public class Foo {

        private static void onStartup(@Observes @Initialized(ApplicationScoped.class) final Object event,

                                      final Foo self) {

          self.frobnicate();

        }

       

        @Transactional

        private void frobnicate() {

       

        }

      }

       

      Thanks,

      Best,

      Laird

        • 1. Re: Interception question
          ljnelson

          Oh, this is interesting.

           

          This is slightly different from the case above, but I think I've found some strange behavior in Weld 3.0.5.Final.  I'm not sure which of the two scenarios below is supposed to work and which is not.

           

          In a test case I have that hopefully I'll be able to package up and redistribute shortly, this works:

           

          @ApplicationScoped

          public class Foo {

           

            @Inject

            private UserTransaction userTransaction;

           

            // Note final:

            private final void onStartup(@Observes @Initialized(ApplicationScoped.class) final Object event)

              throws SystemException {

              assertNotNull(event);

              this.frobnicate();

            }

           

            @Transactional(Transactional.TxType.REQUIRED)

            public void frobnicate() throws SystemException {

              assertNotNull(this.userTransaction);

              assertEquals(Status.STATUS_ACTIVE, this.userTransaction.getStatus());

            }

          }

           

          This does not:

           

          @ApplicationScoped

          public class Foo {

           

            @Inject

            private UserTransaction userTransaction;

           

            // Note not final:

            private void onStartup(@Observes @Initialized(ApplicationScoped.class) final Object event)

              throws SystemException {

              assertNotNull(event);

              this.frobnicate();

            }

           

            @Transactional(Transactional.TxType.REQUIRED)

            public void frobnicate() throws SystemException {

              assertNotNull(this.userTransaction);

              assertEquals(Status.STATUS_ACTIVE, this.userTransaction.getStatus());

            }

          }

           

          Note the lack of final on the onStartup method.  Of course private methods should be final effectively anyway.

           

          On OpenWebBeans both constructs work.

           

          Best,

          Laird

          • 2. Re: Interception question
            mkouba

            Hi Laird,

            I believe that the interceptor should not be invoked because this.frobnicate() is not a business method invocation. However, it's not explicitly defined neither in CDI nor in the Interceptors spec. There is an open spec issue: https://issues.jboss.org/browse/CDI-414.

            • 3. Re: Interception question
              manovotn

              My understanding is same as Martin's.

              It shouldn't be invoked because you are not going through a contextual reference, hence it won't be a business method invocation.

              This, to me, seems fairly well defined in CDI spec 7.2 Container invocations and interception. Although the aforementioned CDI issue has various takes on the interpretation.

              • 4. Re: Interception question
                ljnelson

                mkouba  wrote:

                 

                Hi Laird,

                I believe that the interceptor should not be invoked because this.frobnicate() is not a business method invocation. However, it's not explicitly defined neither in CDI nor in the Interceptors spec. There is an open spec issue: https://issues.jboss.org/browse/CDI-414.

                Thanks, Martin.  Since control has entered the ApplicationScoped bean by way of a lifecycle event observer, isn't the this pointer a contextual reference and hence a client proxy?  I think that's OpenWebBeans' position.  Romain Manni-Bucau was emphatic on this point.

                 

                Regardless, it sounds like you're saying any usage of this.frobnicate() is by definition not a business method invocation, and that if interception occurs it is merely by chance, and I understand why you would say this.  So one of the cases above would be a bug in Weld, namely the case that "works".  Should I file an issue to track this?

                 

                Certainly given that I can just inject a self in a static observer method instead, obviously that's the safest thing to do and I understand that.

                 

                Nevertheless, I am still curious why the mere presence of final on a private instance method serving as an observer method makes this work, whereas removing final makes it not work.  One would think this change should not make any difference, as all private methods are effectively final anyway.

                • 5. Re: Interception question
                  mkouba
                  Thanks, Martin.  Since control has entered the ApplicationScoped bean by way of a lifecycle event observer, isn't the this pointer a contextual reference and hence a client proxy?  I think that's OpenWebBeans' position.  Romain Manni-Bucau was emphatic on this point.

                  Invocations of observer methods are business method invocations and thus are intercepted. But you would have to annotate the onStartup() method with Transactional to make it work.

                  Nevertheless, I am still curious why the mere presence of final on a private instance method serving as an observer method makes this work, whereas removing final makes it not work.  One would think this change should not make any difference, as all private methods are effectively final anyway.

                  I have no idea but I'd guess it works by coincidence... Hm, it would be really interesting to find out more manovotn

                  • 6. Re: Interception question
                    manovotn

                    mkouba  wrote:

                     

                    Thanks, Martin.  Since control has entered the ApplicationScoped bean by way of a lifecycle event observer, isn't the this pointer a contextual reference and hence a client proxy?  I think that's OpenWebBeans' position.  Romain Manni-Bucau was emphatic on this point.

                    Invocations of observer methods are business method invocations and thus are intercepted. But you would have to annotate the onStartup() method with Transactional to make it work.

                    Nevertheless, I am still curious why the mere presence of final on a private instance method serving as an observer method makes this work, whereas removing final makes it not work.  One would think this change should not make any difference, as all private methods are effectively final anyway.

                    I have no idea but I'd guess it works by coincidence... Hm, it would be really interesting to find out more manovotn 

                    It's got to be some magic around how proxies handle invocations of methods that are marked final. Dumping generated bytecode will be the starting point I suppose.

                    ljnelson feel free to create a JIRA, I'll look into it.

                    • 7. Re: Interception question
                      manovotn
                      • 8. Re: Interception question
                        manovotn

                        ljnelson FYI there is a PR up that aligns the behaviour of private final observer methods with the rest of what Weld does in regards to self-invocation and interception.

                        Link - WELD-2571 Private final observer methods can incorrectly trigger interception via self-invocation by manovotn · Pull Req…

                         

                        Thanks for reporting it!