web-dev-qa-db-ja.com

Spring BootアプリでMETA-INF / MANIFEST.MFファイルを読み取る方法

Spring Boot Webアプリ(jarファイルに含まれる)からMETA-INF/MANIFEST.MFファイルを読み取ろうとしています。

私は次のコードを試しています:

        InputStream is = getClass().getResourceAsStream("/META-INF/MANIFEST.MF");

        Properties prop = new Properties();
        prop.load( is );

しかし、明らかに、Spring Bootの背後には、異なるmanifest.mfがロードされている(META-INFフォルダーにある自分のものではない)ものがあります。

Spring Bootアプリでマニフェストアプリを読む方法を知っている人はいますか?

PDATE:いくつかの調査の後、manifest.mfファイルを通常の方法で読み取ると、Spring BootアプリケーションではこれがアクセスされるJarであることに気付きました。

org.springframework.boot.loader.jar.JarFile
15
Ricardo Memoria

Java.lang.Package を使用して、マニフェストからスプリングブートのImplementation-Version属性を読み取ります。

String version = Application.class.getPackage().getImplementationVersion();

Implementation-Version属性はbuild.gradleで設定する必要があります

jar {
    baseName = "my-app"
    version =  "0.0.1"
    manifest {
        attributes("Implementation-Version": version)
    }
}
17
samsong8610

これを追加するだけで簡単です

    InputStream is = this.getClass().getClassLoader().getResourceAsStream("META-INF/MANIFEST.MF");

    Properties prop = new Properties();
    try {
        prop.load( is );
    } catch (IOException ex) {
        Logger.getLogger(IndexController.class.getName()).log(Level.SEVERE, null, ex);
    }

私のために働いています。

注意:

getClass()。getClassLoader()は重要です

そして

「/META-INF/MANIFEST.MF」ではなく「META-INF/MANIFEST.MF」

ありがとう、アレクサンダー

6
abosancic

ほとんどすべてのjarファイルにはマニフェストファイルが付属しているため、コードはfirstfileを返しますクラスパス内 =。

とにかくマニフェストが必要なのはなぜですか?これはJavaが使用するファイルです。 .propertiesファイルの隣の.classファイルなど、必要なカスタム値を他の場所に配置します。

更新2

以下のコメントで述べられているように、not質問では、本当の目標はマニフェストからのバージョン情報です。 Javaはすでに Java.lang.Package クラスでその情報を提供しています。

マニフェストを見つけられたとしても、自分でマニフェストを読もうとしないでください。

更新1

マニフェストファイルはnotPropertiesファイルであることに注意してください。その構造はそれよりもはるかに複雑です。

JARファイル仕様 のJavaドキュメントの例を参照してください。

Manifest-Version: 1.0
Created-By: 1.7.0 (Sun Microsystems Inc.)

Name: common/class1.class
SHA-256-Digest: (base64 representation of SHA-256 digest)

Name: common/class2.class
SHA1-Digest: (base64 representation of SHA1 digest)
SHA-256-Digest: (base64 representation of SHA-256 digest)

ご覧のとおり、NameSHA-256-Digestは複数回出現します。 Propertiesクラスはそれを処理できません。これは単なるMapであり、キーが一意である必要があるためです。

4
Andreas

Springのリソース解決を活用しています:

@Service
public class ManifestService {

    protected String ciBuild;

    public String getCiBuild() { return ciBuild; }

    @Value("${manifest.basename:/META-INF/MANIFEST.MF}")
    protected void setManifestBasename(Resource resource) {
        if (!resource.exists()) return;
        try (final InputStream stream = resource.getInputStream()) {
            final Manifest manifest = new Manifest(stream);
            ciBuild = manifest.getMainAttributes().getValue("CI-Build");
        }
        catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

}

ここでCI-Build、およびサンプルを簡単に拡張して他の属性をロードできます。

2
usethe4ce

Springドキュメントでテストと検索を行った後、マニフェストファイルを読み取る方法を見つけました。

まず、Spring Bootは、リソースのロード方法を変更する独自のClassLoaderを実装します。 getResource()を呼び出すと、Spring Bootは、クラスパスで使用可能なすべてのJARのリスト内の指定されたリソース名に一致する最初のリソースをロードします。アプリjarは最初の選択肢ではありません。

したがって、次のようなコマンドを発行する場合:

getClassLoader().getResourceAsStream("/META-INF/MANIFEST.MF");

クラスパスのJarで最初に見つかったMANIFEST.MFファイルが返されます。私の場合、それはJDK jarライブラリから来ました。

ソリューション:

リソース「/META-INF/MANIFEST.MF」を含むアプリによってロードされたすべてのJarのリストを取得し、リソースがアプリケーションjarから来ているかどうかを確認しました。その場合、そのMANIFEST.MFファイルを読み取り、次のようにアプリに戻ります。

private Manifest getManifest() {
    // get the full name of the application manifest file
    String appManifestFileName = this.getClass().getProtectionDomain().getCodeSource().getLocation().toString() + JarFile.MANIFEST_NAME;

    Enumeration resEnum;
    try {
        // get a list of all manifest files found in the jars loaded by the app
        resEnum = Thread.currentThread().getContextClassLoader().getResources(JarFile.MANIFEST_NAME);
        while (resEnum.hasMoreElements()) {
            try {
                URL url = (URL)resEnum.nextElement();
                // is the app manifest file?
                if (url.toString().equals(appManifestFileName)) {
                    // open the manifest
                    InputStream is = url.openStream();
                    if (is != null) {
                        // read the manifest and return it to the application
                        Manifest manifest = new Manifest(is);
                        return manifest;
                    }
                }
            }
            catch (Exception e) {
                // Silently ignore wrong manifests on classpath?
            }
        }
    } catch (IOException e1) {
        // Silently ignore wrong manifests on classpath?
    }
    return null;
}

このメソッドは、マニフェストオブジェクト内のmanifest.mfファイルからすべてのデータを返します。

Javaを使用してjarファイルからMANIFEST.MFファイルを読み取る からソリューションの一部を借用しました

2
Ricardo Memoria
public Properties readManifest() throws IOException {
    Object inputStream = this.getClass().getProtectionDomain().getCodeSource().getLocation().getContent();
    JarInputStream jarInputStream = new JarInputStream((InputStream) inputStream);
    Manifest manifest = jarInputStream.getManifest();
    Attributes attributes = manifest.getMainAttributes();
    Properties properties = new Properties();
    properties.putAll(attributes);
    return properties;
}
1
outdev
    try {
        final JarFile jarFile = (JarFile) this.getClass().getProtectionDomain().getCodeSource().getLocation().getContent();
        final Manifest manifest = jarFile.getManifest();
        final Map<Object, Object> manifestProps = manifest.getMainAttributes().entrySet().stream()
                .collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue()));
    ...
    } catch (final IOException e) {
        LOG.error("Unable to read MANIFEST.MF", e);
        ...
    }

これは、Java -jarコマンドを使用してアプリを起動した場合にのみ機能し、統合テストを作成した場合は機能しません。

0
Ihor M.