Package jenkins.util

Class SetContextClassLoader

  • All Implemented Interfaces:
    AutoCloseable

    public final class SetContextClassLoader
    extends Object
    implements AutoCloseable
    Java defines a Thread.getContextClassLoader(). Jenkins does not use this much; it will normally be set by the servlet container to the Jenkins core class loader.

    Some Java libraries have a fundamental design flaw, originating in premodular systems with a "flat classpath", whereby they expect Thread.getContextClassLoader() to have access to the same classes as the class loader of the calling class. This fails in Jenkins, because Thread.getContextClassLoader() can only see Jenkins core, not plugins.

    It is a design flaw in the library if it fails to allow clients to directly specify a ClassLoader to use for lookups (or preregister Class instances for particular names). Consider patching the library or looking harder for appropriate APIs that already exist. As an example, ObjectInputStream (used for deserializing Java objects) by default uses a complicated algorithm to guess at a ClassLoader, but you can override ObjectInputStream.resolveClass(java.io.ObjectStreamClass) to remove the need for guessing (as ObjectInputStreamEx in fact does).

    Alternatively, work around the problem by applying SetContextClassLoader liberally in a try-with-resources block wherever we might be calling into such a library:

     class Caller {
         void foo() {
             try (SetContextClassLoader sccl = new SetContextClassLoader()) {
                 [...] // Callee uses Thread.currentThread().getContextClassLoader()
             }
         }
     }
     

    When called from a plugin, SetContextClassLoader() should typically be used. This implicitly uses the class loader of the calling class, which has access to all the plugin's direct and transitive dependencies. Alternatively, the class loader of a specific class can be used via SetContextClassLoader(Class). When the particular class loader needed is unclear, SetContextClassLoader(ClassLoader) can be used as a fallback with PluginManager.UberClassLoader as the argument, though this is not as safe since lookups could be ambiguous in case two unrelated plugins both bundle the same library. In functional tests, RealJenkinsRule.Endpoint can be used to reference a class loader that has access to the plugins defined in the test scenario.

    See the developer documentation for more information.

    Since:
    2.362