7 Replies Latest reply on Mar 19, 2019 2:58 AM by Matěj Novotný

    Interception question

    Laird Nelson Expert

      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
          Laird Nelson Expert

          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
            Martin Kouba Master

            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
              Matěj Novotný Novice

              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
                Laird Nelson Expert

                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
                  Martin Kouba Master
                  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
                    Matěj Novotný Novice

                    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.