Regression with classloading in the Jetty plugin with Gradle 1.0-milestone-6?

I have a multi-project build setup that looks like the following:

project
    |-- webapp
    |
     |-- src/main/java/com.app.state
    |
                  |-- // entity classes
    |
     |-- src/main/webapp/WEB-INF
    |
     |
          |-- servlet-context.xml
    |
     |
          |-- web.xml
    |
     |-- src/main/resources/META-INF/
    |
     |
          |-- persistence.xml
    |
     |-- build.gradle
    |-- buildSrc
    |
      |-- // plugin code
    |
      |-- build.gradle
    |-- build.gradle

With Gradle 1.0-milestone-3 this worked beautifully, the Spring context bootstraps, creates an in-memory HSQLDB and creates the database schema from the entity classes using Hibernate as the ORM with the JPA persistence unit configuration. I can interact with the database using my service layer.

I’m currently developing a plugin for Gradle and wanted to use the Extension objects that were added in Gradle-1.0-milestone-5. I upgraded Gradle to the M6 release and now at runtime I get exceptions about the Entity classes “not being entities”. I think it’s a classloading issue but I’ve no idea where to begin. I don’t know if it’s directly related to Jetty or something else within Gradle.

The only unusual tool I’m using is Project Lombok (http://projectlombok.org/) which I integrated like so: http://forums.gradle.org/gradle/topics/using_project_lombok_with_gradle .

I’m happy to provide my information, any help would be greatly appreciated.

Hi Chris, please provide one of the exception stack traces.

Hi Luke,

There’s a lot of logging in the codebase at the moment but here’s the root of the exception:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘departmentService’: Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void com.app.service.DepartmentService.setDepartmentDao(com.app.dao.DepartmentDao); nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘departmentDao’: FactoryBean threw exception on object creation; nested exception is java.lang.IllegalArgumentException: Not an entity: class com.app.state.Department

And:

Caused by: java.lang.IllegalArgumentException: Not an entity: class com.app.state.Department

at org.hibernate.ejb.metamodel.MetamodelImpl.entity(MetamodelImpl.java:160)

at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation.(JpaMetamodelEntityInformation.java:52)

at org.springframework.data.jpa.repository.support.JpaEntityInformationSupport.getMetadata(JpaEntityInformationSupport.java:61)

at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getEntityInformation(JpaRepositoryFactory.java:145)

at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:83)

at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:66)

at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:146)

at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.getObject(RepositoryFactoryBeanSupport.java:120)

at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.getObject(RepositoryFactoryBeanSupport.java:39)

at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:142)

… 105 more

So the first thing to do is to work out exactly what an “entity” is in this context, and why this method rejects the argument specifically. Any ideas?

Ok, the “not an entity” problem appears to be related to the persistence.xml file. I believe that it’s not being detected on the classpath in the jettyRun task (see above for where I’ve located the file in the project layout).

This problem also appears if I run the jettyRunWar task because the persistence.xml is not actually added to the META-INF folder in the War archive. What’s even stranger is that with this task I get other errors:

java.lang.NoClassDefFoundError: org/apache/xerces/parsers/DOMParser
 at java.lang.ClassLoader.defineClass1(Native Method)
 at java.lang.ClassLoader.defineClassCond(ClassLoader.java:631)
 at java.lang.ClassLoader.defineClass(ClassLoader.java:615)
 at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141)
 at java.net.URLClassLoader.defineClass(URLClassLoader.java:283)
 at java.net.URLClassLoader.access$000(URLClassLoader.java:58)
 at java.net.URLClassLoader$1.run(URLClassLoader.java:197)
 at java.security.AccessController.doPrivileged(Native Method)
 at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
 at org.gradle.util.MultiParentClassLoader.loadClass(MultiParentClassLoader.java:46)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:295)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:295)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
 at org.mortbay.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:401)
 at org.mortbay.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:363)
 at org.thymeleaf.TemplateParser.computeIsNekoInClassPath(TemplateParser.java:169)
 at org.thymeleaf.TemplateParser.<init>(TemplateParser.java:106)
 at org.thymeleaf.TemplateEngine.initialize(TemplateEngine.java:671)
 at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:767)
 at org.thymeleaf.spring3.view.ThymeleafView.render(ThymeleafView.java:201)
 at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1047)
 at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:817)
 at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:719)
 at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:669)
 at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:574)
 at javax.servlet.http.HttpServlet.service(HttpServlet.java:707)
 at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
 at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
 at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
 at com.bouncelms.util.HttpHeaderFilter.doFilter(HttpHeaderFilter.java:105)
 at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
 at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
 at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
 at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
 at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
 at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
 at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
 at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
 at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:440)
 at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:230)
 at org.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.java:114)
 at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
 at org.mortbay.jetty.Server.handle(Server.java:326)
 at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
 at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:926)
 at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:549)
 at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212)
 at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
 at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:410)
 at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)
Caused by: java.lang.ClassNotFoundException: org.apache.xerces.parsers.DOMParser
 at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
 at java.security.AccessController.doPrivileged(Native Method)
 at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
 ... 52 more

It’s definitely something to do with the classpath configuration setup by Gradle I think.

So which dependency is providing the ‘org.apache.xerces.parsers.DOMParser’ class ?

It’s an (optional) dependency from Thymeleaf. i.e.

thymeleaf -> nekohtml -> xerces

Any ideas why the src/main/resources are not being packaged into the War archive?

Putting it in ‘src/main/resources’ will mean it ends up in ‘$war/WEB-INF/classes’.

Why isn’t it in ‘src/main/web-app/META-INF’?

I thought it was supposed to be in $WAR/WEB-INF/classes according to this blog post:

http://javahowto.blogspot.com/2007/06/where-to-put-persistencexml-in-web-app.html

Thanks for the help so far Luke, to be honest I’m not sure whether it’s a problem with the project layout that’s was being forgiven by Gradle 1.0-milestone-3 and prior or whether it’s a genuine classpath regression in the Jetty plugin…

Ok, so that makes sense as to why it is in ‘resources’.

So you’re saying it doesn’t end up in ‘WEB-INF/classes/META-INF’?

It does end up in the right place:

unzip webapp/build/libs/webapp-0.0.1.war
 // ... snip
   creating: WEB-INF/classes/META-INF/
  inflating: WEB-INF/classes/META-INF/persistence.xml
     creating: WEB-INF/classes/i18n/
  inflating: WEB-INF/classes/i18n/core_pt.properties
    inflating: WEB-INF/classes/i18n/core_ro.properties
    inflating: WEB-INF/classes/i18n/core_mt.properties
    inflating: WEB-INF/classes/i18n/core_el.properties
    inflating: WEB-INF/classes/i18n/core.properties
  // ... snip

Using Gradle 1.0-milestone-3 I get the following dependencies in the War:

inflating: WEB-INF/lib/logback-classic-0.9.30.jar
    inflating: WEB-INF/lib/logback-core-0.9.30.jar
    inflating: WEB-INF/lib/slf4j-api-1.6.2.jar
    inflating: WEB-INF/lib/thymeleaf-spring3-1.1.2.jar
    inflating: WEB-INF/lib/thymeleaf-1.1.2.jar
    inflating: WEB-INF/lib/ognl-3.0.jar
    inflating: WEB-INF/lib/javassist-3.14.0-GA.jar
    inflating: WEB-INF/lib/bonecp-0.7.1.RELEASE.jar
    inflating: WEB-INF/lib/guava-r08.jar
    inflating: WEB-INF/lib/hsqldb-2.2.4.jar
    inflating: WEB-INF/lib/spring-webmvc-3.0.6.RELEASE.jar
    inflating: WEB-INF/lib/spring-asm-3.0.6.RELEASE.jar
    inflating: WEB-INF/lib/spring-beans-3.0.6.RELEASE.jar
    inflating: WEB-INF/lib/spring-core-3.0.6.RELEASE.jar
    inflating: WEB-INF/lib/spring-context-3.0.6.RELEASE.jar
    inflating: WEB-INF/lib/spring-expression-3.0.6.RELEASE.jar
    inflating: WEB-INF/lib/spring-aop-3.0.6.RELEASE.jar
    inflating: WEB-INF/lib/aopalliance-1.0.jar
    inflating: WEB-INF/lib/spring-context-support-3.0.6.RELEASE.jar
    inflating: WEB-INF/lib/spring-web-3.0.6.RELEASE.jar
    inflating: WEB-INF/lib/spring-orm-3.0.6.RELEASE.jar
    inflating: WEB-INF/lib/spring-jdbc-3.0.6.RELEASE.jar
    inflating: WEB-INF/lib/spring-tx-3.0.6.RELEASE.jar
    inflating: WEB-INF/lib/spring-data-jpa-1.0.1.RELEASE.jar
    inflating: WEB-INF/lib/spring-data-commons-core-1.1.0.RELEASE.jar
    inflating: WEB-INF/lib/aspectjrt-1.6.8.jar
    inflating: WEB-INF/lib/jcl-over-slf4j-1.6.1.jar
    inflating: WEB-INF/lib/javax.servlet-api-3.0.1.jar
    inflating: WEB-INF/lib/javax.inject-1.jar
    inflating: WEB-INF/lib/hibernate-jpa-2.0-api-1.0.1.Final.jar
    inflating: WEB-INF/lib/hibernate-entitymanager-3.6.8.Final.jar
    inflating: WEB-INF/lib/hibernate-core-3.6.8.Final.jar
    inflating: WEB-INF/lib/antlr-2.7.6.jar
    inflating: WEB-INF/lib/commons-collections-3.1.jar
    inflating: WEB-INF/lib/dom4j-1.6.1.jar
    inflating: WEB-INF/lib/hibernate-commons-annotations-3.2.0.Final.jar
    inflating: WEB-INF/lib/jta-1.1.jar
    inflating: WEB-INF/lib/cglib-2.2.jar
    inflating: WEB-INF/lib/asm-3.1.jar
    inflating: WEB-INF/lib/javassist-3.12.0.GA.jar

Using both the jettyRun and the jettyRunWar tasks work beautifully this is without any change to the build.gradle scripts or the codebase.

Using Gradle-1.0-milestone-6 I get the following dependencies in the War:

inflating: WEB-INF/lib/spring-webmvc-3.0.6.RELEASE.jar
    inflating: WEB-INF/lib/spring-orm-3.0.6.RELEASE.jar
    inflating: WEB-INF/lib/spring-data-jpa-1.0.1.RELEASE.jar
    inflating: WEB-INF/lib/javax.servlet-api-3.0.1.jar
    inflating: WEB-INF/lib/javax.inject-1.jar
    inflating: WEB-INF/lib/hibernate-jpa-2.0-api-1.0.1.Final.jar
    inflating: WEB-INF/lib/hibernate-entitymanager-3.6.8.Final.jar
    inflating: WEB-INF/lib/logback-classic-0.9.30.jar
    inflating: WEB-INF/lib/thymeleaf-spring3-1.1.2.jar
    inflating: WEB-INF/lib/bonecp-0.7.1.RELEASE.jar
    inflating: WEB-INF/lib/hsqldb-2.2.4.jar
    inflating: WEB-INF/lib/spring-asm-3.0.6.RELEASE.jar
    inflating: WEB-INF/lib/commons-logging-1.1.1.jar
    inflating: WEB-INF/lib/spring-core-3.0.6.RELEASE.jar
    inflating: WEB-INF/lib/spring-beans-3.0.6.RELEASE.jar
    inflating: WEB-INF/lib/spring-expression-3.0.6.RELEASE.jar
    inflating: WEB-INF/lib/aopalliance-1.0.jar
    inflating: WEB-INF/lib/spring-aop-3.0.6.RELEASE.jar
    inflating: WEB-INF/lib/spring-context-3.0.6.RELEASE.jar
    inflating: WEB-INF/lib/spring-context-support-3.0.6.RELEASE.jar
    inflating: WEB-INF/lib/spring-web-3.0.6.RELEASE.jar
    inflating: WEB-INF/lib/spring-tx-3.0.6.RELEASE.jar
    inflating: WEB-INF/lib/spring-jdbc-3.0.6.RELEASE.jar
    inflating: WEB-INF/lib/spring-data-commons-core-1.1.0.RELEASE.jar
    inflating: WEB-INF/lib/slf4j-api-1.6.2.jar
    inflating: WEB-INF/lib/jcl-over-slf4j-1.6.1.jar
    inflating: WEB-INF/lib/aspectjrt-1.6.8.jar
    inflating: WEB-INF/lib/antlr-2.7.6.jar
    inflating: WEB-INF/lib/commons-collections-3.1.jar
    inflating: WEB-INF/lib/dom4j-1.6.1.jar
    inflating: WEB-INF/lib/hibernate-commons-annotations-3.2.0.Final.jar
    inflating: WEB-INF/lib/jta-1.1.jar
    inflating: WEB-INF/lib/hibernate-core-3.6.8.Final.jar
    inflating: WEB-INF/lib/asm-3.1.jar
    inflating: WEB-INF/lib/cglib-2.2.jar
    inflating: WEB-INF/lib/javassist-3.12.0.GA.jar
    inflating: WEB-INF/lib/logback-core-0.9.30.jar
    inflating: WEB-INF/lib/ognl-3.0.jar
    inflating: WEB-INF/lib/javassist-3.14.0-GA.jar
    inflating: WEB-INF/lib/thymeleaf-1.1.2.jar
    inflating: WEB-INF/lib/guava-r08.jar

(Note: between switching build tool versions I first run ‘gradle -C rebuild clean’)

They are not exactly the same list of dependencies, with M6 the project fails with missing classes and in M3 the project works perfectly. The differences are that M3 contains:

inflating: WEB-INF/lib/javassist-3.14.0-GA.jar

and M6 contains:

inflating: WEB-INF/lib/commons-logging-1.1.1.jar
    inflating: WEB-INF/lib/javassist-3.12.0.GA.jar

The commons-logging dependency used to be excluded, I use this line in the gradle script:

dependencies {
    springVersion = "3.0.6.RELEASE"
      compile("org.springframework:spring-webmvc:$springVersion") {
        exclude group: "commons-logging", name: "commons-logging"
    }
    // ... snip
}

Which appears to be ignored in the M6 release…

I don’t understand why the same build script would cause different versions of dependencies to be resolved by Gradle ?

Actually after a closer look I’m wrong… the only difference in the War archive ‘lib’ folders is the inclusion of commons-logging which would cause no problems at runtime… which means I’m back to classloading issues… all the way back to square one again…

I ran the war package generated by Gradle (1.0-milestone-6) using the Jetty Runner (http://blogs.webtide.com/janb/entry/jetty_runner) project and it works perfectly. The problem is most definitely to do with the classpath setup within the Jetty Plugin as of 1.0M3 of Gradle. I’ve not got a clue how to solve this or print out the classpath that the jetty plugin is using?

Any further help is appreciated.

In case someone else stumbles across the upgrade craziness from M3 to M6 of Gradle while using the Jetty plugin. I moved the project to using the tomcat-gradle-plugin (https://github.com/bmuschko/gradle-tomcat-plugin) and still get exactly the same error.

Caused by: java.lang.IllegalArgumentException: Not an entity: class com.app.state.Department
at org.hibernate.ejb.metamodel.MetamodelImpl.entity(MetamodelImpl.java:160)
at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation.(JpaMetamodelEntityInformation.java:52)

My web archive package runs perfectly directly from the war but whenever it’s run within Gradle using the jettyRun and jettyRunWar tasks the code fails. I’m wondering if it’s because Gradle is using conflicting dependencies on the classpath that are loading before my webapp’s dependencies?

The problem I’m having appears to be identical to this one:

http://stackoverflow.com/questions/7961220/how-to-solve-java-lang-illegalargumentexception-not-an-entity-error

(except of course I’m using Gradle)

Which method are you using to map the entities? ie are you using @Entity annotations or .hbm.xml files? Are you using classpath scanning or explicitly listing them in your persistence.xml?

Hi Adam,

I’m using classpath scanning with the @Entity annotation.

My persistence.xml looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
                        http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="WebappPU" transaction-type="RESOURCE_LOCAL">
  <provider>org.hibernate.ejb.HibernatePersistence</provider>
  <properties>
    <property name="hibernate.hbm2ddl.auto" value="create"/>
  </properties>
</persistence-unit>
</persistence>

My applicationContext.xml looks like:

// ... snip
<!-- detect and register @Component (or derivatives) as Spring beans -->
<context:component-scan base-package="com.webapp"/>
  <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
  <context:property-placeholder location="classpath:jdbc.properties"/>
  <!-- use Spring Data to construct concrete JPA classes -->
<jpa:repositories base-package="com.webapp.dao"/>
  <!-- detect and register @Controller as Spring MVC controllers -->
<mvc:annotation-driven/>
  <transaction:annotation-driven transaction-manager="transactionManager"/>
// ... snip

Above is all of the component sanning going on in the webapp with Spring.

Just to summarise everything I’ve done so far:

  1. The codebase works beautifully with Gradle 1.0M3.

  2. The codebase runs from the web archive (created by M3 and M6) using the jetty-runner.

  3. Using the jettyRun and jettyRunWar tasks in M6 causes the exception “not an entity”

  4. Aside from the commons-logging dependency that isn’t excluded by M6, they both have exactly the same dependency list in both versions.

  5. Using the gradle-tomcat-plugin causes exactly the same error exception

Something else I’ve tried is:

configurations {
    jetty { extendsFrom runtime }
}
  dependencies {
    jetty group: "org.mortbay.jetty", name: "jetty-runner", version: "8.0.4.v20111024"
// TEMP
}
  task jettyRun(type: JavaExec, dependsOn: ":bounce:classes") {
    args "src/main/webapp"
    classpath configurations.jetty + files("build/classes/main") + files("build/resources/main")
    main "org.mortbay.jetty.runner.Runner"
}

Running my custom jettyRun task causes exactly the same exception.

It’s got to be a problem with the classpath either something that is added or something that is not added by Gradle 1.0M6.

Please, any ideas?

In a last ditch effort to seek help on upgrading to the M6 release of Gradle, I have put together the smallest possible reproducible example of the problem:

http://dl.dropbox.com/u/1250857/toywebapp.zip

Using Gradle M3 the project starts the ToyWebappServlet successfully while in Gradle M6 it fails miserably. As usual any help is appreciated.

When you get a chance, please see my additional information below. Thanks.