Announcement Announcement Module
Collapse
No announcement yet.
cross reference of groovy objects fails Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • cross reference of groovy objects fails

    Hi,

    I am trying to incorporate groovy into my java application, via the spring framework. The problem I am experiencing is that classes loaded this way cannot reference each other as they are loaded with different class loaders.

    Here is an example of what is happening. Say I create a very simple java program that loads an spring context

    Code:
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestSpringGroovy {
            public TestSpringGroovy () { }
    
            public static void main(String[] args) throws Throwable {
                    if (args.length == 0) 
                            System.err.println("Please, pass contexts.xml files in command line. " + "app-context.xml");
                     else
                         ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(args);
            }
    }
    Then I define a simple application context XML file instantiating 2 groovy objects

    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:lang="http://www.springframework.org/schema/lang" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd">
    
            <lang:groovy id="Me" script-source="classpath:User.groovy">
                    <lang:property name="name" value="Me"/>
            </lang:groovy>
    
            <lang:groovy id="Contract" script-source="classpath:Contract.groovy">
                    <lang:property name="user" ref="Me"/>
            </lang:groovy>
    
    </beans>
    Finally I define 2 groovy classes called respectively User.groovy and Contract.groovy

    Code:
    class User {
            def String name;
    }
    Code:
    public class Contract {
            def User user
    }
    Now when I run this java program I get the following error. removing the type User from the contract.groovy fixes the problem

    Code:
    org.springframework.beans.TypeMismatchException: Failed to convert property value of type [User] to required type [User] for property 'user'; nested exception is java.lang.IllegalArgumentException: No matching editors or conversion strategy found
    Caused by: java.lang.IllegalArgumentException: No matching editors or conversion strategy found
            at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:212)
            at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:127)
            at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:775)
            at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:608)
            at org.springframework.beans.AbstractPropertyAccessor.setPropertyValue(AbstractPropertyAccessor.java:49)
            at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:74)
            at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:57)
            at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:970)
            at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:729)
            at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:416)
            at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:245)
            at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:141)
            at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:242)
            at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:156)
            at org.springframework.scripting.support.ScriptFactoryPostProcessor.postProcessBeforeInstantiation(ScriptFactoryPostProcessor.java:232)
            at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInstantiation(AbstractAutowireCapableBeanFactory.java:593)
            at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:365)
            at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:245)
            at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:141)
            at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:242)
            at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:156)
            at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:290)
            at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:348)
            at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:92)
            at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:77)
            at TestSpringGroovy.main(TestSpringGroovy.java:11)
    This could be due to the fact that 2 different Groovy class loaders load those 2 classes. I could verify this if I knew where to look in the spring source code.

    Any help is appreciated

    Regards

    Pascal

  • #2
    GroovyClassLoader not shared in GroovyScriptfactory

    The problem seems to be that everytime a new groovy class loader is created everytime getScriptedObject of GroovyScriptFactory is called

    Code:
        public Object getScriptedObject(ScriptSource actualScriptSource, Class[] actualInterfaces)
                throws IOException, ScriptCompilationException {
    
            ClassLoader cl = ClassUtils.getDefaultClassLoader();
            GroovyClassLoader groovyClassLoader = new GroovyClassLoader(cl);
    This means that a class (from a groovy script) loaded by one invocation of the GroovyClassLoader will not be equal to the same class loaded by another instance of GroovyClassLoader.

    I couldn't find in the code any invocation of this method, except in test, so I am not sure how a <lang:groovy> invokes a
    GroovyScriptFactory and if multiple GroovyScriptfactory are created or just one.

    I will appreciate any help with this. In my mind it makes mixing Spring and groovy pretty useless.

    If only one instance of ScriptGroovyFactory was instantiated by Spring then moving the creation of the GroovyClassLoader to an instance variable would certainly fix the problem. Here is a patch that works for me

    Code:
    Index: GroovyScriptFactory.java
    ===================================================================
    RCS file: /cvsroot/springframework/spring/src/org/springframework/scripting/groovy/GroovyScriptFactory.java,v
    retrieving revision 1.4
    diff -u -r1.4 GroovyScriptFactory.java
    --- GroovyScriptFactory.java    20 Apr 2006 19:20:56 -0000    1.4
    +++ GroovyScriptFactory.java    15 Nov 2006 23:34:47 -0000
    @@ -46,6 +46,7 @@
     public class GroovyScriptFactory implements ScriptFactory {
     
         private final String scriptSourceLocator;
    +    private static final GroovyClassLoader groovyClassLoader = new GroovyClassLoader ();
     
     
         /**
    @@ -91,8 +92,6 @@
         public Object getScriptedObject(ScriptSource actualScriptSource, Class[] actualInterfaces)
                 throws IOException, ScriptCompilationException {
     
    -        ClassLoader cl = ClassUtils.getDefaultClassLoader();
    -        GroovyClassLoader groovyClassLoader = new GroovyClassLoader(cl);
             try {
                 Class clazz = groovyClassLoader.parseClass(actualScriptSource.getScriptAsString());
                 return clazz.newInstance();
    Regards

    Pascal
    Last edited by pdemilly; Nov 15th, 2006, 06:35 PM. Reason: Add patch

    Comment


    • #3
      Can you please raise an issue on JIRA and post back to this thread?
      Thanks.

      Comment


      • #4
        Does anyone know the status of this issue?

        I'm looking at using Groovy Beans in my application loading them with Spring, but if each one has its own class loader that would be an issue.

        Mark

        Comment


        • #5
          I have encounter some similar problem when injecting some groovy scripting bean to a jruby scripting bean. The solution is your groovy script MUST implements a java interface. Both the User and Contract groovy script must implement its own interface. For example, the Contract groovy script should be:
          ...
          public class Contract implements ContractInterface {
          def UserInterface user;
          }
          ...
          And the ContractInterface.java is something like that:
          ...
          public interface ContractInterface [
          public void setUser(UserInterface user) ;
          }
          ...
          For the User groovy script:
          ...
          public class User implements UserInterface {
          }
          ...
          The UserInterface.java should be:
          ...
          public interface UserInterface {
          }
          ...

          Without the java interface, the springframework will not be able to use reflection to inject your User groovy bean to the Contract groovy bean. I have checked all the relevant source codes and found out that there is no workaround for this restriction.

          thanks and best regards
          chris tam
          xenium
          Last edited by cltam96; Feb 15th, 2007, 06:00 AM.

          Comment

          Working...
          X