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

RemoteClassLoader: ClassFormatError c.f. UnsupportedClassVersionError


    • Icon: Bug Bug
    • Resolution: Fixed
    • Icon: Minor Minor
    • remoting
    • None

      When the RemoteClassLoader encounters a Java class file whose version is too high, it ends up throwing ClassFormatError.

      This comes as a nasty surprise to software packages that expect to receive (and trap, and recover from) UnsupportedClassVersionError.

      In particular, this breaks the JGit functionality in the git-client-plugin (versions 1.19.1 to 1.19.7) if it executes on a slave that's running JDK 1.6.

      (Yes, I realize that JDK 1.6 is ancient history so this particular example may be moot now, but the same general problem may reappear in the long march from 1.7 to 1.8 or 1.8 to 1.9).

      jgit-client-plugin 1.9.0 and below don't experience this problem because they didn't include the dependency that triggers this, org.eclipse.jgit:org.eclipse.jgit.java7. That dependency was added in git-client-plugin 1.19.1 with this checkin comment:

      JENKINS-30371 Distribute JGit java7 jar to allow JGit symlink support
      JGit symlink support is optional with JGit 3.7.1. It requires a copy of
      the JGit java7 jar, a Java 7 VM, and a file system which supports symbolic
      links. Including the jar file in a Java 6 environment is safe because
      the JGit code won't load the additional jar when running on Java 6.

      That last sentence is probably true in an environment where the RemoteClassLoader isn't involved (e.g. on master?) but definitely not on JDK 1.6 slaves.

      There's nothing that the git-client-plugin can (or should) do to fix this; the code that's expecting to receive and handle UnsupportedClassVersionError is in org.eclipse.jgit:org.eclipse.jgit.

      (I should probably say "was in" because newer versions of org.eclipse.jgit have made JDK 1.7 required rather than optional).

      My (quite likely clueless) 0.02 re the code in RemoteClassLoader.loadClassFile() is roughly:

      • In the case where it checks bytecodeLevel itself it should throw UnsupportedClassVersionError (not ClassFormatError)
      • In the case where it wraps try-catch around the call to defineClass(), the current catch-block for ClassFormatError also catches the UnsupportedClassVersionError subtype and ends up throwing a ClassFormatError instead. It should, somehow or other, preserve the subtype (UnsupportedClassVersionError).

      Perhaps that second path is never exercised now (because of the explicit bytecodeLevel check above)? I'm not sure. But I definitely got bitten by it today, admittedly using antique versions of Jenkins components (and JDK 1.6).

      FWIW, when I encountered that second case, the UnsupportedClassVersionError that got caught and wrapped as ClassFormatError already displayed this:

      java.lang.UnsupportedClassVersionError: org/eclipse/jgit/util/Java7FSFactory : Unsupported major.minor version 51.0

      i.e. the catch-and-wrap-in-order-to-add-the-class-name rigmarole wasn't beneficial because the class name was already there. But I haven't checked whether that's always true.

            oleg_nenashev Oleg Nenashev
            maslen Thomas Maslen
            0 Vote for this issue
            3 Start watching this issue