Raipe Raipe - 4 days ago 5
Java Question

cglib throws an IllegalArgumentException when enhancing the java.util.Date class

I am trying to enhance

java.util.Date
with cglib. It does not work and I am not experienced with cglib, so I am wondering what is going wrong.

For example, the below code enhancing an
ArrayList
works:

@Test
public void enhance_ArrayList() {

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(ArrayList.class);
enhancer.setCallback(new FixedValue() {
@Override
public Object loadObject() throws Exception {
return "Hello cglib!";
}
});

ArrayList enhanced = (ArrayList)enhancer.create();
}


while the following code:

@Test
public void enhance_Date() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Date.class);
enhancer.setCallback(new FixedValue() {
@Override
public Object loadObject() throws Exception {
return "Hello cglib!";
}
});

Date enhanced = (Date)enhancer.create();
}


results in this exception:

java.lang.IllegalArgumentException
at org.objectweb.asm.ClassReader.<init>(Unknown Source)
at org.objectweb.asm.ClassReader.<init>(Unknown Source)
at org.objectweb.asm.ClassReader.<init>(Unknown Source)
at net.sf.cglib.proxy.BridgeMethodResolver.resolveAll(BridgeMethodResolver.java:61)
at net.sf.cglib.proxy.Enhancer.emitMethods(Enhancer.java:911)
at net.sf.cglib.proxy.Enhancer.generateClass(Enhancer.java:498)
at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216)
at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)
at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:285)

Answer

It seems like your are using a JDK in version 8 which includes class files in version 8. These class files are not supported by cglib as this library depends on an outdated version of ASM.

To debug this, we have to note that ASM does not include any debugging information and does not provide all information in its stack trace. All we know is that there is a IllegalArgumentException thrown from a constructor (named <init>) of its ClassReader. Looking at the source code reveals that there is only one possibility for such an exception. From the source code of ASM 4.2 which is used by the latest version of cglib, we can see that such an exception is only thrown if the class file is of a version that is unknown to the version of ASM:

// checks the class version
if (readShort(off + 6) > Opcodes.V1_7) {
  throw new IllegalArgumentException();
}

Unfortunately, there was not text message provided for this error, there is no real reason for why this is not the case, but we have to live with this. To fix this error, you would need a cglib version which depends on ASM 5+ which supports Java 8.

As of today, there is no compatible version of cglib available as cglib is not really maintained anymore. You might want to try out an alternative library such as Byte Buddy instead (note that I wrote this library, shameless plug). The enhancement would work something like this:

new ByteBuddy().subclass(Date.class)
  .method(named("toString"))
  .intercept(FixedValue.value("Hello world!"))
  .make()
  .load(getClass().getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
  .getLoaded()
  .newInstance();

which would override the toString method as Byte Buddy does not allow you to define classes with illegal return values.

Comments