Skip to content
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

javaagent could not work normally when application is started as a uber executable jar #4868

Closed
segeon opened this issue Jan 4, 2016 · 6 comments
Milestone

Comments

@segeon
Copy link

segeon commented Jan 4, 2016

I'm trying to instrument a spring boot application using javaagent. My agent works fine when started from my ide by running my main method. However, when I package my app into an executable jar using spring-boot-maven-plugin, my agent fails due to some classloading issues. Detail problem description can be found here raphw/byte-buddy#87

After a probe into spring boot's source code, I found the root cause of this problem. When starting a uber jar, spring boot uses a dedicated classloader named LaunchedURLClassLoader to load resources from the uber jar. This classloader does not respect the normal convention of classloading process. It delegates classloading task to ExtClassloader instead of default system classloader. Moreover, it excludes javaagent jars out of its classpath URLs. Consequently java agent classes are invisible to my application classes and java agent classes can't reference third party classes that packaged in the uber jar neither.

One solution is to package the agent into uber jar too. But it's not so elegant.

What puzzles me is why spring boot doesn't respect the normal classloading semantics and skip default system classloader during class loading process and why should the LaunchedURLClassLoader ignore javaagent jars.

Pls help me understand spring boot's considerations. Thanks a lot!

FYI, I'm using spring boot 1.3.1.RELEASE

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Jan 4, 2016
@wilkinsona
Copy link
Member

If launched correctly, Java Agents should work as far as we know. For example, AspectJ load-time weaving is known to work. Can you please provide a sample project that illustrates the problem along with exact details of how to launch your failing application?

@wilkinsona wilkinsona added status: waiting-for-feedback We need additional information before we can continue and removed status: waiting-for-triage An issue we've not yet triaged labels Jan 4, 2016
@segeon
Copy link
Author

segeon commented Jan 5, 2016

@wilkinsona here is a sample project git@github.com:segeon/agent-example.git. This sample project contains two modules, one is the agent and the other is testapp. When launched from testapp's main method, the agent works fine. However, when launched from an executable jar, the agent fails.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Jan 5, 2016
@wilkinsona
Copy link
Member

@segeon Thanks for the sample. Can you please provide exact details of how you are running the app when you use the main method and when you use the executable jar?

@wilkinsona wilkinsona added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels Jan 5, 2016
@segeon
Copy link
Author

segeon commented Jan 6, 2016

@wilkinsona I've added two scripts in script dir. start_from_exec_jar.sh is for starting the testapp as a spring boot uber jar, while the other script is for starting testapp from it's main method directly, imitating the way running an application from an IDE like Idea. When started using the former method, the agent fails. However, it's ok to start using the latter way. Pls have a try

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Jan 6, 2016
@wilkinsona
Copy link
Member

@segeon Thanks again

@wilkinsona
Copy link
Member

@segeon I don't think we'll be able to do anything about this in 1.3.x, but I believe we can simplify things in 1.4.

I've prototyped some changes for #4897 which dramatically simplifies Boot's custom class loader, allowing it to use standard delegation to the app/system class loader. With these changes in place, you executable jar sample works, albeit with one change. I had to remove <scope>provided</scope> from the HTTP client dependency in the agent so that it's included in the jar. That seems reasonable to me as the agent should be self-contained and it relies directly upon HTTP client classes.

@wilkinsona wilkinsona added this to the 1.4.0.M1 milestone Jan 15, 2016
@wilkinsona wilkinsona removed the status: feedback-provided Feedback has been provided label Jan 15, 2016
wilkinsona added a commit to wilkinsona/spring-boot that referenced this issue Feb 5, 2016
When an application is run as an executable archive with nested jars,
the application's own classes need to be able to load classes from
within the nested jars. This means that the application's classes need
to be loaded by the same class loader as is used for the nested jars.
When an application is launched with java -jar the contents of the
jar are on the class path of the app class loader, which is the
parent of the LaunchedURLClassLoader that is used to load classes
from within the nested jars. If the root of the jar includes the
application's classes, they would be loaded by the app class loader
and, therefore, would not be able to load classes from within the
nested jars.

Previously, this problem was resolved by LaunchedURLClassLoader being
created with a copy of all of the app class laoder's URLs and by
using an unconventional delegation model that caused it to skip its
parent (the app class loader) and jump straight to its root class
loader. This ensured that the LaunchedURLClassLoader would load both
the application's own classes and those from within any nested jars.
Unfortunately, this unusual delegation model has proved to be
problematic. We have seen and worked around some problems with Java
Agents (see spring-projectsgh-4911 and spring-projectsgh-863), but there are others (see spring-projectsgh-4868)
that cannot be made to work with the current delegation model.

This commit reworks LaunchedURLClassLoader to use a conventional
delegate model with the app class loader as its parent. With this
change in place, the application's own classes need to be hidden
from the app class loader via some other means. This is now achieved
by packaging application classes in BOOT-INF/classes (and, for
symmetry, nested jars are now packaged in BOOT-INF/lib). Both the
JarLauncher and the PropertiesLauncher (which supports the executable
jar layout) have been updated to look for classes and nested jars in
these new locations.
@wilkinsona wilkinsona changed the title javaagent could not work normally when application is started as a uber executable jar javaagent could not work normally when application is started as a uber executable jar Feb 19, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants