8 Replies Latest reply on Sep 18, 2010 7:08 PM by marcelkolsteren

    Intercepting beans created by weld-se using constructor based injection

    staenker
      Hi,
      my name is Richard, I'm pretty new to weld and currently trying to write a weld-se builder to make testing easy. I'm currently testing the stuff. Injection works fine but I noticed a problem while testing interception: If I try to intercept a bean that is created using a @Inject annotated constructor with multiple arguments I get the following Exception:
      org.jboss.weld.exceptions.DeploymentException: de.rhauswald.weld.test.mock.org$jboss$weld$bean-classpath-ManagedBean-class_de$rhauswald$weld$test$mock$ConstructorBasedInjectionService_$$_WeldProxy
           at org.jboss.weld.bean.ManagedBean.applyInterceptors(ManagedBean.java:600)
           at org.jboss.weld.bean.ManagedBean$ManagedBeanInjectionTarget.produce(ManagedBean.java:256)
           at org.jboss.weld.bean.ManagedBean.create(ManagedBean.java:338)
           at org.jboss.weld.context.DependentContext.get(DependentContext.java:62)
           at org.jboss.weld.manager.BeanManagerImpl.getReference(BeanManagerImpl.java:660)
           at org.jboss.weld.manager.BeanManagerImpl.getReference(BeanManagerImpl.java:698)
           at org.jboss.weld.bean.builtin.InstanceImpl.get(InstanceImpl.java:79)
           at de.rhauswald.weld.test.TestWeldInterceptorTest.produce(TestWeldInterceptorTest.java:75)
           at de.rhauswald.weld.test.TestWeldInterceptorTest.testTestWeldInterceptsConstructorInjectedService(TestWeldInterceptorTest.java:47)
           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.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
           at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
           at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
           at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
           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.junit.runners.ParentRunner.run(ParentRunner.java:236)
           at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
           at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
           at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
           at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
           at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
           at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
      Caused by: org.jboss.weld.exceptions.DefinitionException: de.rhauswald.weld.test.mock.org$jboss$weld$bean-classpath-ManagedBean-class_de$rhauswald$weld$test$mock$ConstructorBasedInjectionService_$$_WeldProxy
           at org.jboss.weld.bean.proxy.ProxyFactory.create(ProxyFactory.java:223)
           at org.jboss.weld.bean.ManagedBean.applyInterceptors(ManagedBean.java:594)
           ... 30 more
      Caused by: java.lang.InstantiationException: de.rhauswald.weld.test.mock.org$jboss$weld$bean-classpath-ManagedBean-class_de$rhauswald$weld$test$mock$ConstructorBasedInjectionService_$$_WeldProxy
           at java.lang.Class.newInstance0(Class.java:340)
           at java.lang.Class.newInstance(Class.java:308)
           at org.jboss.weld.util.reflection.SecureReflections$16.work(SecureReflections.java:396)
           at org.jboss.weld.util.reflection.SecureReflectionAccess.run(SecureReflectionAccess.java:54)
           at org.jboss.weld.util.reflection.SecureReflectionAccess.runAsInstantiation(SecureReflectionAccess.java:216)
           at org.jboss.weld.util.reflection.SecureReflections.newInstance(SecureReflections.java:391)
           at org.jboss.weld.bean.proxy.ProxyFactory.create(ProxyFactory.java:218)
           ... 31 more

      This is not the case if I intercept a bean with a default constructor using field based injection. Is this a bug, a feature or did I made a mistake?

      Thanks,
      Richard
        • 1. Re: Intercepting beans created by weld-se using constructor based injection
          marcelkolsteren

          Hi Richard,


          Interesting problem. I think that the interceptor should work in both situations. I created an Arquillian test that shows a working interceptor in both situations: where the target bean uses field injection, and where it uses constructor injection:



          public class InterceptorTarget1
          {
             @Inject
             private TestBean1 bean1;
          
             @Inject
             private TestBean2 bean2;
          
             @TestInterceptorBinding
             public String getBeanContents()
             {
                return bean1.getContent() + ", " + bean2.getContent();
             }
          }




          public class InterceptorTarget2
          {
             private TestBean1 bean1;
          
             private TestBean2 bean2;
          
             @Inject
             public InterceptorTarget2(TestBean1 bean1, TestBean2 bean2)
             {
                this.bean1 = bean1;
                this.bean2 = bean2;
             }
          
             @TestInterceptorBinding
             public String getBeanContents()
             {
                return bean1.getContent() + ", " + bean2.getContent();
             }
          }



          I attached the Arquillian test as a Maven project: just unpack it and run 'mvn test'.


          Maybe you use another version of Weld, or maybe there are some other differences that explain why your test fails. I hope that this simple test will help you with the further analysis.

          • 2. Re: Intercepting beans created by weld-se using constructor based injection
            marcelkolsteren

            Ok, I found out that attachments are not possible on this forum. You can download the Maven project here.

            • 3. Re: Intercepting beans created by weld-se using constructor based injection
              staenker

              Hi Marcel,
              thanks for your answer I'll download the sample and have a look on it. I also uploaded my Project to google code: http://tnfstacc-code.googlecode.com/svn/trunk/weld.test/weld-test.zip
              You can also check out the sources(http://tnfstacc-code.googlecode.com/svn/trunk/weld.test/) if thats your preferred way. Have a look at TestWeldInterceptorTest . Maybe you can find a mistake of mine. Maybe it is not important, but I am using weld-se.


              Thanks again,
              Richard

              • 4. Re: Intercepting beans created by weld-se using constructor based injection
                staenker

                Surprise Surprise... your test is working fine :-) I copied your classes to the weld.test project(I commited to http://tnfstacc-code.googlecode.com/svn/trunk/weld.test/) and ran the tests again - USING weld-se environment. One is working and one fails. So the same problem with your classes. This makes me think that this is related to weld-se.


                Any thoughts?
                Richard

                • 5. Re: Intercepting beans created by weld-se using constructor based injection
                  marcelkolsteren

                  I think it has something to do with the Weld version. I tested with 1.0.1.Final, while you're using 1.1.0.Beta1. The problem has to do with the interceptor proxy generation. In 1.0.1.Final, the proxy generation was quite different than in 1.1.0.Beta1. My guess is that in 1.1.0.Beta1, the generated proxy doesn't have a public no-arg constructor (for the constructor injection case), so that newInstance() on the proxy results in an exception.


                  I'm trying to run my tests on 1.1.0.Beta1, but didn't succeed yet. Not sure whether Arquillian supports that version.

                  • 6. Re: Intercepting beans created by weld-se using constructor based injection
                    marcelkolsteren

                    I'm quite sure now that you found a bug in Weld 1.1.0.Beta1. I changed my earlier example so that it only tests interception of a method call targeted at a bean that uses constructor injection. The Maven test project, which can be downloaded here, has two profiles:




                    • weld-se-1.0 for running with Weld 1.0.1-SP1

                    • weld-se-1.1 for running with Weld 1.1.0.Beta1



                    Tests can be run as follows:


                    mvn test -Pweld-se-1.0
                    mvn test -Pweld-se-1.1



                    The 1.0 test succeeds, but the 1.1 test fails, clearly indicating a regression in Weld. Most probably this has to do with the refactoring of the proxy layer.


                    Next step would be to submit an issue to JIRA, but only after a Weld developer has had a look at this thread.

                    • 7. Re: Intercepting beans created by weld-se using constructor based injection
                      swd847

                      Can you retry with the following empty file in your archive:


                      META-INF/org.jboss.weld.enableUnsafeProxies
                      



                      This will allow for the proxying of classes that do not have a default constructor. If you use the very latest snapshot of weld it will also allow you to proxy classes that only have private constructors.


                      Looking at this I think the problem should be detected at deployment time, however at some point in the near future the interceptors and decorators implementation is going to be changed to use subclassing rather than proxying, and this should not be an issue any more.

                      • 8. Re: Intercepting beans created by weld-se using constructor based injection
                        marcelkolsteren

                        Thanks Stuart, that fixes the problem!


                        Don't know if I understand what you mean with the detection of the problem at deployment time. You mean that Weld would detect the situation described in this thread? And then inform the developer, that because of a bug in Weld, he needs to add META-INF/org.jboss.weld.enableUnsafeProxies in order to make the application behave in a spec-compliant way?