web-dev-qa-db-fra.com

Obtenir la version d'artefact Maven au moment de l'exécution

J'ai remarqué que dans le fichier JAR d'un artefact Maven, l'attribut project.version est inclus dans deux fichiers:

META-INF/maven/${groupId}/${artifactId}/pom.properties
META-INF/maven/${groupId}/${artifactId}/pom.xml

Existe-t-il un moyen recommandé de lire cette version au moment de l'exécution?

166
Armand

Vous ne devriez pas avoir besoin d'accéder à des fichiers spécifiques à Maven pour obtenir les informations de version d'une bibliothèque/classe donnée.

Vous pouvez simplement utiliser getClass().getPackage().getImplementationVersion() pour obtenir les informations de version stockées dans un fichier .jar MANIFEST.MFHeureusement, Maven est assez intelligent Malheureusement, Maven n'écrit pas les informations correctes dans le manifeste également par défaut!

Au lieu de cela, il faut modifier le <archive> _ élément de configuration du maven-jar-plugin pour définir addDefaultImplementationEntries et addDefaultSpecificationEntries sur true, comme ceci:

<plugin>
    <groupId>org.Apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <configuration>
        <archive>                   
            <manifest>
                <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
                <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
            </manifest>
        </archive>
    </configuration>
</plugin>

Idéalement, cette configuration devrait être mise dans la société pom ou dans une autre base-pom.

Documentation détaillée du <archive> L'élément se trouve dans le documentation Maven Archive .

250
Joachim Sauer

Pour donner suite à la réponse ci-dessus, pour un .war _ artefact, j’ai trouvé qu’il me fallait appliquer la configuration équivalente à maven-war-plugin, plutôt que maven-jar-plugin:

<plugin>
    <artifactId>maven-war-plugin</artifactId>
    <version>2.1</version>
    <configuration>
        <archive>                   
            <manifest>
                <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
                <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
            </manifest>
        </archive>
    </configuration>
</plugin>

Cela a ajouté les informations de version à MANIFEST.MF dans le projet .jar (inclus dans WEB-INF/lib du .war)

73
Rob

Voici une méthode pour obtenir la version du fichier pom.properties, puis le récupérer du manifeste

public synchronized String getVersion() {
    String version = null;

    // try to load from maven properties first
    try {
        Properties p = new Properties();
        InputStream is = getClass().getResourceAsStream("/META-INF/maven/com.my.group/my-artefact/pom.properties");
        if (is != null) {
            p.load(is);
            version = p.getProperty("version", "");
        }
    } catch (Exception e) {
        // ignore
    }

    // fallback to using Java API
    if (version == null) {
        Package aPackage = getClass().getPackage();
        if (aPackage != null) {
            version = aPackage.getImplementationVersion();
            if (version == null) {
                version = aPackage.getSpecificationVersion();
            }
        }
    }

    if (version == null) {
        // we could not compute the version so use a blank
        version = "";
    }

    return version;
} 
27
mysomic

J'ai passé un peu de temps sur les deux approches principales ici et elles n'ont pas fonctionné pour moi. J'utilise Netbeans pour les builds, peut-être y a-t-il plus qui se passe là-bas. J'ai eu quelques erreurs et avertissements de Maven 3 avec quelques constructions, mais je pense que celles-ci étaient faciles à corriger. Pas trop grave.

J'ai trouvé une réponse facile à mettre en œuvre dans cet article sur DZone:

J'ai déjà un sous-dossier resources/config et j'ai nommé mon fichier: app.properties, afin de mieux refléter le type de contenu que nous pouvons y conserver (comme une URL de support, etc.).

Le seul inconvénient est que Netbeans avertit que le IDE doit être filtré. Je ne sais pas où/comment. Il n’a aucun effet à ce stade. Peut-être qu’il ya une solution de rechange si j’ai besoin de traversez ce pont, bonne chance.

3
will

J'utilise maven-Assembly-plugin pour mon emballage maven. L'utilisation de Apache Maven Archiver dans réponse de Joachim Sauer pourrait également fonctionner:

<plugin>
    <groupId>org.Apache.maven.plugins</groupId>
    <artifactId>maven-Assembly-plugin</artifactId>
    <configuration>
        <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
        </descriptorRefs>
        <archive>
            <manifest>
                <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
                <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
            </manifest>
        </archive>
    </configuration>
    <executions>
        <execution .../>
    </executions>
</plugin>

Parce que archiever est l’un des composants partagés maven , il peut être utilisé par plusieurs plugins de construction maven, qui peuvent également être en conflit si deux ou plusieurs plugins sont introduits, y compris archive configuration.

3
千木郷

Pour que cela fonctionne dans Eclipse, ainsi que dans une construction Maven, vous devez ajouter les entrées addDefaultImplementationEntries et addDefaultSpecificationEntries pom comme décrit dans d'autres réponses, puis utiliser le code suivant:

public synchronized static final String getVersion() {
    // Try to get version number from pom.xml (available in Eclipse)
    try {
        String className = getClass().getName();
        String classfileName = "/" + className.replace('.', '/') + ".class";
        URL classfileResource = getClass().getResource(classfileName);
        if (classfileResource != null) {
            Path absolutePackagePath = Paths.get(classfileResource.toURI())
                    .getParent();
            int packagePathSegments = className.length()
                    - className.replace(".", "").length();
            // Remove package segments from path, plus two more levels
            // for "target/classes", which is the standard location for
            // classes in Eclipse.
            Path path = absolutePackagePath;
            for (int i = 0, segmentsToRemove = packagePathSegments + 2;
                    i < segmentsToRemove; i++) {
                path = path.getParent();
            }
            Path pom = path.resolve("pom.xml");
            try (InputStream is = Files.newInputStream(pom)) {
                Document doc = DocumentBuilderFactory.newInstance()
                        .newDocumentBuilder().parse(is);
                doc.getDocumentElement().normalize();
                String version = (String) XPathFactory.newInstance()
                        .newXPath().compile("/project/version")
                        .evaluate(doc, XPathConstants.STRING);
                if (version != null) {
                    version = version.trim();
                    if (!version.isEmpty()) {
                        return version;
                    }
                }
            }
        }
    } catch (Exception e) {
        // Ignore
    }

    // Try to get version number from maven properties in jar's META-INF
    try (InputStream is = getClass()
        .getResourceAsStream("/META-INF/maven/" + MAVEN_PACKAGE + "/"
                + MAVEN_ARTIFACT + "/pom.properties")) {
        if (is != null) {
            Properties p = new Properties();
            p.load(is);
            String version = p.getProperty("version", "").trim();
            if (!version.isEmpty()) {
                return version;
            }
        }
    } catch (Exception e) {
        // Ignore
    }

    // Fallback to using Java API to get version from MANIFEST.MF
    String version = null;
    Package pkg = getClass().getPackage();
    if (pkg != null) {
        version = pkg.getImplementationVersion();
        if (version == null) {
            version = pkg.getSpecificationVersion();
        }
    }
    version = version == null ? "" : version.trim();
    return version.isEmpty() ? "unknown" : version;
}

Si votre construction Java place les classes cibles ailleurs que "cible/classes", vous devrez peut-être ajuster la valeur de segmentsToRemove.

1
Luke Hutchison