Bad version number in .class file - how to find which class it is

class, java

Solution

One way to catch almost all class definitions is to use a Java Agent using the Instrumentation API. This API allows to transform all byte codes before they are used (with the exception of some of the core classes) but, of course, you can use it to just extract the version number and generate a report without changing the byte code.

One nice property of the class file transformation is that the JVM will call the transformers before rejecting malformed/ unsupported byte code to give them a chance to transform the code to something usable. So we will see the responsible byte code before the `UnsupportedClassVersionError` is thrown.

Here is the code of the Java Agent which does the job:

package versioncheck;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;

public class CheckAgent implements ClassFileTransformer
{
  public static void premain(String agentArgs, Instrumentation inst)
  {
    inst.addTransformer(new CheckAgent(), inst.isRetransformClassesSupported());
  }
  public static void agentmain(String agentArgs, Instrumentation inst)
  {
    inst.addTransformer(new CheckAgent(), inst.isRetransformClassesSupported());
  }
  private int supported;
  CheckAgent()
  {
    supported=(int)Double.parseDouble(System.getProperty("java.class.version"));
  }
  public byte[] transform(ClassLoader loader, String className,
      Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
      byte[] classfileBuffer) throws IllegalClassFormatException
  {
    int version=(classfileBuffer[6]&0xff)<<8 | (classfileBuffer[7]&0xff);
    if(version>supported)
      System.out.println(className+" v"+version+" from "
      +protectionDomain.getCodeSource());
    return null;
  }
}

Then you need to put this class in a jar together with a manifest containing the following lines:

Premain-Class: versioncheck.CheckAgent
Agent-Class: versioncheck.CheckAgent
Can-Redefine-Classes: true

And the last thing to do is to add the following parameter to command line which starts your Java application: `-javaagent:pathtothe.jar`

See also http://docs.oracle.com/javase/7/docs/api/java/lang/instrument/package-summary.html

As a side note, such a Java Agent could also fix the problem by transforming (down-porting) the byte code to a lower version. But that’s far beyond the original question…

Problem

I believe I've seen all of the other posts on this issue. I think this is a different question because I am looking for a way to determine which class is causing the problem. I build my jar using Maven. If I ask it to build for Java 5 and I run it under Java 6 it works fine. If I ask it to build for Java 6 and I run it under Java 6 it works fine. If I ask it to build for Java 5 and I run it under Java 5 it fails: ``` java.lang.RuntimeException: public static void ....main(java.lang.String[]) failed for arguments (String[]{...}) Caused by: java.lang.reflect.InvocationTargetException: ... Caused by: java.lang.UnsupportedClassVersionError: Bad version number in .class file at java.lang.ClassLoader.defineClass1(Native Method) ... ``` I have inspected the jar using Java Version Check and it reports all classes in the jar as built for Java 5. I can only conclude that it is hacking its own classpath and linking up with some other library/jar on my machine that is not Java 5. This is quite possible but I do not have access to the source of that process and as far as I am aware I cannot single-step into the class loader to find out which class it is loading. Are there any techniques I can use that will help me work out which class is causing the exception?

Original source