读我自己的JAR的Manifest(Reading my own Jar's Manifes

2019-06-18 10:33发布

我需要阅读的Manifest文件,该文件交付我的课,但是当我使用:

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

我得到的MANIFEST从第一.jar加载到Java运行时。
我的应用程序会从applet或Webstart的运行,
所以我不会有我自己的访问.jar文件,我猜。

其实我是想读的Export-package从属性.jar赖以起家的费利克斯OSGi的,所以我可以公开这些包菲利克斯。 有任何想法吗?

Answer 1:

你可以做两件事情之一:

  1. 呼叫getResources()并通过网址返回集合迭代,阅读他们的清单,直到你找到你的:

     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 。 然后,您可以将它转换和调用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 } 


Answer 2:

你可以先找到你的类的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");


Answer 3:

您可以使用Manifests从jcabi-清单和读取任何可用的MANIFEST.MF文件的任何属性只用一行:

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



Answer 4:

我认为最合适的方式来获得清单的任何束(包括加载指定类的包)是使用捆绑或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();

请注意,捆绑对象还提供getEntry(String path)来查找包含特定包内的资源,而不是寻找那束的整个类路径。

在一般情况下,如果你想捆绑特定的信息,不依赖于有关的类加载器臆断,我只是直接使用的OSGi的API。



Answer 5:

下面的代码可与多种类型的档案(JAR,WAR)和多种类型的类加载器(罐,网址,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);
    }
  }


Answer 6:

您可以使用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();
}


Answer 7:

最简单的方法是使用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:/ ,所以这应该是另外处理。



Answer 8:

我承认了前面这个答案没有回答原来的问题,通常能够访问清单的。 但是,如果什么是真正需要的是读了一些“标准” Manifest属性之一,下面的解决方案比那些贴上面简单得多。 因此,我希望主持人将允许它。 请注意,此解决方案是在科特林,而不是Java,但我希望有一个端口到Java将是微不足道的。 (虽然我承认我不知道在Java相当于“.`package`”的。

在我来说,我想读属性“实施版”,所以我开始与上面给出获取流,然后读取它来获取价值的解决方案。 虽然这种解决方案的工作,同事审查我的代码给我一个更简单的方法做我想要的。 请注意,此解决方案是在科特林,而不是Java。

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

再次注意,这不回答原来的问题,特别是“导出包”似乎并不被支持的属性之一。 这就是说,有一个返回值myPackage.name。 也许有人谁明白这超过了我能就是否返回原来的海报正在请求值评论。



Answer 9:

你为什么包括getClassLoader一步? 如果你说“this.getClass()。getResource()方法”你应该得到相对于调用类的资源。 我从来没有用过ClassLoader.getResource方法,(),虽然从简单看一下Java文档,它听起来就像,将让你在任何当前classpath发现名字的第一资源。



Answer 10:

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


Answer 11:

我已经使用从安东尼Juckel的解决方案,但在MANIFEST.MF关键不得不开始与大写。

所以,我的MANIFEST.MF文件包含就象是一把钥匙:

的myKey:

然后在活化剂或其他类,你可以使用代码从安东尼读取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();


Answer 12:

我有一个运行在嵌入式Jetty服务器战争应用这种怪异的解决方案,但这些应用程序也需要在标准的Tomcat服务器上运行,而我们在manfest某些特殊性能。

问题是,当在Tomcat中,清单可以被理解,但是当在码头,随机清单明显回升(这错过了特殊属性)

基于亚历克斯Konshin的答案,我想出了以下解决方案(InputStream中,然后在Manifest类使用):

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


文章来源: Reading my own Jar's Manifest