J'essaie de lire mon fichier META-INF/MANIFEST.MF à partir de mon application Web Spring Boot (contenue dans un fichier jar).
J'essaie le code suivant:
InputStream is = getClass().getResourceAsStream("/META-INF/MANIFEST.MF");
Properties prop = new Properties();
prop.load( is );
Mais apparemment, il y a quelque chose dans les coulisses de Spring Boot qui charge un autre manifest.mf (et non le mien situé dans le dossier META-INF).
Est-ce que quelqu'un sait comment je peux lire mon application de manifeste dans mon application Spring Boot?
UPDATE: Après quelques recherches, j'ai remarqué qu'en utilisant le moyen habituel de lire un fichier manifest.mf, dans une application Spring Boot, c'est le fichier Jar auquel on accède
org.springframework.boot.loader.jar.JarFile
J'utilise Java.lang.Package pour lire l'attribut Implementation-Version
au démarrage du printemps à partir du manifeste.
String version = Application.class.getPackage().getImplementationVersion();
L'attribut Implementation-Version
doit être configuré dans build.gradle
jar {
baseName = "my-app"
version = "0.0.1"
manifest {
attributes("Implementation-Version": version)
}
}
C'est simple, il suffit d'ajouter ceci
InputStream is = this.getClass().getClassLoader().getResourceAsStream("META-INF/MANIFEST.MF");
Properties prop = new Properties();
try {
prop.load( is );
} catch (IOException ex) {
Logger.getLogger(IndexController.class.getName()).log(Level.SEVERE, null, ex);
}
Travailler pour moi.
Remarque:
getClass (). getClassLoader () est important
et
"META-INF/MANIFEST.MF" pas "/META-INF/MANIFEST.MF"
Merci Aleksandar
Presque tous les fichiers jar sont livrés avec un fichier manifeste. Votre code renvoie donc le fichier premier qu'il peut trouver dans le chemin de classe.
Pourquoi voudriez-vous le manifeste de toute façon? C'est un fichier utilisé par Java. Placez les valeurs personnalisées dont vous avez besoin ailleurs, comme dans un fichier .properties
à côté de votre fichier .class
.
Mise à jour 2
Comme mentionné dans un commentaire ci-dessous, not dans la question, le véritable objectif est l'information de version du manifeste. Java fournit déjà ces informations avec la classe Java.lang.Package
.
N'essayez pas de lire le manifeste vous-même, même si vous pouviez le trouver.
Mise à jour 1
Notez que le fichier manifeste est pas un fichier Properties
. Sa structure est beaucoup plus complexe que cela.
Voir l'exemple dans la documentation Java de spécification de fichier JAR .
Manifest-Version: 1.0
Created-By: 1.7.0 (Sun Microsystems Inc.)
Name: common/class1.class
SHA-256-Digest: (base64 representation of SHA-256 digest)
Name: common/class2.class
SHA1-Digest: (base64 representation of SHA1 digest)
SHA-256-Digest: (base64 representation of SHA-256 digest)
Comme vous pouvez le constater, Name
et SHA-256-Digest
apparaissent plusieurs fois. La classe Properties
ne peut pas gérer cela, car il s'agit simplement d'une Map
et les clés doivent être uniques.
Après avoir testé et recherché des documents Spring, j'ai trouvé un moyen de lire le fichier manifeste.
Tout d'abord, Spring Boot implémente son propre ClassLoader qui modifie le mode de chargement des ressources. Lorsque vous appelez getResource()
, Spring Boot charge la première ressource correspondant au nom de ressource indiqué dans la liste de tous les fichiers JAR disponibles dans le chemin de classe. Votre fichier jar d'application n'est pas le premier choix.
Ainsi, lors de l'émission d'une commande comme:
getClassLoader().getResourceAsStream("/META-INF/MANIFEST.MF");
Le premier fichier MANIFEST.MF trouvé dans un fichier Jar du chemin de classe est renvoyé. Dans mon cas, cela provenait d'une bibliothèque de jar JDK.
SOLUTION:
J'ai réussi à obtenir une liste de tous les fichiers JAR chargés par l'application contenant la ressource "/META-INF/MANIFEST.MF" et à vérifier si la ressource provenait de mon fichier jar d'application. Si tel est le cas, lisez le fichier MANIFEST.MF et revenez dans l'application comme suit:
private Manifest getManifest() {
// get the full name of the application manifest file
String appManifestFileName = this.getClass().getProtectionDomain().getCodeSource().getLocation().toString() + JarFile.MANIFEST_NAME;
Enumeration resEnum;
try {
// get a list of all manifest files found in the jars loaded by the app
resEnum = Thread.currentThread().getContextClassLoader().getResources(JarFile.MANIFEST_NAME);
while (resEnum.hasMoreElements()) {
try {
URL url = (URL)resEnum.nextElement();
// is the app manifest file?
if (url.toString().equals(appManifestFileName)) {
// open the manifest
InputStream is = url.openStream();
if (is != null) {
// read the manifest and return it to the application
Manifest manifest = new Manifest(is);
return manifest;
}
}
}
catch (Exception e) {
// Silently ignore wrong manifests on classpath?
}
}
} catch (IOException e1) {
// Silently ignore wrong manifests on classpath?
}
return null;
}
Cette méthode renverra toutes les données d'un fichier manifest.mf dans un objet Manifest.
J'ai emprunté une partie de la solution à en lisant le fichier MANIFEST.MF à partir d'un fichier jar avec Java
Je l'exploite en exploitant la résolution des ressources de Spring:
@Service
public class ManifestService {
protected String ciBuild;
public String getCiBuild() { return ciBuild; }
@Value("${manifest.basename:/META-INF/MANIFEST.MF}")
protected void setManifestBasename(Resource resource) {
if (!resource.exists()) return;
try (final InputStream stream = resource.getInputStream()) {
final Manifest manifest = new Manifest(stream);
ciBuild = manifest.getMainAttributes().getValue("CI-Build");
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Ici, nous obtenons CI-Build
, et vous pouvez facilement étendre l’exemple pour charger d’autres attributs.
public Properties readManifest() throws IOException {
Object inputStream = this.getClass().getProtectionDomain().getCodeSource().getLocation().getContent();
JarInputStream jarInputStream = new JarInputStream((InputStream) inputStream);
Manifest manifest = jarInputStream.getManifest();
Attributes attributes = manifest.getMainAttributes();
Properties properties = new Properties();
properties.putAll(attributes);
return properties;
}
try {
final JarFile jarFile = (JarFile) this.getClass().getProtectionDomain().getCodeSource().getLocation().getContent();
final Manifest manifest = jarFile.getManifest();
final Map<Object, Object> manifestProps = manifest.getMainAttributes().entrySet().stream()
.collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue()));
...
} catch (final IOException e) {
LOG.error("Unable to read MANIFEST.MF", e);
...
}
Cela ne fonctionnera que si vous lancez votre application via la commande Java -jar
. Cela ne fonctionnera pas si l'on crée un test d'intégration.