dimanche 13 avril 2014

Java - nombre de mauvaise version de .class du dossier - comment trouver la classe à laquelle il est - Stack Overflow


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?




Well to check what JVM versions JARs in your classpath have, I would do something like the recipe below. But as I already mentioned in the last comment, this solution doesn't cover cases when a JAR have a custom classloader which modifies classpath and does other evil things. Nevertheless you may try to use this solution and see if it helps. If no, a more elaborate solution is required...



  1. retrieve a full classpath for your application. Usually I perform this step with Maven AppAssembler but certainly there're other ways. AppAssembler is handy because it builds a shell script with the classpath specified explicitly, so you may simply copy-paste it.


  2. Run the following bash script with the classpath as the argument. The script uses file, unzip and bc internally, so you should have these tools installed.



The script:


#!/bin/sh -e

# force numeric to use decimal point
LC_NUMERIC=C
export LC_NUMERIC

tempdir=

cleanup() {
trap - EXIT
if test -n "$tempdir"; then
rm -rf "$tempdir" ||:
fi
exit "$@"
}

debug() {
test -z "$DEBUG" || echo "$@" >&2 ||:
}

get_jar_version() {
local ver maxVersion compare
maxVersion=0.0
for ver in $(find "$1" -type f -name '*.class' -exec file {} \; | grep 'Java class data, version' | sed -e 's;.*Java class data, version ;;'); do
debug "Version = '$ver'"
compare=$(echo "$ver > $maxVersion" | bc)
if [ "x$compare" = "x1" ]; then
maxVersion="$ver"
fi
done
debug "maxVersion=$maxVersion"
echo -n "$maxVersion"
}

process_classpath_jars() {
trap cleanup EXIT
local cp jar oldIFS maxVersion
oldIFS="$IFS"
for cp in "$@"; do
IFS=':'
for jar in $cp; do
if [ -z "$jar" -o "${jar%.jar}" = "$jar" ]; then
continue
fi
debug "processing JAR $jar"
tempdir=$(mktemp -d jar.XXXXXXX)
unzip -qq "$jar" -d "$tempdir"
IFS="$oldIFS"
maxVersion=$(get_jar_version "$tempdir")
rm -rf "$tempdir" ||:
tempdir=
IFS=':'
echo "$jar is compiled for $maxVersion JVM"
done
IFS="$oldIFS"
done
}

process_classpath_jars "$@"


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?



Well to check what JVM versions JARs in your classpath have, I would do something like the recipe below. But as I already mentioned in the last comment, this solution doesn't cover cases when a JAR have a custom classloader which modifies classpath and does other evil things. Nevertheless you may try to use this solution and see if it helps. If no, a more elaborate solution is required...



  1. retrieve a full classpath for your application. Usually I perform this step with Maven AppAssembler but certainly there're other ways. AppAssembler is handy because it builds a shell script with the classpath specified explicitly, so you may simply copy-paste it.


  2. Run the following bash script with the classpath as the argument. The script uses file, unzip and bc internally, so you should have these tools installed.



The script:


#!/bin/sh -e

# force numeric to use decimal point
LC_NUMERIC=C
export LC_NUMERIC

tempdir=

cleanup() {
trap - EXIT
if test -n "$tempdir"; then
rm -rf "$tempdir" ||:
fi
exit "$@"
}

debug() {
test -z "$DEBUG" || echo "$@" >&2 ||:
}

get_jar_version() {
local ver maxVersion compare
maxVersion=0.0
for ver in $(find "$1" -type f -name '*.class' -exec file {} \; | grep 'Java class data, version' | sed -e 's;.*Java class data, version ;;'); do
debug "Version = '$ver'"
compare=$(echo "$ver > $maxVersion" | bc)
if [ "x$compare" = "x1" ]; then
maxVersion="$ver"
fi
done
debug "maxVersion=$maxVersion"
echo -n "$maxVersion"
}

process_classpath_jars() {
trap cleanup EXIT
local cp jar oldIFS maxVersion
oldIFS="$IFS"
for cp in "$@"; do
IFS=':'
for jar in $cp; do
if [ -z "$jar" -o "${jar%.jar}" = "$jar" ]; then
continue
fi
debug "processing JAR $jar"
tempdir=$(mktemp -d jar.XXXXXXX)
unzip -qq "$jar" -d "$tempdir"
IFS="$oldIFS"
maxVersion=$(get_jar_version "$tempdir")
rm -rf "$tempdir" ||:
tempdir=
IFS=':'
echo "$jar is compiled for $maxVersion JVM"
done
IFS="$oldIFS"
done
}

process_classpath_jars "$@"

0 commentaires:

Enregistrer un commentaire