Ottieni la versione del manufatto Maven in fase di esecuzione


177

Ho notato che nel JAR di un artefatto di Maven, l'attributo project.version è incluso in due file:

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

Esiste un modo consigliato per leggere questa versione in fase di esecuzione?


Risposte:


265

Non è necessario accedere ai file specifici di Maven per ottenere le informazioni sulla versione di una determinata libreria / classe.

Puoi semplicemente usare getClass().getPackage().getImplementationVersion()per ottenere le informazioni sulla versione archiviate in un file .jar MANIFEST.MF. Fortunatamente Maven è abbastanza intelligente Sfortunatamente Maven non scrive le informazioni corrette anche sul manifest per impostazione predefinita!

Invece si deve modificare l' <archive>elemento di configurazione del maven-jar-pluginset addDefaultImplementationEntriese addDefaultSpecificationEntriesto true, in questo modo:

<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>

Idealmente questa configurazione dovrebbe essere inserita nell'azienda pomo in un'altra base.

La documentazione dettagliata <archive>dell'elemento è disponibile nella documentazione dell'archivio Maven .


6
purtroppo non tutti i classloader sembrano caricare queste proprietà dal file manifest (ricordo che ho avuto problemi con Tomcat proprio in questo caso).
— Dwegener,

@avithan: davvero? Non ho mai avuto problemi con Tomcat con questo approccio. Inoltre, penso che un classloader che ignori il manifest probabilmente non sia conforme.
— Joachim Sauer,

@JoachimSauer ok, mi sbagliavo. Attualmente sembra funzionare alla grande su HotSpot ma non funziona in modo affidabile su OpenJDK.
— Riferirò

@avithan questo è rilevante per me (e non ho visto quello che riferisci) - hai ancora ricevuto informazioni dettagliate?
— Thorbjørn Ravn Andersen,

4
Sfortunatamente questo non funziona se il progetto viene eseguito da Eclipse o usando "mvn exec: java".
— Jaan,

77

Per seguire la risposta sopra, per a .war artefatto, ho scoperto che dovevo applicare la configurazione equivalente maven-war-plugin, anziché 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>

Questo aggiunto le informazioni sulla versione per MANIFEST.MFnegli anni del progetto .jar(incluso nel WEB-INF/libdel .war)


3
<archiveClasses> true </archiveClasses> ha provocato errori nel mio caso. Ma il problema ha ottenuto risolto stackoverflow.com/questions/14934299/...
— Paul Verest

10
Quando provo questo, il mio risultato è sempre nullsebbene MANIFEST.MF nei file di guerra contenga le informazioni corrette.
— thomas.mc.work

Avevo anche bisogno di aggiungerlo a maven-assembly-plugin
— acheron55,

2
<archiveClasses> true </archiveClasses> non sembra correlato
— Karl Kildén

1
@RafaelSimonelli l'ho rimosso <archiveClasses>true</archiveClasses>- e funziona in modo affidabile da allora.
— thomas.mc.work

28

Ecco un metodo per ottenere la versione da pom.properties, tornare indietro per ottenerla dal manifest

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;
} 

2
Mettilo in un blocco di inizializzatore statico.
— opyate

1
Buon Consiglio. Sebbene, se lo stai usando in un servlet (o .jsp), assicurati di usare getServletContext (). GetResourceAsStream invece di getClass (). GetResourceAsStream
— Sandman,

3
Funziona solo quando l'applicazione viene eseguita dal vaso. Se eseguito da exec-maven-plugin (ad esempio Netbeans) la risorsa è nulla.
— Leif Gruenwoldt,

Questo codice farà parte delle impostazioni predefinite della mia classe principale! Grazie!!
— Wendel,

L'ho usato con la risposta di Will per un'opzione semplice e di facile manutenzione.
— javydreamercsw,

3

Ho trascorso un po 'di tempo sui due approcci principali qui e non hanno funzionato per me. Sto usando Netbeans per le build, forse c'è dell'altro. Ho avuto alcuni errori e avvertimenti da Maven 3 con alcuni costrutti, ma penso che fossero facili da correggere. Nessun problema.

Ho trovato una risposta che sembra mantenibile e semplice da implementare in questo articolo su DZone:

Ho già una sottocartella risorse / config e ho chiamato il mio file: app.properties, per riflettere meglio il tipo di cose che possiamo tenere lì (come un URL di supporto, ecc.).

L'unica avvertenza è che Netbeans avverte che l'IDE deve essere filtrato. Non sono sicuro di dove / come. Non ha alcun effetto a questo punto. Forse c'è un modo per aggirarlo se devo attraversare quel ponte. Buona fortuna.


3

Sto usando maven-assembly-pluginper la mia confezione di Maven. L'uso di Apache Maven Archiver nella risposta di Joachim Sauer potrebbe anche funzionare:

<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>

Poiché archiever è uno dei componenti condivisi di Maven , potrebbe essere utilizzato da più plug-in di Maven Building, che potrebbero anche avere conflitti se vengono introdotti due o più plugin, inclusa la archiveconfigurazione all'interno.


2

Per farlo funzionare in Eclipse, così come in una build Maven, è necessario aggiungere le voci addDefaultImplementationEntriese addDefaultSpecificationEntriespom come descritto nelle altre risposte, quindi utilizzare il seguente codice:

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;
}

Se la build Java inserisce le classi target in un posto diverso da "target / classi", potrebbe essere necessario regolare il valore di segmentiToRemove.


Sai se questo è per unit test che puoi System.getProperty("user.dir")/pom.xml. Sono abbastanza sicuro che lo farà anche per altre cose tranne forse per WTP.
— Adam Gent,

Funzionerà solo se il tuo progetto è in una directory - se stai eseguendo un progetto basato su jarfile, la tua soluzione non funzionerà. Devi usare .getResource()o .getResourceAsStream().
— Luke Hutchison,

Sì, stavo supponendo che tu abbia già controllato il vaso (ala getResource). Questo è il primo che controlli con getResource se fallisce, quindi il progetto non è stato ancora creato in un barattolo, il che significa che lo stai eseguendo da Eclipse o Maven che significa `System.getProperty (" user.dir ") / pom.xml . L'unico problema è che questo file pom non è il vero pom efficace (cioè alcune proprietà non saranno espanse) ma non è nemmeno quello che stai ottenendo con il modo Eclipse.
— Adam Gent,

1

Sulla mia applicazione di avvio a molla, la soluzione dalla risposta accettata ha funzionato fino a quando non ho aggiornato di recente il mio jdk alla versione 12. Ho provato anche tutte le altre risposte e non sono riuscito a farlo funzionare.

A quel punto, ho aggiunto la riga seguente alla prima classe della mia applicazione di avvio a molla, subito dopo l'annotazione @SpringBootApplication

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

Successivamente utilizzo quanto segue per ottenere il valore dal file delle proprietà in qualsiasi classe voglio usare il suo valore e appVersionottenere la versione del progetto per me:

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

Spero che aiuti qualcuno.


Come fare lo stesso con più file pom? Voglio caricare la versione da più file pom.
— THM,

0

Una soluzione semplice che è compatibile Maven e funziona per qualsiasi classe (quindi anche di terze parti):

    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();
    }

-1

Variante Java 8 per EJB in file di guerra con progetto maven. Testato su 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!");
            }
        });
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.