Uploaded image for project: 'Jenkins'
  1. Jenkins
  2. JENKINS-8057

Maven Project not resetting classloader after execution

    • Icon: Bug Bug
    • Resolution: Unresolved
    • Icon: Major Major
    • maven-plugin
    • None
    • Tomcat 6.0, Java 1.6.0_22, Maven 2.2.1, Hudson 1.383, Windows XP SP3

      It appears that when executing a Maven project, the classloader is being changed after running the Publisher tasks' prebuild steps, but is not being returned to the original setting prior to invoking the Publisher perform steps. Looks like this is happening down somewhere inside of MavenUtil.createEmbedder or its children when a call to Thread.setContextClassLoader is called.

      This causes problems for those of us trying to write plugins where one of our dependent libraries expects the same class loader to be in place during all calls. I'm trying to integrate with IBM's Rational Team Concert, which has this expectation. It seems like various BuildWrappers, Publishers, etc should be isolated from any classloader changes made to implement the build, with the classloader changed back to its original setting prior to finalizing the build.

          [JENKINS-8057] Maven Project not resetting classloader after execution

          tdiz added a comment -

          Have you found a solution to this? We haven't seen it yet, but are potentially headed down the path of integrating w/ RTC.

          tdiz added a comment - Have you found a solution to this? We haven't seen it yet, but are potentially headed down the path of integrating w/ RTC.

          Will Lauer added a comment -

          We ended up finding a workaround, but unfortunately, classloading issues continue to plague our implementation of an RTC/Jenkins plugin on and off. This is partially due to Jenkins, but also due to some strange things that RTC does in its libraries.

          The quick answer to the maven induced classloader issue was to save and restore the Thread.contextClassLoader set on any thread running RTC code. I dug into this a bit, and it appears that the classworlds library used by the Maven plexus code was resetting this value at the beginning of a build but never restoring it when a build completed. Any code running in Hudson/Jenkins as part of BuildPublisher plugins that relied on the classloader being the same would then fail. This is important for RTC because RTC is built on top of eclipse and eclipse does some funny caching of values internally based on the classloader. If the classloader changed, you would start getting various XML parsing errors due to eclipse libraries being unable to process the RESTful service requests being made.

          While simply saving and loading the classloader before and after running a build worked around a number of the classloader issues, there were still edge cases where things failed when various build errors, etc occurred. Because of this, I ended up rewriting our RTC plugin so that all RTC operations actually occur on a seperate worker thread with a known classloader. I used a ThreadPoolExecutor with a ThreadFactory that initialized threads with a known contextClassLoader based on a value snapshotted at initialization. Then, all RTC operations would be delegated to that thread by wrapping them in Callables and submitting them to the Executor. This approach did a better job at isolating RTC from the various classloader games played by Jenkins, although we still have a couple of mysterious issues where components of the classloader chain vanish after a while.

          The biggest gotcha with this approach has been its ability to support the master/slave system for distributed builds. I've not been able to get Jenkins to pass the right classes through the magic classloader it uses to proxy code onto the slave system. We managed to make slave systems work by adding the RTC libraries to the slave's systemClassPath before starting it up, but that means we can't run the slave as a service on windows or auto-start it on UNIX. Instead, we use a custom script that initialized the environment correctly to start the slave.

          Will Lauer added a comment - We ended up finding a workaround, but unfortunately, classloading issues continue to plague our implementation of an RTC/Jenkins plugin on and off. This is partially due to Jenkins, but also due to some strange things that RTC does in its libraries. The quick answer to the maven induced classloader issue was to save and restore the Thread.contextClassLoader set on any thread running RTC code. I dug into this a bit, and it appears that the classworlds library used by the Maven plexus code was resetting this value at the beginning of a build but never restoring it when a build completed. Any code running in Hudson/Jenkins as part of BuildPublisher plugins that relied on the classloader being the same would then fail. This is important for RTC because RTC is built on top of eclipse and eclipse does some funny caching of values internally based on the classloader. If the classloader changed, you would start getting various XML parsing errors due to eclipse libraries being unable to process the RESTful service requests being made. While simply saving and loading the classloader before and after running a build worked around a number of the classloader issues, there were still edge cases where things failed when various build errors, etc occurred. Because of this, I ended up rewriting our RTC plugin so that all RTC operations actually occur on a seperate worker thread with a known classloader. I used a ThreadPoolExecutor with a ThreadFactory that initialized threads with a known contextClassLoader based on a value snapshotted at initialization. Then, all RTC operations would be delegated to that thread by wrapping them in Callables and submitting them to the Executor. This approach did a better job at isolating RTC from the various classloader games played by Jenkins, although we still have a couple of mysterious issues where components of the classloader chain vanish after a while. The biggest gotcha with this approach has been its ability to support the master/slave system for distributed builds. I've not been able to get Jenkins to pass the right classes through the magic classloader it uses to proxy code onto the slave system. We managed to make slave systems work by adding the RTC libraries to the slave's systemClassPath before starting it up, but that means we can't run the slave as a service on windows or auto-start it on UNIX. Instead, we use a custom script that initialized the environment correctly to start the slave.

            Unassigned Unassigned
            wlauer wlauer
            Votes:
            1 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated: