Dapatkan versi artefak Maven saat runtime

176

Saya perhatikan bahwa dalam JAR artifact MAR, atribut project.version termasuk dalam dua file:

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

Apakah ada cara yang disarankan untuk membaca versi ini saat runtime?

Armand
sumber
Lihat stackoverflow.com/a/26589696/52176
Leif Gruenwoldt

Jawaban:

265

Anda tidak perlu mengakses file khusus Maven untuk mendapatkan informasi versi perpustakaan / kelas yang diberikan.

Anda cukup menggunakan getClass().getPackage().getImplementationVersion()untuk mendapatkan informasi versi yang disimpan dalam file .jar-file MANIFEST.MF. Untungnya Maven cukup pintar. Sayangnya Maven tidak menulis informasi yang benar ke manifes juga secara default!

Sebagai gantinya kita harus memodifikasi <archive>elemen konfigurasi dari maven-jar-pluginto set addDefaultImplementationEntriesdan addDefaultSpecificationEntriesto true, seperti ini:

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

Idealnya konfigurasi ini harus dimasukkan ke perusahaan pomatau base-pom lainnya.

Dokumentasi rinci <archive>elemen dapat ditemukan dalam dokumentasi Maven Archive .

Joachim Sauer
sumber
6
sayangnya tidak setiap classloader tampaknya memuat properti ini dari file manifes (saya ingat memiliki masalah dengan Tomcat dalam kasus ini).
dwegener
@avithan: benarkah? Saya tidak pernah memiliki masalah dengan Tomcat dengan pendekatan ini. Juga, saya pikir classloader yang mengabaikan manifes mungkin tidak sesuai.
Joachim Sauer
@ JoachimSauer ok, saya salah. Saat ini tampaknya berfungsi dengan baik di HotSpot tetapi tidak bekerja andal di OpenJDK. Saya akan melaporkan kembali ketika saya mendapatkan informasi terperinci
dwegener
@avithan ini relevan bagi saya (dan saya belum melihat apa yang Anda laporkan) - apakah Anda sudah mendapatkan informasi terperinci?
Thorbjørn Ravn Andersen
4
Sayangnya ini tidak berfungsi jika proyek dijalankan dari Eclipse atau menggunakan "mvn exec: java".
Jaan
77

Untuk menindaklanjuti jawaban di atas, untuk .warartefak, saya menemukan saya harus menerapkan konfigurasi yang setara maven-war-plugin, daripada 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>

Hal ini menambah informasi versi untuk MANIFEST.MFdi ini proyek .jar(termasuk dalam WEB-INF/libsatu .war)

rampok
sumber
3
<archiveClasses> true </archiveClasses> menyebabkan kesalahan dalam kasus saya. Tapi masalah teratasi stackoverflow.com/questions/14934299/…
Paul Verest
10
Ketika saya mencoba ini maka hasil saya selalu nullmeskipun MANIFEST.MF dalam file perang berisi informasi yang benar.
thomas.mc.work
Saya juga perlu menambahkannya ke maven-assembly-plugin
acheron55
2
<archiveClasses> true </archiveClasses> tampaknya tidak terkait
Karl Kildén
1
@ RafaelSimonelli Saya sudah menghapus <archiveClasses>true</archiveClasses>- dan itu berfungsi dengan andal sejak saat itu.
thomas.mc.work
28

Berikut adalah metode untuk mendapatkan versi dari pom.properties, kembali untuk mendapatkannya dari manifes

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;
} 
mysomic
sumber
2
Letakkan ini di blok penginisialisasi statis.
Opyate
1
Saran yang bagus. Meskipun, jika Anda menggunakan ini dalam servlet (atau .jsp), pastikan untuk menggunakan getServletContext (). GetResourceAsStream alih-alih getClass (). GetResourceAsStream
Sandman
3
Ini hanya berfungsi saat aplikasi dijalankan dari toples. Jika dijalankan dari exec-maven-plugin (mis. Netbeans) sumber daya adalah nol.
Leif Gruenwoldt
Kode ini akan menjadi bagian dari standar kelas utama saya! Terima kasih!!
Wendel
Saya menggunakan ini dengan jawaban Will untuk opsi yang lurus ke depan dan mudah dipertahankan.
javydreamercsw
3

Saya menghabiskan beberapa waktu pada dua pendekatan utama di sini dan mereka tidak berhasil untuk saya. Saya menggunakan Netbeans untuk membangun, mungkin ada lebih banyak terjadi di sana. Saya memiliki beberapa kesalahan dan peringatan dari Maven 3 dengan beberapa konstruksi, tapi saya pikir itu mudah diperbaiki. Bukan masalah besar.

Saya memang menemukan jawaban yang terlihat dapat dipertahankan dan mudah diterapkan di artikel ini di DZone:

Saya sudah memiliki sub-folder sumber daya / konfigurasi, dan saya beri nama file saya: app.properties, untuk lebih mencerminkan jenis barang yang kami simpan di sana (seperti URL dukungan, dll.).

Satu-satunya peringatan adalah bahwa Netbeans memberikan peringatan bahwa IDE perlu disaring. Tidak yakin di mana / bagaimana. Tidak ada efek pada saat ini. Mungkin ada pekerjaan untuk itu jika saya perlu menyeberangi jembatan itu. Semoga berhasil.

akan
sumber
3

Saya menggunakan maven-assembly-pluginuntuk kemasan pakar saya. Penggunaan Apache Maven Archiver dalam jawaban Joachim Sauer juga bisa berfungsi:

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

Karena archiever adalah salah satu komponen bersama maven , itu dapat digunakan oleh beberapa plugin bangunan pakar, yang juga dapat memiliki konflik jika dua atau lebih plugin diperkenalkan, termasuk archivekonfigurasi di dalamnya.

千 木 郷
sumber
2

Untuk menjalankan ini di Eclipse, serta di Maven build, Anda harus menambahkan entri addDefaultImplementationEntriesdan addDefaultSpecificationEntriespom seperti yang dijelaskan dalam balasan lain, lalu gunakan kode berikut:

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

Jika bangunan Java Anda menempatkan kelas target di tempat lain selain "target / kelas", maka Anda mungkin perlu menyesuaikan nilai segmentToRemove.

Luke Hutchison
sumber
Anda tahu jika ini untuk unit test Anda bisa saja System.getProperty("user.dir")/pom.xml. Saya cukup yakin itu akan untuk hal-hal lain juga kecuali mungkin bukan untuk WTP.
Adam Gent
Itu hanya akan berfungsi jika proyek Anda ada di direktori - jika Anda menjalankan proyek yang berbasis di jarfiles, solusi Anda tidak akan berfungsi. Anda perlu menggunakan .getResource()atau .getResourceAsStream().
Luke Hutchison
Ya saya berasumsi Anda sudah memeriksa toples (ala getResource). Itu pertama Anda memeriksa dengan getResource jika gagal maka proyek belum dibangun ke dalam toples yang berarti Anda menjalankannya dari Eclipse atau Maven yang berarti `System.getProperty (" user.dir ") / pom.xml . Satu-satunya masalah adalah file pom ini bukan pom efektif yang sebenarnya (yaitu beberapa properti tidak akan diperluas) namun juga bukan yang Anda dapatkan dengan cara Eclipse.
Adam Gent
1

Pada aplikasi booting musim semi saya, solusi dari jawaban yang diterima bekerja sampai saya baru-baru ini memperbarui jdk saya ke versi 12. Mencoba semua jawaban lain juga dan tidak bisa membuatnya bekerja.

Pada saat itu, saya menambahkan baris di bawah ini ke kelas pertama aplikasi booting musim semi saya, tepat setelah anotasi @SpringBootApplication

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

Kemudian saya menggunakan di bawah ini untuk mendapatkan nilai dari file properti di kelas mana pun saya ingin menggunakan nilainya dan appVersionmendapatkan versi proyek untuk saya:

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

Semoga itu bisa membantu seseorang.

Reema
sumber
Bagaimana melakukan hal yang sama dengan banyak file pom? Saya ingin memuat versi dari beberapa file pom.
THM
0

Solusi sederhana yang kompatibel dengan Maven dan berfungsi untuk semua kelas (dengan demikian juga pihak ketiga):

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

Varian Java 8 untuk EJB dalam file perang dengan proyek maven. Diuji pada 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
sumber