実行時にMavenアーティファクトバージョンを取得する


176

MavenアーティファクトのJARでは、project.version属性が2つのファイルに含まれていることに気付きました。

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

実行時にこのバージョンを読み取るための推奨される方法はありますか?


回答:


265

特定のライブラリ/クラスのバージョン情報を取得するためにMaven固有のファイルにアクセスする必要はありません。

を使用getClass().getPackage().getImplementationVersion()して、.jar-filesに格納されているバージョン情報を取得できますMANIFEST.MF幸い、Mavenは十分にスマートです。残念ながら、Mavenはデフォルトで正しい情報をマニフェストにも書き込みません。

代わりに、次のようにset とtoの<archive>構成要素を変更する必要があります。maven-jar-pluginaddDefaultImplementationEntriesaddDefaultSpecificationEntriestrue

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

理想的には、この構成は会社pomまたは別のベースポンに配置する必要があります。

<archive>要素の詳細なドキュメントは、Maven Archiveドキュメントにあります


6
悲しいことに、すべてのクラスローダーがこれらのプロパティをマニフェストファイルからロードするようには見えません(この場合、Tomcatで問題があったことを覚えています)。
dwegener

@avithan:本当に?この方法では、Tomcatで問題が発生したことはありません。また、マニフェストを無視するクラスローダーはおそらく準拠していないと思います。
Joachim Sauer、

@JoachimSauerわかりました、私は間違っていました。現在、それはHotSpotでうまく機能しているようですが、OpenJDKでは信頼性がありません。詳細情報が
わかり次第

@avithanこれは私に関連しています(そして私はあなたが報告するものを見ていません)-あなたはまだ詳細な情報を得ましたか?
–ThorbjørnRavn Andersen 2013

4
残念ながら、プロジェクトがEclipseから実行されている場合、または「mvn exec:java」を使用している場合、これは機能しません。
Jaan

77

上記の回答をフォローアップするために、.warアーティファクトについては、同等の設定をに適用する必要がmaven-war-pluginありました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>

これは、にバージョン情報を追加してMANIFEST.MF、プロジェクトの中.jar(に含まれてWEB-INF/lib.war


3
<archiveClasses> true </ archiveClasses>が原因でエラーが発生しました。しかし、問題は解決しましたstackoverflow.com/questions/14934299/...
ポールVerest

10
これを試してみるとnull、warファイルのMANIFEST.MFには正しい情報が含まれていますが、結果は常にです。
thomas.mc.work 2014年

また、maven-assembly-pluginにも追加する必要がありました
acheron55 '19年

2
<archiveClasses> true </ archiveClasses>は無関係と思われる
KarlKildén2015

1
@RafaelSimonelli私は削除しました<archiveClasses>true</archiveClasses>-そしてそれはそれ以来確実に機能します。
thomas.mc.work 2018

28

これは、pom.propertiesからバージョンを取得するためのメソッドです。マニフェストから取得するためにフォールバックします

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
これを静的初期化ブロックに入れます。
オピエート2013

1
いいアドバイス。ただし、これをサーブレット(または.jsp)で使用している場合は、必ずgetClass()。getResourceAsStreamではなくgetServletContext()。getResourceAsStreamを使用してください
Sandman

3
これは、アプリケーションがjarから実行された場合にのみ機能します。exec-maven-plugin(例:Netbeans)から実行した場合、リソースはnullです。
Leif Gruenwoldt 2014年

このコードは私のメインクラスのデフォルトの一部になります!ありがとう!!
Wendel

これをウィルの回答と一緒に使用して、簡単で保守しやすいオプションを作成しました。
javydreamercsw

3

ここで2つの主要なアプローチに少し時間を費やしましたが、それらは私にとってうまくいきませんでした。私はビルドにNetbeansを使用しています。Maven 3からいくつかのエラーと警告がありましたが、それらは簡単に修正できたと思います。大したことはありません。

DZoneに関するこの記事で、保守が容易で実装が簡単に見える回答を見つけました。

私はすでにresources / configサブフォルダーを持っており、私たちが自分のファイルにapp.propertiesという名前を付けて、そこに保持する可能性のあるもの(サポートURLなど)をより適切に反映しています。

唯一の注意点は、NetbeansがIDEにフィルターをかける必要があるという警告を出すことです。どこで/どうやってわからない。この時点では効果はありません。おそらく、その橋を渡る必要がある場合は、回避策があるかもしれません。幸運を祈ります。


3

私はmaven-assembly-plugin私のmavenパッケージに使用しています。使用のApache Mavenのアーカイバヨアヒム・ザウアーの答えはまた仕事ができます:

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

archieverはMaven共有コンポーネントの 1つであるため、複数のMavenビルドプラグインによって使用される可能性があり、archive内部の構成を含め、2つ以上のプラグインが導入された場合にも競合する可能性があります。


2

これをEclipseとMavenビルドで実行するには、他の返信で説明されているようにaddDefaultImplementationEntriesaddDefaultSpecificationEntriespomエントリを追加してから、次のコードを使用します。

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

Javaビルドが「ターゲット/クラス」以外の場所にターゲットクラスを配置する場合、segmentsToRemoveの値を調整する必要がある場合があります。


これが単体テスト用かどうかはわかりますSystem.getProperty("user.dir")/pom.xml。WTP以外の場合を除いて、他の事柄にも同様に対応できると確信しています。
アダム・ゲント

これは、プロジェクトがディレクトリにある場合にのみ機能します。jarfilesに基づいてプロジェクトを実行している場合、ソリューションは機能しません。.getResource()またはを使用する必要があります.getResourceAsStream()
ルークハッチソン

はい、私はあなたがすでにjarをチェックしていると思っていました(ala getResource)。それが最初にgetResourceで確認し、失敗した場合、プロジェクトはまだjarに組み込まれていません。つまり、EclipseまたはMavenから実行しているか、つまり `System.getProperty(" user.dir ")/ pom.xmlを実行しています。 。唯一の問題は、このpomファイルが実際の有効なpomではない(つまり、一部のプロパティが展開されない)ことですが、Eclipseの方法で取得したものはどちらでもありません。
アダムゲント2017年

1

私のスプリングブートアプリケーションでは、jdkを最近バージョン12に更新するまで、受け入れられた回答からの解決策が機能しました。他のすべての回答も試したところ、機能しませんでした。

その時点で、次の行を注釈の直後のスプリングブートアプリケーションの最初のクラスに追加しました @SpringBootApplication

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

後で、以下を使用して、値を使用するクラスのプロパティファイルから値をappVersion取得し、プロジェクトバージョンを取得します。

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

それが誰かを助けることを願っています。


複数のpomファイルで同じことをするには?複数のpomファイルからバージョンをロードしたい。
THM

0

Maven互換で、あらゆる(したがってサードパーティの)クラスでも機能するシンプルなソリューション:

    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

mavenプロジェクトのwarファイルにあるEJBのJava 8バリアント。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!");
            }
        });
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.