Récupérer la version d'une classe Java


Récupérer la version d'une classe Java


Un problème souvent rencontré java.lang.UnsupportedClassVersionError: Bad version number in .class file

Ceci est un problème que je rencontre souvent dans mes développements JAVA/J2EE étant donnée que j'utilise 3 versions de JDK (1.4 pour le boulot, 1.5 comme JVM par défaut, et 1.6 selon certaines besoins).

Rien de bien méchant, il me suffit de changer le JAVA_HOME pour pointer vers la bonne version :). Tout le souci est de savoir qu'elle est cette bonne version.

En mode lazy, on switchera vers la version la plus récente, mais cela peut avoir des conséquences assez gênantes/fatals, si votre environnement de déploiement tourne sur ancienne version pour une raison ou autres (politique, développement embarqué/pour mobile, ...).

L'Erreur java.lang.UnsupportedClassVersionError

L'erreur se produit sous la forme :

Exception in thread "main" java.lang.UnsupportedClassVersionError: Bad version number in .class file
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClass(Unknown Source)
        at java.security.SecureClassLoader.defineClass(Unknown Source)
        at java.net.URLClassLoader.defineClass(Unknown Source)
        at java.net.URLClassLoader.access$100(Unknown Source)
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClassInternal(Unknown Source)

ou encore:

java.lang.UnsupportedClassVersionError:  Main (Unsupported major.minor version 49.0)

ou encore class version mismatch.

Les versions des  fichiers classes Java

La version Java est contenue dans chaque fichier class, ce tableau référence les différentes versions, et la correspondance  entre le code version "interne" et le code version "humain". 

major version Version Java Valeur hexa  
45 1.0 ou 1.1 2D Chapeau d'avoir trouvé une class aussi vielle :D
46 1.2 2E  
47 1.3 2F  
48 1.4 30 Assert
49 5.0 31 @rocks :)
50 6 32 Powa
51 7 33 invokedynamic
52 8 34 lambda
53 9 35 jigsaw

Trouver la bonne version d'un fichier .class

Au lieu de jouer au pif afin de savoir quel version de la JVM il faut utiliser, et éviter les risques cité ci-haut, il faut trouver la bonne version à utiliser.

Les moyens sont multiples :

Linux Way

le fameux outil "file".

$ file Main.class
Main.class: compiled Java class data, version 49.0

vous pouvez même l'enrichir en modifiant le fichier magic number (sous ubuntu : /etc/magic) :

0 belong 0xcafebabe      Fichier Class Java
>6 beshort x Version Majeur %d.
>6 beshort =45 Necessite JRE 1.1 ou supérieur
>6 beshort =46 Necessite JRE 1.2 ou supérieur
>6 beshort =47 Necessite JRE 1.3 ou supérieur
>6 beshort =48 Necessite JRE 1.4 ou supérieur
>6 beshort =49 Necessite JRE 5.0 ou supérieur
>6 beshort =50 Necessite JRE 6.0 ou supérieur
>6 beshort >50 Necessite JRE 7 ou supérieur
Ce qui donne un truc plus cool/frenchy : $ file Main.class
Main.class: Fichier Class Java Version Majeur 49. Necessite JRE 5.0 ou supérieur

avec JDK 1.4

si vous possédez une JDK 1.4, le numéro de version est affiché dans le message d'erreur, comme vu plus haut (49.0).

javap (Java Disamsembler) /JDK 1.5

si vous possédez la JDK 1.5 ou supérieur, le plus simple est de lancer javap (Java Disamsembler ) sur le class qui cause le soucis :

$ javap -verbose Main | head -n 10

Compiled from "Main.java"
class Main extends java.lang.Object
  SourceFile: "Main.java"
  minor version: 0
 
major version: 49
  Constant pool:
const #1 = Method    #6.#15;    //  java/lang/Object."<init>":()V
const #2 = Field    #16.#17;    //  java/lang/System.out:Ljava/io/PrintStream;
const #3 = String    #18;    //  Hello world
const #4 = Method    #19.#20;    //  java/io/PrintStream.println:(Ljava/lang/String;)V

Le code qui nous intéresse se trouve au tout début, le reste c'est le byte-code java désassemblé.


Geek way

La méthode la plus ..."geek" est de faire appel à hexdump ou à votre éditeur hexadécimal préféré, pour cela il faudra connaitre le format du fichier .Class qui est le suivant :

4 premiers octets : correspondent à la 0xCAFEBABE : c'est le magic number qui définit que le fichier est un fichier .class,

ensuite viennent 4 octets représentant les versions, ils sont séparés en deux séries (de deux octets) : la version mineur (je me demande si ca sert à quel chose), et la fameuse version majeur qui nous intéresse :)

pour plus d'informations sur le format des fichiers .class vous pouvez consulter la doc SUN doc Oracle, ou la page wikipedia.

Générer un fichier Class pour une version JDK donnée

Et pour finir sachez que, si besoin, vous pouvez générer une class pour une version donnée, grâce aux paramètres -source et -target de javac :

$ javac -version
javac 1.6.0_07
$ javac -target 1.4  Main.java
-bootclasspath /opt/java.old/j2re1.4.2_18/lib/rt.jar
$ file Main.class
Main.class: Fichier Class Java Version Majeur 48. Necessite JRE 1.4 ou supérieur

 

Le compilateur java, en vérifiant le code va chercher les classes "internes" dans le chemin relative à javac. L'option -bootclasspath permet de changer ca, de cette façon on fait ce qu'on appelle du cross-compiling, et on évite certaines erreurs qui ne se détectent qu'au moment de l'exécution du style  java.lang.NoSuchMethodError.

Pour plus d'informations sur ce genre de problème, vous pouvez consulter l'article suivant.

Pour récupérer une vieille version du JRE/JDK, consultez le site de sun ou les archives.

Si vous utilisez jdk 1.5 ou supérieur, vous pouvez aussi rencontrez des soucis pour compiler pour une vielle version : javac: target release 1.4 conflicts with default source release 1.5. il s'agit en fait des assertions introduites dans la version 1.4 de java. Il faudra donc spécifier au compilateur quel version choisir (1.3 désactive, 1.4, 1.5 ...).

 

Sur ce, je vous souhaite un bon café :)

 

 


Poster un commentaire:

Nom/Name
Comment.

#.JJIJI (25 May 2015 - 02:08)

JK
 1
1 commentaires