web-dev-qa-db-fra.com

Comment lire mon fichier META-INF/MANIFEST.MF dans une application Spring Boot?

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
12
Ricardo Memoria

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)
    }
}
17
samsong8610

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

4
abosancic

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.

4
Andreas

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

2
Ricardo Memoria

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. 

1
usethe4ce
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;
}
1
outdev
    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.

0
Ihor M.