gradle-tomcat-plugin icon indicating copy to clipboard operation
gradle-tomcat-plugin copied to clipboard

How do I provide files to the embedded Tomcat instance? Specifically a custom classloader jar.

Open Jazzepi opened this issue 11 years ago • 9 comments

I'm trying to put Spring's custom Tomcat classloader into my embedded Tomcat instance that I use for integration tests in my Gradle project because I plan on using load time weaving. It works fine in my normal install when I place spring-instrument-tomcat-3.2.5.RELEASE.jar into the apache-tomcat-7.0.40/lib folder, but how do I access that folder of an embedded Tomcat instance and place the jar in there before the boostrap classloader finds it?

I've included the stack overflow question if someone would rather answer there :) Either way I will x-post the answer to both places. Thanks in advance!

http://stackoverflow.com/questions/21895507/how-do-a-give-an-embedded-tomcat-instance-a-custom-classloader-when-using-gradle

Jazzepi avatar Feb 20 '14 00:02 Jazzepi

Related: I used a custom Java agent just fine until recently by setting org.gradle.jvmargs=[...] -javaagent: [...] in my gradle.properties to facilitate load-time weaving in gradle-tomcat-plugin but either since Gradle 1.11 or Tomcat 7.0.50 it stopped working by picking up the agent.

This leaves me with the same problem op has. Please provide some guidance.

Emontis avatar Feb 20 '14 11:02 Emontis

@Jazzepi The plugin instantiates its own classloader to separate the project's classpath from Gradle's general classpath. You might want to try to add the JAR file to the tomcat configuration to see if that works. If not, it would be helpful if you could provide an example project on GitHub.

@Emontis Would you mind checking which makes a difference for your project - the new Gradle or Tomcat version?

bmuschko avatar Feb 23 '14 23:02 bmuschko

Here's a stripped down example of the problem I'm having. The integration tests pass when they're running against a locally installed Tomcat instance on port 8090. When running them by invoking gradlew integrationTest I get a stack trace.

https://github.com/Jazzepi/tomcat-gradle-test

To run this you'll to start up a local mysql database, and set the application.properties. Should be very straightforward, let me know if you have any questions.

https://github.com/Jazzepi/tomcat-gradle-test/blob/master/src/main/resources/application.properties

Jazzepi avatar Feb 25 '14 01:02 Jazzepi

@bmuschko Finally had time to nail the culprit down. Turns out it is some change between Tomcat 7.0.50 and 7.0.52.

Up until 7.0.50 the -javaagent I configured for Gradle gets picked up:

2014-02-27 09:17:22.501 [localhost-startStop-1] INFO  o.s.c.w.DefaultContextLoadTimeWeaver - Could not obtain server-specific LoadTimeWeaver: Could not initialize TomcatLoadTimeWeaver because Tomcat API classes are not available
2014-02-27 09:17:22.503 [localhost-startStop-1] INFO  o.s.c.w.DefaultContextLoadTimeWeaver - Found Spring's JVM agent for instrumentation

Whereas with 7.0.52 it does not get picked up:

2014-02-27 09:17:57.788 [localhost-startStop-1] INFO  o.s.c.w.DefaultContextLoadTimeWeaver - Could not obtain server-specific LoadTimeWeaver: Could not initialize TomcatLoadTimeWeaver because Tomcat API classes are not available
2014-02-27 09:17:57.798 [localhost-startStop-1] ERROR o.s.w.c.ContextLoader - Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.context.weaving.AspectJWeavingEnabler#0': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'loadTimeWeaver': Initialization of bean failed; nested exception is java.lang.IllegalStateException: ClassLoader [org.apache.catalina.loader.WebappClassLoader] does NOT provide an 'addTransformer(ClassFileTransformer)' method. Specify a custom LoadTimeWeaver or start your Java virtual machine with Spring's agent: -javaagent:org.springframework.instrument.jar

Of course a solution that uses the TomcatLoadTimeWeaver would be much more preferable. I uploaded an MWE at https://github.com/Emontis/gtp-ltw-mwe

Emontis avatar Feb 27 '14 08:02 Emontis

@Emontis Hmm, I am not sure what I can do about this if is broken with a newer version of Tomcat. Would you mind asking about it on the Tomcat user mailing list? It's probably a bug on their end then.

bmuschko avatar Mar 07 '14 22:03 bmuschko

Will do.

It would be great and preferable, however, if you can point out/build a way to use the TomcatLoadTimeWeaver with gradle-tomcat-plugin.

Emontis avatar Mar 12 '14 13:03 Emontis

Tomcat's API doesn't allow for just putting the JAR on the classpath.

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'loadTimeWeaver': Initialization of bean failed; nested exception is java.lang.IllegalStateException: ClassLoader [org.apache.catalina.loader.WebappClassLoader] does NOT provide an 'addTransformer(ClassFileTransformer)' method. Specify a custom LoadTimeWeaver or start your Java virtual machine with Spring's agent: -javaagent:org.springframework.instrument.jar

You will need to use -javagent. You'll have to ask on Tomcat's user mailing list why this was broken with 7.0.52. Maybe it's a bug or there's a reason for this not working anymore.

bmuschko avatar Mar 15 '14 13:03 bmuschko

Thats true, but usually you create a context.xml in META-INF to tell Tomcat to use a different class loader than org.apache.catalina.loader.WebappClassLoader, e.g. Spring’s org.springframework.instrument.classloading.tomcat.TomcatLoadTimeWeaver.

The -javaagent was a workaround because the different class loader was not picked up by gradle-tomcat-plugin. The use of an appropriate class loader is much more preferable and it would be nice if the plugin would support it.

Emontis avatar Mar 19 '14 09:03 Emontis

This issue isn't necessarily high on my priority list. If you want this feature, you might want to look into contributing it. I'd be more than happy to review and pull it in. You should probably also have a look at the Gradle Cargo plugin. It let's you use an isolated and installed container that you can configure the way you want.

bmuschko avatar Mar 20 '14 00:03 bmuschko