Comment savoir quelle version du compilateur Java a été utilisée pour créer un fichier jar? J'ai un fichier jar, et il aurait pu être construit dans l'un des trois JDK. Nous devons savoir exactement lequel pour pouvoir certifier la compatibilité. La version du compilateur est-elle intégrée quelque part dans les fichiers de classe ou le fichier jar?
Vous ne pouvez pas nécessairement savoir à partir du fichier JAR lui-même.
Téléchargez un éditeur hexadécimal, ouvrez l'un des fichiers de classe à l'intérieur du fichier JAR et examinez les décalages d'octet 4 à 7. Les informations de version sont intégrées.
http://en.wikipedia.org/wiki/Java_class_file
Remarque: comme mentionné dans le commentaire ci-dessous,
ces octets vous indiquent quelle version la classe a été compilée POUR, pas quelle version l'a compilée.
jar
est simplement un conteneur. C'est une archive de fichier à la tar
. Bien que jar
puisse contenir des informations intéressantes dans sa hiérarchie META-INF , il n’est pas obligatoire de spécifier l’époque des classes dans son contenu. Pour cela, il faut examiner les fichiers class
.
Comme Peter Lawrey mentionné dans le commentaire de la question initiale, vous ne pouvez pas nécessairement savoir quelle version du JDK a construit un fichier class
donné, mais vous pouvez trouver la version de classe de code en octets du fichier class
contenue dans jar
.
Oui, c'est un peu nul, mais la première étape consiste à extraire une ou plusieurs classes de jar
. Par exemple:
$ jar xf log4j-1.2.15.jar
Sous Linux, Mac OS X ou Windows avec Cygwin installé, la commande fichier (1) connaît la version de la classe.
$ file ./org/Apache/log4j/Appender.class
./org/Apache/log4j/Appender.class: compiled Java class data, version 45.3
Ou bien, utiliser javap
à partir du JDK comme @ jikes.Thunderbolt fait remarquer:
$ javap -v ./org/Apache/log4j/Appender.class | grep major
major version: 45
Et si vous êtes relégué dans un environnement Windows
sans file
ni grep
> javap -v ./org/Apache/log4j/Appender.class | findstr major
major version: 45
FWIW, je conviens que javap
en dira beaucoup plus sur un fichier class
donné que la question initiale posée.
Quoi qu'il en soit, une version de classe différente, par exemple:
$ file ~/bin/classes/P.class
/home/dave/bin/classes/P.class: compiled Java class data, version 50.0
Le numéro majeur de version de classe correspond aux versions Java JDK suivantes:
Voici comment Java trouve ces informations.
Windows: javap -v <class> | findstr major
Unix: javap -v <class> | grep major
Par exemple:> javap -v Application | findstr major major version: 51
Le compilateur Java (javac
) ne construit pas de fichiers jar, il traduit les fichiers Java en fichiers de classe. L'outil Jar (jar
) crée les pots réels. Si aucun manifeste personnalisé n'a été spécifié, le manifeste par défaut précisera quelle version du JDK a été utilisée pour créer le fichier JAR.
Il n’est pas nécessaire de décompresser le fichier JAR (si l’un des noms de classe est connu ou recherché, par exemple à l’aide de 7Zip), les éléments suivants sont suffisants sous Windows:
javap -cp log4j-core-2.5.jar -verbose org.Apache.logging.log4j.core.Logger | findstr major
Comme j'avais besoin d'analyser de gros pots, je m'intéressais à la version de chaque classe dans un fichier jar. Par conséquent, j’ai adopté l’approche de Joe Liversedge https://stackoverflow.com/a/27877215/1497139 et l’a combinée avec David J. Liszewski ' https://stackoverflow.com/a/3313839/1497139 table de version de numéro de classe pour créer un script jarv de bash afin d'afficher les versions de tous les fichiers de classe d'un fichier jar.
usage
usage: ./jarv jarfile
-h|--help: show this usage
Exemple
jarv $Home/.m2/repository/log4j/log4j/1.2.17/log4j-1.2.17.jar
Java 1.4 org.Apache.log4j.Appender
Java 1.4 org.Apache.log4j.AppenderSkeleton
Java 1.4 org.Apache.log4j.AsyncAppender$DiscardSummary
Java 1.4 org.Apache.log4j.AsyncAppender$Dispatcher
...
Bash script jarv
#!/bin/bash
# WF 2018-07-12
# find out the class versions with in jar file
# see https://stackoverflow.com/questions/3313532/what-version-of-javac-built-my-jar
# uncomment do debug
# set -x
#ansi colors
#http://www.csc.uvic.ca/~sae/seng265/fall04/tips/s265s047-tips/bash-using-colors.html
blue='\033[0;34m'
red='\033[0;31m'
green='\033[0;32m' # '\e[1;32m' is too bright for white bg.
endColor='\033[0m'
#
# a colored message
# params:
# 1: l_color - the color of the message
# 2: l_msg - the message to display
#
color_msg() {
local l_color="$1"
local l_msg="$2"
echo -e "${l_color}$l_msg${endColor}"
}
#
# error
#
# show an error message and exit
#
# params:
# 1: l_msg - the message to display
error() {
local l_msg="$1"
# use ansi red for error
color_msg $red "Error: $l_msg" 1>&2
exit 1
}
#
# show the usage
#
usage() {
echo "usage: $0 jarfile"
# -h|--help|usage|show this usage
echo " -h|--help: show this usage"
exit 1
}
#
# showclassversions
#
showclassversions() {
local l_jar="$1"
jar -tf "$l_jar" | grep '.class' | while read classname
do
class=$(echo $classname | sed -e 's/\.class$//')
class_version=$(javap -classpath "$l_jar" -verbose $class | grep 'major version' | cut -f2 -d ":" | cut -c2-)
class_pretty=$(echo $class | sed -e 's#/#.#g')
case $class_version in
45.3) Java_version="Java 1.1";;
46) Java_version="Java 1.2";;
47) Java_version="Java 1.3";;
48) Java_version="Java 1.4";;
49) Java_version="Java5";;
50) Java_version="Java6";;
51) Java_version="Java7";;
52) Java_version="Java8";;
53) Java_version="Java9";;
54) Java_version="Java10";;
*) Java_version="x${class_version}x";;
esac
echo $Java_version $class_pretty
done
}
# check the number of parameters
if [ $# -lt 1 ]
then
usage
fi
# start of script
# check arguments
while test $# -gt 0
do
case $1 in
# -h|--help|usage|show this usage
-h|--help)
usage
exit 1
;;
*)
showclassversions "$1"
esac
shift
done
vous pouvez trouver la version du compilateur Java à partir de fichiers .class à l'aide d'un éditeur hexadécimal.
Étape 1: Extraire les fichiers .class du fichier jar à l'aide d'un extracteur Zip
étape 2: ouvrez le fichier .class avec un éditeur hexadécimal (j'ai utilisé le plugin notepad ++ hex editor. Ce plugin lit le fichier en tant que binaire et l'affiche en hexadécimal).
Les index 6 et 7 donnent le numéro de version majeur du format de fichier de classe utilisé. https://en.wikipedia.org/wiki/Java_class_file
Java SE 11 = 55 (0x37 hex)
Java SE 10 = 54 (0x36 hex)
Java SE 9 = 53 (0x35 hex)
Java SE 8 = 52 (0x34 hex),
Java SE 7 = 51 (0x33 hex),
Java SE 6.0 = 50 (0x32 hex),
Java SE 5.0 = 49 (0x31 hex),
JDK 1,4 = 48 (0x30 hex),
JDK 1,3 = 47 (0x2F hex),
JDK 1.2 = 46 (0x2E hex),
JDK 1.1 = 45 (0x2D hex).
Le code posté par Owen peut vous dire les informations mentionnées par un certain nombre d'autres réponses ici:
public void simpleExample ()
{
FileInputStream fis = new FileInputStream ("mytest.class");
parseJavaClassFile ( fis );
}
protected void parseJavaClassFile ( InputStream classByteStream ) throws Exception
{
DataInputStream dataInputStream = new DataInputStream ( classByteStream );
magicNumber = dataInputStream.readInt();
if ( magicNumber == 0xCAFEBABE )
{
int minorVer = dataInputStream.readUnsignedShort();
int majorVer = dataInputStream.readUnsignedShort();
// do something here with major & minor numbers
}
}
Voir aussi this et this site. J'ai fini par modifier rapidement le code de Mind Products pour vérifier le but de chacune de mes dépendances.
Vous pouvez dire à la version binaire Java par inspectant les 8 premiers octets (ou en utilisant un app qui peut).
À ma connaissance, le compilateur lui-même n'insère aucune signature d'identification. Je ne peux pas repérer une telle chose dans le fichier format de classe de spécification VM de toute façon.
Une doublure (Linux)
unzip -p mylib.jar META-INF/MANIFEST.MF
Ceci affiche le contenu du fichier MANIFEST.MF
sur la sortie standard (espérons-le, il y en a un dans votre fichier jar :)
En fonction de la construction de votre package, vous trouverez la version du JDK dans la touche Created-By
ou Build-Jdk
.
Les développeurs et les administrateurs qui exécutent Bash peuvent trouver ces fonctions pratiques utiles:
jar_jdk_version() {
[[ -n "$1" && -x "`command -v javap`" ]] && javap -classpath "$1" -verbose $(jar -tf "$1" | grep '.class' | head -n1 | sed -e 's/\.class$//') | grep 'major version' | sed -e 's/[^0-9]\{1,\}//'
}
print_jar_jdk_version() {
local version
version=$(jar_jdk_version "$1")
case $version in 49) version=1.5;; 50) version=1.6;; 51) version=1.7;; 52) version=1.8;; esac
[[ -n "$version" ]] && echo "`basename "$1"` contains classes compiled with JDK version $version."
}
Vous pouvez les coller pour une utilisation unique ou les ajouter à ~/.bash_aliases
ou ~/.bashrc
. Les résultats ressemblent à quelque chose comme:
$ jar_jdk_version poi-ooxml-3.5-FINAL.jar
49
et
$ print_jar_jdk_version poi-ooxml-3.5-FINAL.jar
poi-ooxml-3.5-FINAL.jar contains classes compiled with JDK version 1.5.
EDIT Comme le souligne jackrabbit , vous ne pouvez pas compter à 100% sur le manifeste pour vous dire quelque chose d'utile. Si tel était le cas, vous pourriez l'extraire dans votre shell UNIX préféré avec unzip
:
$ unzip -pa poi-ooxml-3.5-FINAL.jar META-INF/MANIFEST.MF
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.7.1
Created-By: 11.3-b02 (Sun Microsystems Inc.)
Built-By: yegor
Specification-Title: Apache POI
Specification-Version: 3.5-FINAL-20090928
Specification-Vendor: Apache
Implementation-Title: Apache POI
Implementation-Version: 3.5-FINAL-20090928
Implementation-Vendor: Apache
Ce fichier .jar n’a rien d’utile dans le manifeste concernant les classes contenues.
Chaque fichier de classe a un numéro de version incorporé pour le niveau de code d'octet que la machine virtuelle Java utilise pour voir si elle aime ou non cette partie de code d'octet particulière. Il s'agit de 48 pour Java 1,4, 49 pour Java 1,5 et 50 pour Java 6.
Il existe de nombreux compilateurs pouvant générer du code octet à chaque niveau, javac utilise l'option "-target" pour indiquer le niveau de code octet à générer, et le javac Java 6 peut générer du code octet pour au moins 1.4, 1.5 et 6. Je ne crois pas que le compilateur insère quoi que ce soit qui puisse identifier le compilateur lui-même, ce que je pense que vous demandez. De plus, le compilateur Eclipse est de plus en plus utilisé, car il s’agit d’un seul fichier jar pouvant être exécuté avec le JRE uniquement.
Dans un fichier jar, il existe généralement de nombreuses classes, chacune étant indépendante, vous devez donc examiner toutes les classes du fichier jar pour être certain des caractéristiques du contenu.
Pour développer Jonathon Faust et McDowell réponses: Si vous utilisez un système basé sur * nix, vous pouvez utiliser od
(l'un des premiers programmes Unix1 devrait être disponible pratiquement partout) pour interroger le fichier .class
sur un niveau binaire:
od -An -j7 -N1 -t dC SomeClassFile.class
Cela produira les valeurs entières familières, par ex. 50
pour Java 5
, 51
pour Java 6
et ainsi de suite.
1 Citation de https://en.wikipedia.org/wiki/Od_ (Unix)
Pour faire suite à la réponse de @David J. Liszewski, j’ai exécuté les commandes suivantes pour extraire le manifeste du fichier jar sous Ubuntu:
# Determine the manifest file name:
$ jar tf LuceneSearch.jar | grep -i manifest
META-INF/MANIFEST.MF
# Extract the file:
$ Sudo jar xf LuceneSearch.jar META-INF/MANIFEST.MF
# Print the file's contents:
$ more META-INF/MANIFEST.MF
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.8.2
Created-By: 1.7.0_25-b30 (Oracle Corporation)
Main-Class: org.wikimedia.lsearch.config.StartupManager
Bien souvent, vous pouvez regarder des fichiers jar entiers ou des fichiers war contenant beaucoup de fichiers jar en plus d'eux-mêmes.
Comme je ne voulais pas vérifier manuellement chaque classe, j'ai écrit un programme Java pour le faire:
https://github.com/Nthalk/WhatJDK
./whatjdk some.war
some.war:WEB-INF/lib/xml-apis-1.4.01.jar contains classes compatible with Java1.1
some.war contains classes compatible with Java1.6
Bien que cela ne dise pas avec quoi la classe a été compilée, cela détermine ce que JDK sera capable de charger les classes, ce qui est probablement ce que vous vouliez commencer.
Vous archivez dans le fichier manifeste d'un exemple de fichier jar:
Manifest-Version: 1.0 Création-Par: 1.6.0 (IBM Corporation)
J'ai également écrit mon propre script bash pour vider la version Java requise par tous les fichiers jars passés en ligne de commande ... La mienne est un peu approximative, mais fonctionne pour moi ;-)
$ jar_dump_version_of_jvm_required.sh *.jar
JVM VERSION REQUIRED: 46.0, /private/tmp/jars/WEB-INF/lib/json-simple-1.1.jar
JVM VERSION REQUIRED: 49.0, /private/tmp/jars/WEB-INF/lib/json-smart-1.1.1.jar
JVM VERSION REQUIRED: 50.0, /private/tmp/jars/WEB-INF/lib/jsontoken-1.0.jar
JVM VERSION REQUIRED: 50.0, /private/tmp/jars/WEB-INF/lib/jsr166y-1.7.0.jar
#!/bin/bash
DIR=$(PWD)
function show_help()
{
ME=$(basename $0)
IT=$(cat <<EOF
Dumps the version of the JVM required to run the classes in a jar file
usage: $ME JAR_FILE
e.g.
$ME myFile.jar -> VERSION: 50.0 myFile.jar
Java versions are:
54 = Java 10
53 = Java 9
52 = Java 8
51 = Java 7
50 = Java 6
49 = Java 5
48 = Java 1.4
47 = Java 1.3
46 = Java 1.2
45.3 = Java 1.1
EOF
)
echo "$IT"
exit
}
if [ "$1" == "help" ]
then
show_help
fi
if [ -z "$1" ]
then
show_help
fi
function unzipJarToTmp()
{
JAR=$1
CLASS_FILE=$(jar -tf "$JAR" | grep \.class$ | grep -v '\$' | head -n1 | awk '{print $NF}')
OUT_FILE="$CLASS_FILE"
#echo "J=$JAR C=$CLASS_FILE O=$OUT_FILE"
jar xf "$JAR" "$CLASS_FILE"
MAJOR=$(javap -v "$OUT_FILE" 2>&1 | grep major | awk -F' ' '{print $3'})
MINOR=$(javap -v "$OUT_FILE" 2>&1 | grep minor | awk -F' ' '{print $3'})
if [ -z "$MAJOR" ]
then
echo "JVM VERSION REQUIRED: NA as no classes in $JAR"
else
echo "JVM VERSION REQUIRED: $MAJOR.$MINOR, $JAR"
fi
}
# loop over cmd line args
for JAR in "$@"
do
cd "$DIR"
JAR_UID=$(basename "$JAR" | sed s/.jar//g)
TMPDIR=/tmp/jar_dump/$JAR_UID/
mkdir -p "$TMPDIR"
JAR_ABS_PATH=$(realpath $JAR)
cd "$TMPDIR"
#echo "$JAR_ABS_PATH"
unzipJarToTmp "$JAR_ABS_PATH"
#sleep 2
done