gradle-tomcat-plugin
gradle-tomcat-plugin copied to clipboard
How do I provide files to the embedded Tomcat instance? Specifically a custom classloader jar.
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
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.
@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?
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
@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 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.
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
.
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.
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.
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.