Announcement Announcement Module
Collapse
No announcement yet.
Groovy Support Broken with 1.1-beta-2 Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Groovy Support Broken with 1.1-beta-2

    I am attempting to upgrade to Groovy 1.1 beta into an application that has been using Spring and Groovy 1.0. (I want to be able to use annotations and generics.)

    When attempting to use a Groovy bean that bas been instantiated via the lang:groovy method I'm getting the following error:

    Code:
         [java] java.lang.IllegalArgumentException: object is not an instance of declaring class
         [java]     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
         [java]     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
         [java]     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
         [java]     at java.lang.reflect.Method.invoke(Method.java:585)
         [java]     at org.codehaus.groovy.runtime.metaclass.ReflectionMetaMethod.invoke(ReflectionMetaMethod.java:52)
         [java]     at org.codehaus.groovy.runtime.MetaClassHelper.doMethodInvoke(MetaClassHelper.java:714)
         [java]     at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:583)
         [java]     at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:476)
         [java]     at org.codehaus.groovy.runtime.Invoker.invokePogoMethod(Invoker.java:115)
         [java]     at org.codehaus.groovy.runtime.Invoker.invokeMethod(Invoker.java:81)
         [java]     at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:85)
         [java]     at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:158)
         [java]     at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethod0(ScriptBytecodeAdapter.java:182)
         [java]     at ServiceBeanImpl2.doService(script1185134866212.groovy:13)
         [java]     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
         [java]     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
         [java]     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
         [java]     at java.lang.reflect.Method.invoke(Method.java:585)
         [java]     at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:299)
         [java]     at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:172)
         [java]     at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:139)
         [java]     at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:127)
         [java]     at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:115)
         [java]     at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:161)
         [java]     at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
         [java]     at $Proxy0.doService(Unknown Source)
         [java]     at ServiceConsumerImpl.useService(ServiceConsumerImpl.java:10)
         [java]     at Test.main(Test.java:18)
    That stacktrace is from a test that I wrote to demonstrate the issue, but I get basically the same thing in my full application.

    The issue seems to be when one Groovy bean attempts to access a service on another groovy bean. (ie. Groovy service bean ---depends_on---> Groovy DAO; or Groovy Struts 2 Action ---depends_on----> Groovy Service bean)

    I believe the root of the problem is that each Groovy bean instantiated by Spring has its own classloader.

    I have a test application that demonstrates the issue, and also shows the same exact code working in Groovy 1.0. I apologize that the download is on the large side. If I knew how to do dependencies in Ant I'd have done it.

    http://mark.mjm.net/~mark/springGroovyTest.tar.gz

    To build and run the test simply run ant in the root of the application. In the src-java there is a class Test.java with a main that instantiates a ServiceConsumer, then wires that to a service bean, ServiceBeanImpl2, and calls useService().

    The service beans are two Groovy beans, in src-groovy, that are chained by configuration in resources/applicationContext.xml, ServiceBeanImpl2 to ServiceBeanImpl. ServiceBeanImpl2 simply calls the doService() method of ServiceBeanImpl.

    In the course of the test I output the class loaders of the Groovy service beans. They each have a different GroovyClassLoader. It is my understanding that this is my design in Spring, but it appears to me to be the root of the issue. If I wire all of my Groovy objects together with Class instances loaded with the same GroovyClassLoader it all works as expected.

    Any help on this issue would be greatly appreciated. I've sort of reached the end of what I know about Spring and Groovy.

  • #2
    I have tried the testing program and found that if the "refresh-check-delay" attribute is removed, everything works fine. The only difference created by "refresh-check-delay" attribute is that Spring will create a JDK Proxy instead of the plain groovy object for the groovy lang tag. My guess is that the JDK Proxy and Groovy 1.1 has some incompatible behavior which cause the exception. I have check the source code of JdkDynamicAopProxy class under aop/framework package but so far I still cannot locate the source of problem. Hope anyone can help to solve this problem.

    Cheers
    chris tam
    hong kong

    Comment


    • #3
      Chris,

      I should have emailed you directly. Thanks for digging into this. I just about ran out of mental energy yesterday working on this. I'll take a look at JdkDynamicAopProxy, not that I think I'll get very far, because once we get to Proxies I'm out of my element.

      Thanks,

      Mark

      Comment


      • #4
        OK, a little more investigation turns up this post over on the Groovy User list:

        http://www.nabble.com/using-MOP-to-i....html#a9968363

        That test fails under Groovy 1.0, which obviously isn't happening with the existing Spring Groovy support, but it might provide some insight for someone who better understands proxies.

        I've also further refined my understanding of what is going on. The error only seems to be thrown when a Groovy object is calling a method on another proxied Groovy object. If you change the Java test to use serviceBean1 the test will success:


        Code:
                
        try {
                    System.out.println ("\nTesting Java implementation of ServiceConsumer with only one service bean with Spring.");
                    ServiceConsumer sc = new ServiceConsumerImpl ();
                    sc.setServiceBean ((ServiceBean ) c.getBean ("serviceBean1") );
                    sc.useService ();
                } catch (Exception e) {
                    // System.out.println ("\nFAILURE: Testing Java implementation of ServiceConsumer with Spring.");
                    e.printStackTrace ();
                } finally {
                    Thread.sleep (1000);
                }
        So, the issue seems to be some interaction between the JdkDynamicAopProxy and the Groovy method calling mechanism in Groovy 1.1.

        Comment


        • #5
          I think this is a Groovy issue, and not a Spring issue. I have simplified the test, removing all dependencies on Spring. Please see:

          http://mark.mjm.net/~mark/GroovyProxyTest.tar.gz

          The issue seems to be with a groovy object calling a proxied Groovy object.

          Mark

          Comment


          • #6
            Thanks for your information.

            cheers
            chris tam
            hong kong

            Comment


            • #7
              I got the some problem!

              I got the some problem in my project. and I found that use TransactionProxyFactoryBean can make it working.

              Code:
                  <bean id="txProxyTemplate" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
                      <property name="transactionManager" ref="transactionManager" />
                      <property name="transactionAttributes">
                          <props>
                              <prop key="add*">PROPAGATION_REQUIRED</prop>
                              <prop key="delete*">PROPAGATION_REQUIRED</prop>
                              <prop key="*">PROPAGATION_REQUIRED, readOnly</prop>
                          </props>
                      </property>
                  </bean>
              
                  <lang:groovy id="fooServiceBean" refresh-check-delay="1000" script-source="classpath:/com/legendapl/cms/author/service/FooService.groovy">
                  </lang:groovy>
              
                  
                  <bean id="fooService" parent="txProxyTemplate">
                      <property name="target" ref="fooServiceBean" />
                      <property name="proxyInterfaces">
                          <list>
                              <value>com.legendapl.cms.author.service.spi.IFooService</value>
                          </list>
                      </property>
                  </bean>
              But, this is not the style in spring2.0!

              is there better solution for this problem?

              Comment


              • #8
                No quick solution yet. There's a conversation going on over on the Groovy-dev list:

                http://www.nabble.com/JDK-Style-Prox...tf4137792.html

                I've isolated the issue down to the use of proxies where a GroovyBean -calls-a-method-on-> Proxied(GroovyBean).

                This works in Groovy 1.0. Just so you know.

                Comment


                • #9
                  Do you by chance know what AopProxy implementation is used to manufacture the proxy object using the TransactionProxyFactoryBean methodology?

                  There are four of them in org.springframwork.aop.framework: Cglib2AopProxy, DefaultAopProxyFactory, JdkDynamicAopProxy, and ProxyFactory.

                  It is my suspicion that it likely uses Cglib2AopProxy, but I'd like to know for sure.

                  Mark

                  Comment


                  • #10
                    I have created a patch for Groovy-1.1-beta-3-SNAPSHOT that fixes the JDK dynamic proxy issue. It has not been applied to the SVN trunk yet. You can retrieve the patch from:

                    http://jira.codehaus.org/browse/GROOVY-2006

                    Mark

                    Comment

                    Working...
                    X