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

177

J'ai remarqué que dans le 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?

Armand
la source
Voir stackoverflow.com/a/26589696/52176
Leif Gruenwoldt

Réponses:

265

Vous ne devriez pas avoir besoin d'accéder aux 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.MF. Heureusement, Maven est assez intelligent Malheureusement, Maven n'écrit pas non plus les informations correctes dans le manifeste par défaut!

Au lieu de cela, il faut modifier l' <archive>élément de configuration du maven-jar-pluginto set addDefaultImplementationEntrieset addDefaultSpecificationEntriesto 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 l'entreprise pomou dans un autre pom de base.

Une documentation détaillée de l' <archive>élément peut être trouvée dans la documentation Maven Archive .

Joachim Sauer
la source
6
malheureusement, tous les chargeurs de classes ne semblent pas charger ces propriétés à partir du fichier manifeste (je me souviens avoir eu des problèmes avec Tomcat dans ce cas précis).
dwegener
@avithan: vraiment? Je n'ai jamais eu de problème avec Tomcat avec cette approche. De plus, je pense qu'un chargeur de classe qui ignore le manifeste n'est probablement pas conforme.
Joachim Sauer
@JoachimSauer ok, je me suis trompé. Actuellement, il semble que cela fonctionne très bien sur HotSpot mais ne fonctionne pas de manière fiable sur OpenJDK. Je ferai un rapport quand j'aurai des informations détaillées
dwegener
@avithan c'est pertinent pour moi (et je n'ai pas vu ce que vous rapportez) - avez-vous encore des informations détaillées?
Thorbjørn Ravn Andersen
4
Malheureusement, cela ne fonctionne pas si le projet est exécuté depuis Eclipse ou en utilisant "mvn exec: java".
Jaan
77

Pour suivre la réponse ci-dessus, pour un .warartefact, j'ai trouvé que je devais 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.MFdans le projet .jar(inclus dans WEB-INF/lible .war)

Rob
la source
3
<archiveClasses> true </archiveClasses> a causé une erreur dans mon cas. Mais le problème a été résolu stackoverflow.com/questions/14934299/…
Paul Verest
10
Quand j'essaye ceci, mon résultat est toujours nullbien que le MANIFEST.MF dans les fichiers de guerre contienne les informations correctes.
thomas.mc.work
J'avais également besoin de l'ajouter à maven-assembly-plugin
acheron55
2
<archiveClasses> true </archiveClasses> semble sans rapport
Karl Kildén
1
@RafaelSimonelli J'ai supprimé <archiveClasses>true</archiveClasses>- et cela fonctionne de manière fiable depuis lors.
thomas.mc.work
28

Voici une méthode pour obtenir la version à partir du pom.properties, en revenant à l'obtenir à partir 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;
} 
mysomique
la source
2
Mettez ceci dans un bloc d'initialisation statique.
opyate le
1
Bon conseil. Bien que, si vous l'utilisez dans un servlet (ou .jsp), veillez à utiliser getServletContext (). GetResourceAsStream au lieu de getClass (). GetResourceAsStream
Sandman
3
Cela ne fonctionne que lorsque l'application est exécutée à partir du fichier jar. Si elle est exécutée depuis exec-maven-plugin (par exemple Netbeans), la ressource est nulle.
Leif Gruenwoldt
Ce code fera partie de mes valeurs par défaut de classe principale! Merci!!
Wendel
J'ai utilisé cela avec la réponse de Will pour une option simple et facile à entretenir.
javydreamercsw
3

J'ai passé du temps sur les deux approches principales ici et elles n'ont pas fonctionné pour moi. J'utilise Netbeans pour les builds, il se peut qu'il y en ait plus là-bas. J'ai eu des erreurs et des avertissements de Maven 3 avec certaines constructions, mais je pense que ceux-ci étaient faciles à corriger. Pas de problème.

J'ai trouvé une réponse qui semble maintenable et simple à 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, pour mieux refléter le genre de choses que nous pouvons y conserver (comme une URL de support, etc.).

La seule mise en garde est que Netbeans avertit que l'EDI doit être filtré. Je ne sais pas où / comment. Cela n'a aucun effet à ce stade. Il y a peut-être un moyen de contourner ce problème si je dois traverser ce pont. Bonne chance.

volonté
la source
3

J'utilise maven-assembly-pluginpour mon emballage maven. L'utilisation d' Apache Maven Archiver dans la 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 qu'archiever est l'un des composants partagés de maven , il pourrait être utilisé par plusieurs plugins de construction maven, ce qui pourrait également avoir un conflit si deux ou plusieurs plugins sont introduits, y compris la archiveconfiguration à l'intérieur.

千 木 郷
la source
2

Pour que cela fonctionne dans Eclipse, ainsi que dans une version Maven, vous devez ajouter les entrées addDefaultImplementationEntrieset addDefaultSpecificationEntriespom comme décrit dans les 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 build Java place les classes cibles ailleurs que "cible / classes", vous devrez peut-être ajuster la valeur de segmentsToRemove.

Luke Hutchison
la source
Vous savez que si c'est pour les tests unitaires, vous pouvez simplement System.getProperty("user.dir")/pom.xml. Je suis assez sûr que ce sera le cas pour d'autres choses, sauf peut-être pas pour WTP.
Adam Gent
Cela ne fonctionnera que si votre projet est dans un répertoire - si vous exécutez un projet basé sur jarfiles, votre solution ne fonctionnera pas. Vous devez utiliser .getResource()ou .getResourceAsStream().
Luke Hutchison le
Oui, je supposais que vous avez déjà vérifié le pot (ala getResource). C'est-à-dire que vous vérifiez d'abord avec getResource si cela échoue, le projet n'a pas encore été construit dans un fichier jar, ce qui signifie que vous l'exécutez à partir d'Eclipse ou de Maven, ce qui signifie `System.getProperty (" user.dir ") / pom.xml . Le seul problème est que ce fichier pom n'est pas encore le vrai pom efficace (c'est-à-dire que certaines propriétés ne seront pas développées), mais ce n'est pas non plus celui que vous obtenez avec la méthode Eclipse.
Adam Gent
1

Sur mon application Spring Boot, la solution de la réponse acceptée a fonctionné jusqu'à ce que je mette récemment à jour mon jdk vers la version 12. J'ai également essayé toutes les autres réponses et je n'ai pas réussi à faire fonctionner cela.

À ce stade, j'ai ajouté la ligne ci-dessous à la première classe de mon application Spring Boot, juste après l'annotation @SpringBootApplication

@PropertySources({ 
        @PropertySource("/META-INF/maven/com.my.group/my-artefact/pom.properties")
})

Plus tard, j'utilise ce qui suit pour obtenir la valeur du fichier de propriétés dans la classe dans laquelle je veux utiliser sa valeur et appVersionme donne la version du projet:

@Value("${version}")
private String appVersion;

J'espère que cela aide quelqu'un.

Reema
la source
Comment faire la même chose avec plusieurs fichiers pom? Je veux charger la version à partir de plusieurs fichiers pom.
THM
0

Une solution simple qui est compatible Maven et fonctionne pour n'importe quelle classe (donc également tierce):

    private static Optional<String> getVersionFromManifest(Class<?> clazz) {
        try {
            File file = new File(clazz.getProtectionDomain().getCodeSource().getLocation().toURI());
            if (file.isFile()) {
                JarFile jarFile = new JarFile(file);
                Manifest manifest = jarFile.getManifest();
                Attributes attributes = manifest.getMainAttributes();
                final String version = attributes.getValue("Bundle-Version");
                return Optional.of(version);
            }
        } catch (Exception e) {
            // ignore
        }
        return Optional.empty();
    }
rdehuyss
la source
-1

Variante Java 8 pour EJB dans un fichier war avec le projet maven. Testé sur EAP 7.0.

@Log4j // lombok annotation
@Startup
@Singleton
public class ApplicationLogic {

    public static final String DEVELOPMENT_APPLICATION_NAME = "application";

    public static final String DEVELOPMENT_GROUP_NAME = "com.group";

    private static final String POM_PROPERTIES_LOCATION = "/META-INF/maven/" + DEVELOPMENT_GROUP_NAME + "/" + DEVELOPMENT_APPLICATION_NAME + "/pom.properties";

    // In case no pom.properties file was generated or wrong location is configured, no pom.properties loading is done; otherwise VERSION will be assigned later
    public static String VERSION = "No pom.properties file present in folder " + POM_PROPERTIES_LOCATION;

    private static final String VERSION_ERROR = "Version could not be determinated";

    {    
        Optional.ofNullable(getClass().getResourceAsStream(POM_PROPERTIES_LOCATION)).ifPresent(p -> {

            Properties properties = new Properties();

            try {

                properties.load(p);

                VERSION = properties.getProperty("version", VERSION_ERROR);

            } catch (Exception e) {

                VERSION = VERSION_ERROR;

                log.fatal("Unexpected error occured during loading process of pom.properties file in META-INF folder!");
            }
        });
    }
}
onderbewustzijn
la source