读我自己的Jar's Manifest

Hou*_*man 124 java osgi manifest.mf apache-felix

我需要读取Manifest传递我的课程的文件,但是当我使用时:

getClass().getClassLoader().getResources(...)
Run Code Online (Sandbox Code Playgroud)

MANIFEST从第一次.jar加载到Java Runtime.
我的应用程序将从小程序或webstart运行,
因此.jar我猜我无法访问自己的文件.

我实际上想要读取启动Felix OSGi 的Export-package属性.jar,所以我可以将这些包暴露给Felix.有任何想法吗?

ZZ *_*der 117

您可以先找到您班级的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");
Run Code Online (Sandbox Code Playgroud)

  • 您需要关闭流,清单构造函数不需要. (3认同)
  • 可以通过删除条件检查`classPath.replace("org/example/MyClass.class", "META-INF/MANIFEST.MF"` 来改进一点 (2认同)
  • 谁关闭了流? (2认同)

Chs*_*y76 110

你可以做以下两件事之一:

  1. 调用getResources()并遍历返回的URL集合,将其作为清单读取,直到找到您的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
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 您可以尝试检查是否getClass().getClassLoader()是实例java.net.URLClassLoader.大多数Sun类加载器都包括在内AppletClassLoader.然后,您可以将其转换并调用findResource()已知的 - 至少对于applet - 直接返回所需的清单:

    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
    }
    
    Run Code Online (Sandbox Code Playgroud)

  • 这是**良好的做法**,为每个人制作**单独的答案,而不是在一个答案中包括2个答案.单独的答案可以独立投票. (7认同)
  • 完善!我从来不知道你可以迭代同名的资源. (4认同)

yeg*_*256 21

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

String value = Manifests.read("My-Attribute");
Run Code Online (Sandbox Code Playgroud)

您需要的唯一依赖是:

<dependency>
  <groupId>com.jcabi</groupId>
  <artifactId>jcabi-manifests</artifactId>
  <version>0.7.5</version>
</dependency>
Run Code Online (Sandbox Code Playgroud)

此外,请参阅此博客文章了解更多详情:http://www.yegor256.com/2014/07/03/how-to-read-manifest-mf.html


Ant*_*kel 11

我认为获取任何bundle的清单(包括加载给定类的bundle)的最合适的方法是使用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();
Run Code Online (Sandbox Code Playgroud)

请注意,Bundle对象还提供getEntry(String path)查找特定包中包含的资源,而不是搜索该包的整个类路径.

通常,如果您需要特定于包的信息,请不要依赖关于类加载器的假设,只需直接使用OSGi API.


小智 9

以下代码适用于多种类型的存档(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);
    }
  }
Run Code Online (Sandbox Code Playgroud)


Ste*_*sen 9

我将首先承认这个答案不能回答最初的问题,即通常能够访问清单的问题。但是,如果真正需要读取多个“标准”清单属性之一,则以下解决方案比上面发布的解决方案简单得多。因此,我希望主持人允许。请注意,此解决方案是在Kotlin中而不是Java中进行的,但是我希望通向Java的端口很简单。(尽管我承认我不知道Java的“ .`package`”。

在我的情况下,我想读取属性“ Implementation-Version”,因此我从上面给出的解决方案开始获取流,然后对其进行读取以获取值。在此解决方案有效的同时,一位同事检查了我的代码,向我展示了一种更轻松的方法来完成我想要的事情。请注意,此解决方案位于Kotlin中,而不是Java中。

val myPackage = MyApplication::class.java.`package`
val implementationVersion = myPackage.implementationVersion
Run Code Online (Sandbox Code Playgroud)

再次注意,这并不能回答原始问题,特别是“ Export-package”似乎不是受支持的属性之一。也就是说,有一个myPackage.name返回一个值。也许有人比我理解得更多,但我无法评论它是否返回原始海报所要求的值。

  • 确实,Java端口很简单:字符串实现版本= MyApplication.class.getPackage()。getImplementationVersion();。 (4认同)

ayu*_*huk 8

最简单的方法是使用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);
Run Code Online (Sandbox Code Playgroud)

因为在某些情况下...class.getProtectionDomain().getCodeSource().getLocation();会给出路径vfs:/,所以这应该另外处理.


Dav*_*e B 7

更简单的方法是使用 getPackage()。例如,要获取实现版本:

Application.class.getPackage().getImplementationVersion()
Run Code Online (Sandbox Code Playgroud)

  • 这并没有具体回答原来的问题,但这正是我正在寻找的。 (2认同)

小智 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();
}
Run Code Online (Sandbox Code Playgroud)

  • 从哪里导入`DataUtilities`?它似乎不在JDK中. (4认同)