-
Notifications
You must be signed in to change notification settings - Fork 40.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ClassNotFoundException with executable JAR and New Relic agent #1057
Comments
Do you see the same issue with |
I can't reproduce this using one of Boot's sample apps against either 1.1.0.RC1 or a 1.1.0 snapshot. The New Relic agent does its thing and then the application's main class loads as expected:
If you'd like us to look into this some more, a sample application would definitely be helpful. Perhaps there's a difference in the version of the New Relic agent, the JVM or similar? According to the change log in the zip I downloaded, I'm using 3.7.1 of the New Relic agent and I'm running Java 1.7.0_51 on a Mac. |
Let me put together a sample application to at least verify that the issue is reproducible. I did not/have not tried the 1.0.2.RELEASE, which I can also try. As for the agent, I am also using 3.7.1 and update 55 of Java 1.7 on a Mac. |
After creating a sample Spring-Boot application (version
I am not exactly sure what is different from the application that is experiencing the issue (other than the inclusion of a few more dependencies used for compilation). It certainly doesn't explain the behavior I am seeing when attaching the remote debugger with regards to the class loaders. I will continue to dig on my end, but any pointers/leads with regards to what could be impacting the class loader delegation when running with the |
Did you try the 1.0.2 version with your original application? Some logic has changed with |
I tried 1.0.2, but get the same issue:
So there are two weird things that happen. First, I created another sample application and cannot for the life of me to get it to cause the same error as above. I slowly started adding dependencies and source to the test application from the one that is failing and it still starts as expected. The second odd thing is that when I remove the |
I have narrowed this down a bit (I think). I cloned the project that is having the issue above and stripped it down to just have an empty Spring config and simple main method in the main class launched by Spring. When I do this in the test application, it starts up fine, even with the Java agent present. When I do this with the cloned application (with no dependencies, other than the ones required to run Spring-Boot and Jetty), it fails with the error above. On a whim, I decided to remove all of the Eclipse metadata, Gradle metdata and other non-Spring-Boot related files stored in the project (the New Relic agent YAML file, some other files used to deploy with Docker, etc). I am almost positive that these files are NOT making it on to the classpath or in to the repackaged JAR (as I do not seem them in there when I expand the JAR file). However, when I remove them from my source tree (and get rid of the task that copies them around in to the ZIP file I am building), the application starts up with the agent present. I am manually copying the New Relic YAML file into place after expanding the ZIP which contains the New Relic agent and the repackaged JAR file. I am starting to think that some magic is causing these extra files to be picked up by Spring-Boot and is causing it to choke because they are not something it can handle. Is this even possible? I know that you can use YAML configuration files with Spring-Boot, but I thought they either had to be on the classpath or in a specified location (e.g. it does not just scan for every YAML file around). I am going to see if I can further pin this down, but it seems to have something to do with the presence of these non-Spring-Boot files being around/copied at distribution time. |
If the One other thing to check is that the JAR itself has not been recompressed at all during your zip processing. Nested JAR entries need to be stored uncompressed so that we can seek into them. (see http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#executable-jar-zip-entry-compression). Although I think you would see a different error if it was a compression problem. |
I don't think the compression is the issue. If I build the JAR and zip it and the New Relic agent jar up together, then unzip it and run it works:
I am going to try adding in one by one the other files that are being included in the ZIP to see if I can determine when it stops working (I suppose it still could be a compression issue, as that would change as more and more files are added -- especially text based files that can become highly compressed). |
I think that I have ruled out compression as the culprit. I changed my Gradle script to built a I have also ruled out the presence of the config files/non-source files in the ZIP/TAR as the culprit. I added them to the project that starts up correctly and it did not have an impact. As far as I can tell, I have two identical projects: one that loads just fine with the New Relic agent active and one that fails with the
I'm going to investigate #2 a bit more to see if there is in fact a bogus dependency on the class-path that is causing the issue, as at this point, I do not see really any other differences between the project that works and the one that does not work. |
So, I think that I just lead everyone on a wild goose chase. The issue appears to be with using the |
Wow! Sounds like that was a pain to track down. It's worth knowing about the restriction but I don't think we need to change anything from our end. Hopefully this bug report will help anyone in the future that happens to hit the same issue. |
This is old but just stumbled across it. School boy error. I managed to modify the executable jar from underneath the running application. This corrupted it, and caused the same ClassNotFound |
When using
spring-boot
1.1.0.RC1 in combination with the latest New Relic agent, the application fails to start withClassNotFoundException
errors on start up for classes referenced by the main class. I have done some debugging into thespring-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:where
myApplication.jar
is aspring-boot
repackaged JAR file created by thespring-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 thedoLoadClass
method inorg.springframework.boot.loader.LaunchedURLClassLoader
behaves. When the New Relic agent is NOT present, the call toreturn this.rootClassLoader.loadClass(name);
throws aClassNotFoundException
, 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 theLaunchedURLClassLoader
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 theJarLauncher
from the loader. The only work around that I have found is to not use thespring-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 inExecutableArchiveLauncher
that prevents the agent from being included in theLaunchedURLClassLoader
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 theagent
option present in thespring-boot
configuration inbuild.gradle
. From what I can tell, this is because launching via the Gradle plugin does not use thespring-boot-loader
and its launchers: instead, if uses the Gradle Java execution and launches a new VM with theagent
andclasspath
options. This should probably be changed to use thespring-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.The text was updated successfully, but these errors were encountered: