web-dev-qa-db-ja.com

自分のジャーのマニフェストを読む

クラスを配信したManifestファイルを読む必要がありますが、使用する場合:

getClass().getClassLoader().getResources(...)

Java Runtimeにロードされた最初の.jarからMANIFESTを取得します。
アプリはアプレットまたはWebスタートから実行されますが、
したがって、自分の.jarファイルにアクセスできなくなります。

実際に、Felix OSGiを起動したExport-packageから.jar属性を読み取りたいので、それらのパッケージをFelixに公開できます。何か案は?

121
Houtman

次の2つのいずれかを実行できます。

  1. getResources()を呼び出して、返されたURLのコレクションを反復処理し、自分が見つかるまでそれらをマニフェストとして読み取ります。

    Enumeration<URL> resources = getClass().getClassLoader()
      .getResources("META-INF/MANIFEST.MF");
    while (resources.hasMoreElements()) {
        try {
          Manifest manifest = new Manifest(resources.nextElement().openStream());
          // check that this is your manifest and do what you need or get the next one
          ...
        } catch (IOException E) {
          // handle
        }
    }
    
  2. getClass().getClassLoader()Java.net.URLClassLoaderのインスタンスであるかどうかを確認できます。 AppletClassLoaderなど、Sunクラスローダーの大部分は次のとおりです。次に、それをキャストして、既知のfindResource()を呼び出すことができます-少なくともアプレットでは-必要なマニフェストを直接返すには:

    URLClassLoader cl = (URLClassLoader) getClass().getClassLoader();
    try {
      URL url = cl.findResource("META-INF/MANIFEST.MF");
      Manifest manifest = new Manifest(url.openStream());
      // do stuff with it
      ...
    } catch (IOException E) {
      // handle
    }
    
106
ChssPly76

最初にクラスのURLを見つけることができます。 JARの場合、そこからマニフェストをロードします。例えば、

Class clazz = MyClass.class;
String className = clazz.getSimpleName() + ".class";
String classPath = clazz.getResource(className).toString();
if (!classPath.startsWith("jar")) {
  // Class not from JAR
  return;
}
String manifestPath = classPath.substring(0, classPath.lastIndexOf("!") + 1) + 
    "/META-INF/MANIFEST.MF";
Manifest manifest = new Manifest(new URL(manifestPath).openStream());
Attributes attr = manifest.getMainAttributes();
String value = attr.getValue("Manifest-Version");
115
ZZ Coder

Manifests from jcabi-manifests を使用して、利用可能なMANIFEST.MFファイルから1行だけで属性を読み取ることができます。

String value = Manifests.read("My-Attribute");

必要な唯一の依存関係は次のとおりです。

<dependency>
  <groupId>com.jcabi</groupId>
  <artifactId>jcabi-manifests</artifactId>
  <version>0.7.5</version>
</dependency>

また、詳細についてはこのブログ投稿を参照してください: http://www.yegor256.com/2014/07/03/how-to-read-manifest-mf.html

21
yegor256

バンドル(特定のクラスをロードしたバンドルを含む)のマニフェストを取得する最も適切な方法は、BundleまたはBundleContextオブジェクトを使用することです。

// If you have a BundleContext
Dictionary headers = bundleContext.getBundle().getHeaders();

// If you don't have a context, and are running in 4.2
Bundle bundle = FrameworkUtil.getBundle(this.getClass());
bundle.getHeaders();

Bundleオブジェクトは、特定のバンドルのクラスパス全体を検索するのではなく、特定のバンドルに含まれるリソースを検索するgetEntry(String path)も提供することに注意してください。

一般に、バンドル固有の情報が必要な場合は、クラスローダーに関する仮定に依存せずに、OSGi APIを直接使用してください。

11
Anthony Juckel

最も簡単な方法は、JarURLConnectionクラスを使用することです。

String className = getClass().getSimpleName() + ".class";
String classPath = getClass().getResource(className).toString();
if (!classPath.startsWith("jar")) {
    return DEFAULT_PROPERTY_VALUE;
}

URL url = new URL(classPath);
JarURLConnection jarConnection = (JarURLConnection) url.openConnection();
Manifest manifest = jarConnection.getManifest();
Attributes attributes = manifest.getMainAttributes();
return attributes.getValue(PROPERTY_NAME);

場合によっては...class.getProtectionDomain().getCodeSource().getLocation();vfs:/でパスを提供するため、これも追加で処理する必要があります。

8
ayurchuk

次のコードは、複数のタイプのアーカイブ(jar、war)および複数のタイプのクラスローダー(jar、url、vfs、...)で動作します

  public static Manifest getManifest(Class<?> clz) {
    String resource = "/" + clz.getName().replace(".", "/") + ".class";
    String fullPath = clz.getResource(resource).toString();
    String archivePath = fullPath.substring(0, fullPath.length() - resource.length());
    if (archivePath.endsWith("\\WEB-INF\\classes") || archivePath.endsWith("/WEB-INF/classes")) {
      archivePath = archivePath.substring(0, archivePath.length() - "/WEB-INF/classes".length()); // Required for wars
    }

    try (InputStream input = new URL(archivePath + "/META-INF/MANIFEST.MF").openStream()) {
      return new Manifest(input);
    } catch (Exception e) {
      throw new RuntimeException("Loading MANIFEST for class " + clz + " failed!", e);
    }
  }
8
muellair

次のようにgetProtectionDomain()。getCodeSource()を使用できます。

URL url = Menu.class.getProtectionDomain().getCodeSource().getLocation();
File file = DataUtilities.urlToFile(url);
JarFile jar = null;
try {
    jar = new JarFile(file);
    Manifest manifest = jar.getManifest();
    Attributes attributes = manifest.getMainAttributes();
    return attributes.getValue("Built-By");
} finally {
    jar.close();
}
6
Uto

この答えは元の質問、一般的にマニフェストにアクセスできるという質問には答えないことを前もって認めます。ただし、実際に必要なのが、多数の「標準」マニフェスト属性の1つを読み取ることである場合、次の解決策は上記の投稿よりもはるかに簡単です。ですから、モデレーターが許可してくれることを願っています。このソリューションはJavaではなくKotlinにあることに注意してください。ただし、Javaへの移植は簡単になると思われます。 (「.`package`」に相当するJavaを知らないことは認めますが。

私の場合、「Implementation-Version」属性を読みたいので、上記のソリューションから始めてストリームを取得し、次にそれを読み取って値を取得しました。このソリューションは機能していましたが、同僚がコードをレビューしてくれたので、私が望んでいたことを簡単に行う方法がわかりました。このソリューションはJavaではなくKotlinにあることに注意してください。

val myPackage = MyApplication::class.Java.`package`
val implementationVersion = myPackage.implementationVersion

繰り返しますが、これは元の質問に答えていないことに注意してください。特に、「エクスポートパッケージ」はサポートされている属性の1つではないようです。ただし、値を返すmyPackage.nameがあります。おそらく、元の投稿者が要求している値を返すかどうかについてコメントするよりも、このことをよく理解している人がいるかもしれません。

5

なぜgetClassLoaderステップを含めるのですか? 「this.getClass()。getResource()」と言うと、呼び出し元のクラスに関連するリソースを取得しているはずです。 ClassLoader.getResource()を使用したことはありませんが、Java Docsを簡単に見ると、現在のクラスパスで見つかったその名前の最初のリソースを取得できるように思えます。

2
Jay
  public static Manifest getManifest( Class<?> cl ) {
    InputStream inputStream = null;
    try {
      URLClassLoader classLoader = (URLClassLoader)cl.getClassLoader();
      String classFilePath = cl.getName().replace('.','/')+".class";
      URL classUrl = classLoader.getResource(classFilePath);
      if ( classUrl==null ) return null;
      String classUri = classUrl.toString();
      if ( !classUri.startsWith("jar:") ) return null;
      int separatorIndex = classUri.lastIndexOf('!');
      if ( separatorIndex<=0 ) return null;
      String manifestUri = classUri.substring(0,separatorIndex+2)+"META-INF/MANIFEST.MF";
      URL url = new URL(manifestUri);
      inputStream = url.openStream();
      return new Manifest( inputStream );
    } catch ( Throwable e ) {
      // handle errors
      ...
      return null;
    } finally {
      if ( inputStream!=null ) {
        try {
          inputStream.close();
        } catch ( Throwable e ) {
          // ignore
        }
      }
    }
  }
1
Alex Konshin

組み込みJettyサーバーでwarアプリケーションを実行するこの奇妙なソリューションがありますが、これらのアプリケーションは標準のTomcatサーバーでも実行する必要があり、manfestにはいくつかの特別なプロパティがあります。

問題は、Tomcatではマニフェストを読み取ることができたが、桟橋ではランダムなマニフェストがピックアップされるということでした(特別なプロパティが見つかりませんでした)

Alex Konshinの答えに基づいて、私は次の解決策を考え出しました(入力ストリームはマニフェストクラスで使用されます)。

private static InputStream getWarManifestInputStreamFromClassJar(Class<?> cl ) {
    InputStream inputStream = null;
    try {
        URLClassLoader classLoader = (URLClassLoader)cl.getClassLoader();
        String classFilePath = cl.getName().replace('.','/')+".class";
        URL classUrl = classLoader.getResource(classFilePath);
        if ( classUrl==null ) return null;
        String classUri = classUrl.toString();
        if ( !classUri.startsWith("jar:") ) return null;
        int separatorIndex = classUri.lastIndexOf('!');
        if ( separatorIndex<=0 ) return null;
        String jarManifestUri = classUri.substring(0,separatorIndex+2);
        String containingWarManifestUri = jarManifestUri.substring(0,jarManifestUri.indexOf("WEB-INF")).replace("jar:file:/","file:///") + MANIFEST_FILE_PATH;
        URL url = new URL(containingWarManifestUri);
        inputStream = url.openStream();
        return inputStream;
    } catch ( Throwable e ) {
        // handle errors
        LOGGER.warn("No manifest file found in war file",e);
        return null;
    }
}
0
GriffoGoes

Anthony Juckelのソリューションを使用しましたが、MANIFEST.MFではキーは大文字で開始する必要があります。

したがって、私のMANIFEST.MFファイルには次のようなキーが含まれています。

Mykey: value

次に、アクティベータまたは別のクラスで、Anthonyのコードを使用して、MANIFEST.MFファイルと必要な値を読み取ることができます。

// If you have a BundleContext 
Dictionary headers = bundleContext.getBundle().getHeaders();

// If you don't have a context, and are running in 4.2 
Bundle bundle = `FrameworkUtil.getBundle(this.getClass()); 
bundle.getHeaders();
0
user2935659