12 Replies Latest reply on Jul 25, 2012 11:55 AM by Marko Lukša

    Question about interceptors

    Ron Sigal Master

      Hi,

       

      I wrote a test for Resteasy in which interceptors are applied by way of 1) @Interceptors and 2) CDI annotations, and I've gotten what looks like an anomalous behavior.  As I understand the specs, the following rules apply:

       

      1. Interceptors applied by way of @Interceptors are ordered by the rules


         a) class interceptors execute before method interceptors

         b) within each category, interceptors are ordered by their order in the @Interceptor annotation (or the xml file)

       

      2. Interceptors applied by way of CDI annotations are ordered according to their order in beans.xml, regardless of their placement on class or method.

       

      3. According to the CDI spec, "Interceptors declared using @Interceptors or in ejb-jar.xml are called before interceptors using interceptor bindings.

       

      I have an interceptor binding

       

      @InterceptorBinding

      @Target({TYPE, METHOD})

      @Retention(RUNTIME)

      public @interface TestBinding

      {

         String placement() default "CLASS";

      }

       

      My JAX-RS resource looks like

       

      @Path("/")

      @RequestScoped

      @Interceptors ({Interceptor0.class})

      @TestBinding(placement="CLASS")

      public class InterceptorResource

         @Inject private Logger log;

       

         @POST

         @Path("test")

         @Interceptors ({Interceptor1.class})

         @TestBinding(placement="METHOD")

         public Response test()

         { ...

       

      My interceptors, somewhat abbreviated, are

       

      public class Interceptor0

      {

         @AroundInvoke

         public Object intercept(InvocationContext ctx) throws Exception

         {

            log.info("*** Intercepting call to Interceptor0.intercept()");

            Object result = ctx.proceed();

            return result;

         }

      }

       

      public class Interceptor1

      {

         @AroundInvoke

         public Object intercept(InvocationContext ctx) throws Exception

         {

            log.info("*** Intercepting call to Interceptor1.intercept()");

            Object result = ctx.proceed();

            return result;

         }

      }

       

      @Interceptor

      @TestBinding(placement="CLASS")

      public class Interceptor2

      {

         @AroundInvoke

         public Object intercept(InvocationContext ctx) throws Exception

         {

            log.info("*** Intercepting call to Interceptor2.intercept()");

            Object result = ctx.proceed();

            return result;

         }

      }

       

      @Interceptor

      @TestBinding(placement="METHOD")

      public class Interceptor3

      {

         @AroundInvoke

         public Object intercept(InvocationContext ctx) throws Exception

         {

            log.info("*** Intercepting call to Interceptor3.intercept()");

            Object result = ctx.proceed();

            return result;

         }

      }

       

      and beans.xml is

       

      <beans xmlns="http://java.sun.com/xml/ns/javaee"

      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">

      <interceptors>

      <class>org.jboss.resteasy.cdi.interceptors.Interceptor2</class>

      <class>org.jboss.resteasy.cdi.interceptors.Interceptor3</class>

      </interceptors>

      </beans>

       

      So, Interceptor0 and Interceptor1 are applied by way of the @Interceptors annotation, and Interceptor2 and Interceptor3 are applied by way of the @TestBinding annotation.  The output includes

       

      16:40:59,606 INFO  [org.jboss.resteasy.cdi.interceptors.Interceptor0] (http--127.0.0.1-8080-3) *** Intercepting call to Interceptor0.intercept()

      16:40:59,606 INFO  [org.jboss.resteasy.cdi.interceptors.Interceptor2] (http--127.0.0.1-8080-3) *** Intercepting call to Interceptor2.intercept()

      16:40:59,607 INFO  [org.jboss.resteasy.cdi.interceptors.Interceptor3] (http--127.0.0.1-8080-3) *** Intercepting call to Interceptor3.intercept()

      16:40:59,607 INFO  [org.jboss.resteasy.cdi.interceptors.Interceptor1] (http--127.0.0.1-8080-3) *** Intercepting call to Interceptor1.intercept()

       

      Some of this ordering makes sense:

       

      * Interceptor0 appears before Interceptor1 by rule 1a above.

      * Interceptor2 appears before Interceptor3 by rule 2 above.

       

      But the appearance of Interceptor2 and Interceptor3 before Interceptor1 seems to violate rule 3.  I expected them to be ordered: Interceptor0, Interceptor1, Interceptor2, Interceptor3.

       

      Am I missing something?  Have I done something wrong?

       

      Thanks,

      Ron

        • 1. Re: Question about interceptors
          Ales Justin Master

          Can you provide an Arquillian test for this?

          • 2. Re: Question about interceptors
            Ron Sigal Master

            Hi Ales,

             

            Yep.  I just attached resteasy-cdi-ejb-test.tgz, which contains an eclipse project.  The particular test is org.jboss.resteasy.test.cdi.interceptors.InterceptorTest, and it uses arquillian.

             

            Thanks,

            Ron

            • 3. Re: Question about interceptors
              Ales Justin Master

              What about rewriting it as Weld' tests-arquillian/ test and doing a pull-request? ;-)

              • 4. Re: Question about interceptors
                Ron Sigal Master

                Ok, I cloned weld/core, added

                 

                * org.jboss.resteasy.cdi.interceptors to core/tests-arquillian/src/main/java,

                * org.jboss.resteasy.test.cdi.interceptors to core/tests-arquillian/src/test/java

                * interceptorBeans.xml to core/tests-arquillian/src/test/resources

                 

                and

                 

                <dependency>

                      <groupId>org.jboss.resteasy</groupId>

                      <artifactId>resteasy-jaxrs</artifactId>

                      <version>2.3.4.Final</version>

                </dependency>

                 

                to core/tests-arquillian/pom.xml and generated a pull request.  Is that what you meant?

                 

                Now, when I run it, I get

                 

                org.jboss.weld.exceptions.DeploymentException: WELD-000048 Conflicting interceptor bindings found on class org.jboss.resteasy.cdi.interceptors.InterceptorResource.test()

                 

                Hmmm.

                • 5. Re: Question about interceptors
                  Ron Sigal Master

                  I'm thinking you probably don't want the JAX-RS stuff in there.  If want to keep the test, I can replace the JAX-RS resource with an EJB, but for now it's something to play with.

                  • 6. Re: Question about interceptors
                    Marko Lukša Apprentice

                    Ron, you get the "Conflicting interceptor bindings found" exception because InterceptorResource.test() has two interceptor bindings of the same type (TestBinding) but with different member values ("CLASS" and "METHOD").

                     

                    From http://docs.jboss.org/cdi/spec/1.0/html_single/#interceptorbindings :

                     

                    If the set of interceptor bindings of a bean or interceptor, including bindings inherited from stereotypes and other interceptor bindings, has two instances of a certain interceptor binding type and the instances have different values of some annotation member, the container automatically detects the problem and treats it as a definition error.

                    • 7. Re: Question about interceptors
                      Ron Sigal Master

                      Hi Marko,

                       

                      Thank you for that insight.  Curiously, I've run my original version of the same test with AS 7.1.1.Final and it hasn't complained.

                       

                      -Ron

                      • 8. Re: Question about interceptors
                        Marko Lukša Apprentice

                        hmm, yeah, that's interesting...

                         

                        Anyway, I think I've found the reason for the incorrect order of interceptor invocations. Let me just clean the code up a little and I'll push the fix to my github repo.

                        • 9. Re: Question about interceptors
                          Ron Sigal Master

                          That's great, Marko. Thank you for looking into it.

                          • 10. Re: Question about interceptors
                            Marko Lukša Apprentice

                            OK, the fix is at https://github.com/luksa/weld-core/tree/weld1174

                             

                            I'm not a 100% sure this will fix the problem you're facing, as I removed the resteasy stuff from your test. But I did get the same incorrect interceptor order: 0, 2, 3, 1.

                             

                            Please test your original test against my branch and if it passes I'll ask Aleš to integrate my fix into master.

                            • 11. Re: Question about interceptors
                              Ron Sigal Master

                              Hi Marko,

                               

                              I've used your updated weld-core jar with my version of the test, and it works.  The interceptors get called in order: Interceptor0, Interceptor1, Interceptor2, Interceptor3.  Yay.

                               

                              1. I was able to build and install weld-core, but when I ran it I got

                               

                              java.lang.Error: Unresolved compilation problem:

                                  The method produce(CreationalContext<T>) of type AbstractProducerBean<X,T,S>.AbstractProducer must override a superclass method

                               

                                  at org.jboss.weld.bean.AbstractProducerBean$AbstractProducer.produce(AbstractProducerBean.java:315)

                                  at org.jboss.weld.bean.AbstractProducerBean.create(AbstractProducerBean.java:307)

                                  at org.jboss.weld.context.unbound.DependentContextImpl.get(DependentContextImpl.java:68)

                                  at org.jboss.weld.manager.BeanManagerImpl.getReference(BeanManagerImpl.java:635)

                                  at org.jboss.weld.manager.BeanManagerImpl.getReference(BeanManagerImpl.java:700)

                                  at org.jboss.weld.injection.FieldInjectionPoint.inject(FieldInjectionPoint.java:136)

                                  at org.jboss.weld.util.Beans.injectBoundFields(Beans.java:686)

                                  at org.jboss.weld.util.Beans.injectFieldsAndInitializers(Beans.java:695)

                                  at org.jboss.weld.manager.SimpleInjectionTarget$1.proceed(SimpleInjectionTarget.java:106)

                                  at org.jboss.weld.injection.InjectionContextImpl.run(InjectionContextImpl.java:48)

                                  at org.jboss.weld.manager.SimpleInjectionTarget.inject(SimpleInjectionTarget.java:109)

                                  at org.jboss.arquillian.testenricher.cdi.CDIInjectionEnricher.injectNonContextualInstance(CDIInjectionEnricher.java:145)

                                  at org.jboss.arquillian.testenricher.cdi.CDIInjectionEnricher.injectClass(CDIInjectionEnricher.java:125)

                                  at org.jboss.arquillian.testenricher.cdi.CDIInjectionEnricher.enrich(CDIInjectionEnricher.java:78)

                                  at org.jboss.arquillian.test.impl.TestInstanceEnricher.enrich(TestInstanceEnricher.java:52)

                                  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

                                  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)

                                  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)

                                  at java.lang.reflect.Method.invoke(Method.java:597)

                                  at org.jboss.arquillian.core.impl.ObserverImpl.invoke(ObserverImpl.java:90)

                                  at org.jboss.arquillian.core.impl.EventContextImpl.invokeObservers(EventContextImpl.java:99)

                                  at org.jboss.arquillian.core.impl.EventContextImpl.proceed(EventContextImpl.java:81)

                                  at org.jboss.arquillian.test.impl.TestContextHandler.createTestContext(TestContextHandler.java:89)

                                  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

                                  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)

                                  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)

                                  at java.lang.reflect.Method.invoke(Method.java:597)

                                  at org.jboss.arquillian.core.impl.ObserverImpl.invoke(ObserverImpl.java:90)

                                  at org.jboss.arquillian.core.impl.EventContextImpl.proceed(EventContextImpl.java:88)

                                  at org.jboss.arquillian.test.impl.TestContextHandler.createClassContext(TestContextHandler.java:75)

                                  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

                                  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)

                                  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)

                                  at java.lang.reflect.Method.invoke(Method.java:597)

                                  at org.jboss.arquillian.core.impl.ObserverImpl.invoke(ObserverImpl.java:90)

                                  at org.jboss.arquillian.core.impl.EventContextImpl.proceed(EventContextImpl.java:88)

                                  at org.jboss.arquillian.test.impl.TestContextHandler.createSuiteContext(TestContextHandler.java:60)

                                  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

                                  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)

                                  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)

                                  at java.lang.reflect.Method.invoke(Method.java:597)

                                  at org.jboss.arquillian.core.impl.ObserverImpl.invoke(ObserverImpl.java:90)

                                  at org.jboss.arquillian.core.impl.EventContextImpl.proceed(EventContextImpl.java:88)

                                  at org.jboss.arquillian.core.impl.ManagerImpl.fire(ManagerImpl.java:135)

                                  at org.jboss.arquillian.core.impl.ManagerImpl.fire(ManagerImpl.java:115)

                                  at org.jboss.arquillian.test.impl.EventTestRunnerAdaptor.before(EventTestRunnerAdaptor.java:95)

                                  at org.jboss.arquillian.junit.Arquillian$4.evaluate(Arquillian.java:222)

                                  at org.jboss.arquillian.junit.Arquillian.multiExecute(Arquillian.java:314)

                                  at org.jboss.arquillian.junit.Arquillian.access$100(Arquillian.java:46)

                                  at org.jboss.arquillian.junit.Arquillian$5.evaluate(Arquillian.java:240)

                                  at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)

                                  at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)

                                  at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)

                                  at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)

                                  at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)

                                  at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)

                                  at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)

                                  at org.jboss.arquillian.junit.Arquillian$2.evaluate(Arquillian.java:185)

                                  at org.jboss.arquillian.junit.Arquillian.multiExecute(Arquillian.java:314)

                                  at org.jboss.arquillian.junit.Arquillian.access$100(Arquillian.java:46)

                                  at org.jboss.arquillian.junit.Arquillian$3.evaluate(Arquillian.java:199)

                                  at org.junit.runners.ParentRunner.run(ParentRunner.java:236)

                                  at org.jboss.arquillian.junit.Arquillian.run(Arquillian.java:147)

                                  at org.junit.runner.JUnitCore.run(JUnitCore.java:157)

                                  at org.junit.runner.JUnitCore.run(JUnitCore.java:136)

                                  at org.jboss.arquillian.junit.container.JUnitTestRunner.execute(JUnitTestRunner.java:65)

                                  at org.jboss.arquillian.protocol.servlet.runner.ServletTestRunner.executeTest(ServletTestRunner.java:160)

                                  at org.jboss.arquillian.protocol.servlet.runner.ServletTestRunner.execute(ServletTestRunner.java:126)

                                  at org.jboss.arquillian.protocol.servlet.runner.ServletTestRunner.doGet(ServletTestRunner.java:90)

                                  at javax.servlet.http.HttpServlet.service(HttpServlet.java:734)

                                  at javax.servlet.http.HttpServlet.service(HttpServlet.java:847)

                                  at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:329)

                                  at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248)

                                  at org.jboss.weld.servlet.ConversationPropagationFilter.doFilter(ConversationPropagationFilter.java:62)

                                  at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:280)

                                  at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248)

                                  at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:275)

                                  at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:161)

                                  at org.jboss.as.web.security.SecurityContextAssociationValve.invoke(SecurityContextAssociationValve.java:153)

                                  at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:155)

                                  at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)

                                  at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)

                                  at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:368)

                                  at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:877)

                                  at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:671)

                                  at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:930)

                                  at java.lang.Thread.run(Thread.java:662)

                               

                              When I commented @Override on line 314 of AbstractProducerBean.java, it worked.

                               

                              2. With the new version, I got the complaint about "Conflicting interceptor bindings found" again, so that's good.

                               

                              3. Can I ask you a git question for the git impaired?  Why did "git checkout weld1174" not give me your version, but "git checkout 5bf7ac042e6d7d50f86ce852cb44c78eb6e9c8" did?  If this is a silly question, don't bother answering. 

                               

                              Thanks for getting this issue fixed so quickly.

                               

                              -Ron

                              • 12. Re: Question about interceptors
                                Marko Lukša Apprentice

                                I'm not a git expert, but I think it has something to do with tracking remote branches. If I remember correctly, they are not always tracked by default.

                                 

                                You can do something like: git branch weld1174 --track luksa_remote/weld1174

                                And then you'd be able to do git checkout weld1174

                                 

                                But sometimes this is done automatically by git. I'm not sure when, though.