PDA

View Full Version : Maven2+Ant+Spring : Classloading problem ?



mtim
Mar 3rd, 2008, 09:21 AM
Hi all,

can't instantiate spring container in groovy script if I run that script within ant task in maven's pom.xml.
Spring finds configuration xml file but it seems that it doesn't see any beans defined. Here debugging messages that spring logs:

INFO ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlAp plicationContext@666d83: display name [org.springf
ramework.context.support.ClassPathXmlApplicationCo ntext@666d83]; startup date [Mon Mar 03 09:57:13 EST 2008]; root of context hierarchy
DEBUG PluggableSchemaResolver - Loading schema mappings from [META-INF/spring.schemas]
DEBUG PluggableSchemaResolver - Loaded schema mappings: {}
DEBUG PluggableSchemaResolver - Loading schema mappings from [META-INF/spring.schemas]
DEBUG PluggableSchemaResolver - Loaded schema mappings: {}
DEBUG XmlBeanDefinitionReader - Loaded 0 bean definitions from location pattern [classpath*:applicationContext.xml]
INFO ClassPathXmlApplicationContext - Bean factory for application context [org.springframework.context.support.ClassPathXmlAp plicationContext@666d83
]: org.springframework.beans.factory.support.DefaultL istableBeanFactory@1e5cf0
DEBUG ClassPathXmlApplicationContext - 0 beans defined in org.springframework.context.support.ClassPathXmlAp plicationContext@666d83: display name [org
.springframework.context.support.ClassPathXmlAppli cationContext@666d83]; startup date [Mon Mar 03 09:57:13 EST 2008]; root of context hierarchy
DEBUG ClassPathXmlApplicationContext - Unable to locate MessageSource with name 'messageSource': using default [org.springframework.context.support.De
legatingMessageSource@aecb09]
DEBUG ClassPathXmlApplicationContext - Unable to locate ApplicationEventMulticaster with name 'applicationEventMulticaster': using default [org.spring
framework.context.event.SimpleApplicationEventMult icaster@721965]
INFO DefaultListableBeanFactory - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultL istableBeanFactory@1e5cf0: defini
ng beans []; root of factory hierarchy
DEBUG ClassPathXmlApplicationContext - Publishing event in context [org.springframework.context.support.ClassPathXmlAp plicationContext@666d83]: org.sp
ringframework.context.event.ContextRefreshedEvent[source=org.springframework.context.support.ClassPa thXmlApplicationContext@666d83: display name [org.
springframework.context.support.ClassPathXmlApplic ationContext@666d83]; startup date [Mon Mar 03 09:57:13 EST 2008]; root of context hierarchy]


2 problems here :
=====> XmlBeanDefinitionReader loads/finds 0 beans.
and
=========> Loading schema mappings from [META-INF/spring.schemas]
=========> Loaded schema mappings: {}



If I wrap groovy script into GroovyTestCase and run it as test case using maven's surefile plugin it works fine.


Has anyone noticed this ? Any ideas why this is happening ?

spring config:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
<bean id="messageSource" class="org.springframework.context.support.ResourceBundle MessageSource">
<property name="basename" value="messages"/>
</bean>

<bean id="countryService" class="org.springframework.samples.countries.DefaultCount ryService"/>

</beans>


groovy script:


import org.springframework.context.support.ClassPathXmlAp plicationContext
import org.springframework.samples.countries.CountryServi ce

class ScriptRunner {

static void main(args) {
println '*'*60 + '\n\t\tRUNNING GROOVY SCRIPT \n' + '*'*60
def context = new ClassPathXmlApplicationContext("classpath*:applicationContext.xml")
def service = (CountryService) context.getBean("countryService")
def countries = service.getAllCountries(Locale.ENGLISH)
println "Number of countries (GROOVY):: ${countries.size()}"
}
}


pom.xml :


......
<dependency>
<groupId>org.codehaus.mojo.groovy.runtime</groupId>
<artifactId>groovy-runtime-1.1</artifactId>
<version>1.0-beta-3</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>2.0.5</version>
</dependency>

<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>1.5.4</version>
</dependency>
......
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo.groovy</groupId>
<artifactId>groovy-maven-plugin</artifactId>
<extensions>true</extensions>
</plugin>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>integration-test</phase>
<configuration>
<tasks>
<taskdef name="groovy"
classname="org.codehaus.groovy.ant.Groovy">
<classpath refid="maven.runtime.classpath"/>
</taskdef>
<groovy src="src/test/groovy/com/test/ScriptRunner.groovy"/>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>

</build>



Thanks,
Timour

mtim
Mar 3rd, 2008, 10:21 AM
figured that out ....

maven itself or maven-antrun-plugin set Thread.currentThread() to "org.codehaus.classworlds.RealmClassLoader which is a root class loader for maven as far as I understand.

I fixed it by setting thread's class loader in groovy script:


import org.springframework.context.support.ClassPathXmlAp plicationContext
import org.springframework.samples.countries.CountryServi ce

class ScriptRunner {

static void main(args) {
println '*'*60 + '\n\t\tRUNNING GROOVY SCRIPT \n' + '*'*60

//setting current thread's class loader to class loader that loaded this script
Thread.currentThread().setContextClassLoader(Scrip tRunner.class.getClassLoader())

def context = new ClassPathXmlApplicationContext("applicationContext.xml")
def service = (CountryService) context.getBean("countryService")
def countries = service.getAllCountries(Locale.ENGLISH)
println "Number of countries (GROOVY):: ${countries.size()}"
}
}


Thanks,
Timour

gnandiga
Mar 3rd, 2008, 12:01 PM
Did you try setting the resource and testResource config in the pom.xml?

mtim
Mar 3rd, 2008, 02:07 PM
I think I did .... I tried so many things, until I started looking into Spring's src :-).
Spring could find configuration file since the beginning, otherwise it would throw FileNotFoundException


the problem is how Spring resolves resources. If Spring doesn't have Class or ClassLoader instance passed it uses default context class loader to
resolve resource using org.springframework.util.ClassUtils.getDefaultClas sLoader(). well in my case the context class loader
was "org.codehaus.classworlds.RealmClassLoader"

here is class loader hierarchy inside of groovy script :


groovy.lang.GroovyClassLoader$InnerLoader@1ed00d1
groovy.lang.GroovyClassLoader@119549e
groovy.lang.GroovyClassLoader@c42804
org.apache.tools.ant.loader.AntClassLoader2@aa559d
sun.misc.Launcher$AppClassLoader@53ba3d
sun.misc.Launcher$ExtClassLoader@e80a59


while RealmClassLoader is a stanalone class loader (no parent) . It looks like bookstrap class loader to me.


//org.codehaus.classworlds.RealmClassLoader@53fb57
Thread.currentThread().getContextClassLoader().get URLs().each {
println it
}
file:/C:/m2/repository/org/apache/maven/plugins/maven-antrun-plugin/1.1/maven-antrun-plugin-1.1.jar
file:/C:/m2/repository/ant/ant/1.6.5/ant-1.6.5.jar
file:/C:/m2/repository/org/codehaus/plexus/plexus-utils/1.1/plexus-utils-1.1.jar
file:/C:/m2/repository/ant/ant-launcher/1.6.5/ant-launcher-1.6.5.jar