While working on having the latest version of glu compile with jdk1.6 and run with either jdk1.6 or jdk1.7 I ran into the following error raised by groovy:

Caused by: java.lang.IncompatibleClassChangeError: the number of constructors 
during runtime and compile time for java.lang.RuntimeException do not match. 
Expected 4 but got 5 
info_48x48.pngThe code in this blog has been updated on 2013/05/13 to include a fix for the fact that the workaround would not stick due to weak references.

The issue

The issue gets triggered when running any groovy code compiled with jdk1.6 and running with jdk1.7 whenever you are throwing an exception that has been written in groovy (meaning your exception is defined in a .groovy file).

The source of the problem

This exception is triggered by the following section in the groovy source code

public int selectConstructorAndTransformArguments(int numberOfConstructors, Object[] arguments) {
  //TODO: that is just a quick prototype, not the real thing!
  if (numberOfConstructors != constructors.size()) {
      throw new IncompatibleClassChangeError("the number of constructors during runtime and compile time for " +
              this.theClass.getName() + " do not match. Expected " + numberOfConstructors + " but got " + constructors.size());
  }
  // ... clipped remainder of the method...
}

This is pretty frustrating as the comment seems to indicate that this was added as a quick prototype. Also the numberOfConstructors variable is not used in the remainder of the method!

In java 1.7, there is one more constructor that has been added to the java.lang.Exception class (a first in many years!). So at runtime, the previous code simply fails and throws IncompatibleClassChangeError.

The solution(s)

Compile and run under jdk1.7

If you don’t need the backward compatibility, you can always (re)compile your code with jdk1.7 and run with jdk1.7.

The problem here is that if you use any groovy library compiled with jdk1.6 (or earlier), then you will have the same problem.

Changing your groovy exceptions into java exceptions

Doing a quick search on the Internet, I realized I was not the only one and found this solution.

This solution does indeed work and if you can change all your exceptions that were groovy exceptions into java exceptions then the problem does go away.

The problem is that in my case, it was a lot of changes in several different projects.

Also you have the same issue as in the previous section: if you use any groovy library compiled with jdk1.6 (or earlier), then you will have the same problem.

Removing the “extra” constructor at runtime

I came up with a different approach which simply removes the extra constructor added by jdk1.7 at runtime. The reasoning behind this is, since I am compiling with jdk1.6 there is no way I can use this extra constructor in my code since it did not exist!

So here is what I did:

static void installWorkaround()
{
  if(getJavaVersionAsDouble() >= 1.7)
  {
    def metaClass = Exception.metaClass

      if(metaClass.hasProperty("__gluExceptionJdk17Workaround"))
      {
        log.info "Running with jdk1.7: groovy Exception workaround already installed"
      }
      else
      {
        def field = MetaClassImpl.class.getDeclaredField('constructors')
        field.setAccessible(true)
        def constructors = field.get(metaClass)

        int jdk7ConstructorIdx = -1

        constructors.array.eachWithIndex { elt, idx ->
          if(elt.cachedConstructor.parameterTypes.size() == 4)
            jdk7ConstructorIdx = idx
        }

        if(jdk7ConstructorIdx != -1)
        {
          constructors.remove(jdk7ConstructorIdx)
          // this has 2 purposes:
          // 1) allowing to check the workaround
          // 2) making sure that the metaclass is no longer a weak reference, hence is not garbage collected!
          metaClass.__gluExceptionJdk17Workaround = true
          log.info "Running with jdk1.7: installed groovy Exception workaround"
        }
      }

      // making sure the workaround is in place...
      if(!new Exception().__gluExceptionJdk17Workaround)
        throw new RuntimeException("groovy exception workaround not set properly!!!")
  }
  else
  {
    log.debug "Running with jdk1.6: non workaround necessary"
  }
}

This code modifies the metaclass of the java.lang.Exception class to remove the constructor added by jdk1.7. Note that this code is pretty safe to call many times but is recommended to be called as early as possible when you start your groovy application.

From my own testing this works very well and the original IncompatibleClassChangeError is no longer thrown. I have asked for feedback on the groovy forum in regards to this solution but have not received any as of this writing.

Note that I am using groovy version 2.0.7 (and this problem remains on the (groovy) trunk as of this writing). I have not tried this solution with any other version of groovy.

Reference