Description
When using spring-boot
1.1.0.RC1 in combination with the latest New Relic agent, the application fails to start with ClassNotFoundException
errors on start up for classes referenced by the main class. I have done some debugging into the spring-boot-loader
code and have determined that there appears to be a class loader issue. Our application is being executed with a command similar to the following:
java -javaagent:newrelic.jar -jar myApplication.jar
where myApplication.jar
is a spring-boot
repackaged JAR file created by the spring-boot
Gradle plugin (i.e. it contains all the necessary dependency JARs packaged inside). When the -javaagent
option is not present in the command, the application starts successfully. I attached a remote debugger and determined that the issue/difference is with how the doLoadClass
method in org.springframework.boot.loader.LaunchedURLClassLoader
behaves. When the New Relic agent is NOT present, the call to return this.rootClassLoader.loadClass(name);
throws a ClassNotFoundException
, which causes the code to "try to find the class locally", per the comment in that method. When the agent is present, the call to the root class loader returns the main class. This then causes an issue, as the main class's class loader is set to the root class loader and not the LaunchedURLClassLoader
instance, which knows about the nested/included JAR files from the application's JAR file. My suspicion is that the agent is causing the main class to get loaded into the root class loader during its instrumentation phase, thus causing it to be returned when attempting to launch the main class via the JarLauncher
from the loader. The only work around that I have found is to not use the spring-boot-loader
at all and include the dependency JAR's externally to the application JAR and run with both the -javaagent
and -classpath
options. It should also be noted that I verified that the New Relic agent was not making it into the repackaged JAR and verified that the code in ExecutableArchiveLauncher
that prevents the agent from being included in the LaunchedURLClassLoader
does appear to work, though does not solve this issue. One final twist: this issue does NOT happen if you launch the application via Gradle with the agent
option present in the spring-boot
configuration in build.gradle
. From what I can tell, this is because launching via the Gradle plugin does not use the spring-boot-loader
and its launchers: instead, if uses the Gradle Java execution and launches a new VM with the agent
and classpath
options. This should probably be changed to use the spring-boot-loader
for consistency. Let me know if a sample application would be helpful and I will continue to debug this issue on my end to see if I can gain any additional insight into what is going on.