Skip to content

Conversation

Dav1dde
Copy link
Contributor

@Dav1dde Dav1dde commented Feb 24, 2018

Related to and fixes: #12088

Sets the Thread's classloader for JMX endpoints, before invoking the endpoint, to the classloader which was initially used to load the context.

Before:

--- Class Loaders
getClass().getClassLoader(): java.net.URLClassLoader@4ac20dd2
currentThread().getContextClassLoader(): sun.misc.Launcher$AppClassLoader@18b4aac2
ClassUtils.getDefaultClassLoader(): sun.misc.Launcher$AppClassLoader@18b4aac2
---

After:

--- Class Loaders
getClass().getClassLoader(): java.net.URLClassLoader@4ac20dd2
currentThread().getContextClassLoader(): java.net.URLClassLoader@4ac20dd2
ClassUtils.getDefaultClassLoader(): java.net.URLClassLoader@4ac20dd2
---

Test-Code (using sample Application from start.spring.io including web and actuator):

@Component
@JmxEndpoint(id = "classloader")
public class JmxClassloaderEndpoint {
    @WriteOperation
    public void someOperation() {
        System.out.println("--- Class Loaders");
        System.out.println("getClass().getClassLoader(): " + getClass().getClassLoader());
        System.out.println("currentThread().getContextClassLoader(): " + Thread.currentThread().getContextClassLoader());
        System.out.println("ClassUtils.getDefaultClassLoader(): " + ClassUtils.getDefaultClassLoader());
        System.out.println("---");
    }
}

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Feb 24, 2018
@Dav1dde
Copy link
Contributor Author

Dav1dde commented Feb 24, 2018

Build failure seems to be random and not related to this change, can someone re-trigger the build?

@wilkinsona
Copy link
Member

Closing and re-opening to trigger a new build.

@wilkinsona wilkinsona closed this Feb 24, 2018
@wilkinsona wilkinsona reopened this Feb 24, 2018
@wilkinsona
Copy link
Member

I think that used to work with Travis. Doesn’t appear to have worked with Concourse.

@wilkinsona
Copy link
Member

I’ve manually triggered a new build on Concourse but I’m not sure it’s building the right change. There are some git-related errors in the logs.

@Dav1dde
Copy link
Contributor Author

Dav1dde commented Apr 16, 2018

Any news or interest in this?

@snicoll
Copy link
Member

snicoll commented Apr 17, 2018

@Dav1dde there is. I had a look at your proposal a while back and didn't like it very much but I have nothing else to offer. I'll have to dig a bit more first.

@Dav1dde
Copy link
Contributor Author

Dav1dde commented Apr 19, 2018

@snicoll thank you!

You're right, it feels hack-ish. Want me to update it so the classloader comes from BeanClassLoaderAware instead of getClass().getClassLoader()?

@snicoll
Copy link
Member

snicoll commented Apr 20, 2018

That won't change much I am afraid. Switching the classloader of the current thread without restoring the previous instance once the method has been invoked sounds definitely wrong to me. I would only do this as a last resort option.

@Dav1dde
Copy link
Contributor Author

Dav1dde commented Apr 20, 2018

Oh, that's not a problem, actually had a version that restored it. (that's also what we do at work right now). I'll update this tonight and let's see if you like that more.

@snicoll
Copy link
Member

snicoll commented Apr 21, 2018

Sorry, I meant that this proposal doesn’t look great to me (and at the very least the classloader should be restored).

@Dav1dde
Copy link
Contributor Author

Dav1dde commented Apr 21, 2018

Updated and restoring class loader now (no-op if bean class loader is not set, e.g. in tests). I personally don't like the solution either but I can't think of anything better, if you can think of something let me know and I can change the PR accordingly.

@snicoll
Copy link
Member

snicoll commented Apr 22, 2018

Thanks @Dav1dde, that's a very nice summary of the situation indeed. I'll spend a bit more time on it and report here when I am done.

throw new ReflectionException(new IllegalArgumentException(message), message);
}
return invoke(operation, params);
ClassLoader previousClassLoader = ClassUtils.overrideThreadContextClassLoader(this.classLoader);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may throw a SecurityException. I think we should set the TCCL on a best-effort basis so any SecurityException that's thrown should be caught (and logged) and we should then proceed with the invoke.

@wilkinsona
Copy link
Member

FWIW, other than the SecurityException that I've just commented about, I think we should make this change.

@Dav1dde
Copy link
Contributor Author

Dav1dde commented Apr 30, 2018

I'll update it later tonight.

try {
ObjectName name = this.objectNameFactory.getObjectName(endpoint);
EndpointMBean mbean = new EndpointMBean(this.responseMapper, endpoint);
mbean.setBeanClassLoader(this.classLoader);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered having the class loader as a constructor argument, but for me the class loader is optional for the EndpointMBean.

}
catch (SecurityException exc) {
// can't set class loader, ignore it and proceed
logger.warn("Unable to override class loader for JMX endpoint.");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if a info log would be better here.

@Dav1dde
Copy link
Contributor Author

Dav1dde commented Apr 30, 2018

Branch updated, I added a few comments where I am not sure if things are okay this way and fit into the spring codebase.

@snicoll snicoll self-assigned this May 14, 2018
snicoll added a commit that referenced this pull request May 14, 2018
* pr/12209:
  Polish "Set classloader for JMX endpoints to application classloader"
  Set classloader for JMX endpoints to application classloader
@snicoll snicoll added the type: bug A general bug label May 14, 2018
@snicoll snicoll removed the status: waiting-for-triage An issue we've not yet triaged label May 14, 2018
@snicoll snicoll added this to the 2.0.3 milestone May 14, 2018
@snicoll snicoll closed this in 2be1c8f May 14, 2018
@snicoll
Copy link
Member

snicoll commented May 14, 2018

@Dav1dde thank you for your first contribution to Spring Boot. This has been merged in 2.0.x and master.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug A general bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Wrong ClassLoader set within JMX Endpoint
4 participants