Java 阅读我自己的 Jar 清单

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1272648/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 07:41:16  来源:igfitidea点击:

Reading my own Jar's Manifest

javaosgimanifest.mfapache-felix

提问by Houtman

I need to read the Manifestfile, which delivered my class, but when I use:

我需要阅读该Manifest文件,该文件提供了我的课程,但是当我使用时:

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

I get the MANIFESTfrom the first .jarloaded into the Java Runtime.
My app will be running from an applet or a webstart,
so I will not have access to my own .jarfile, I guess.

MANIFEST从第一个.jar加载到 Java 运行时获取。
我的应用程序将从小程序或 webstart 运行,
所以我想我将无法访问我自己的.jar文件。

I actually want to read the Export-packageattribute from the .jarwhich started the Felix OSGi, so I can expose those packages to Felix. Any ideas?

我实际上想Export-package.jar启动 Felix OSGi的属性中读取属性,这样我就可以将这些包公开给 Felix。有任何想法吗?

采纳答案by ChssPly76

You can do one of two things:

您可以执行以下两项操作之一:

  1. Call getResources()and iterate through the returned collection of URLs, reading them as manifests until you find yours:

    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. You can try checking whether getClass().getClassLoader()is an instance of java.net.URLClassLoader. Majority of Sun classloaders are, including AppletClassLoader. You can then cast it and call findResource()which has been known - for applets, at least - to return the needed manifest directly:

    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
    }
    
  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。大多数 Sun 类加载器是,包括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
    }
    

回答by Jay

Why are you including the getClassLoader step? If you say "this.getClass().getResource()" you should be getting resources relative to the calling class. I've never used ClassLoader.getResource(), though from a quick look at the Java Docs it sounds like that will get you the first resource of that name found in any current classpath.

为什么要包含 getClassLoader 步骤?如果您说“this.getClass().getResource()”,您应该获取与调用类相关的资源。我从未使用过 ClassLoader.getResource(),但从 Java Docs 的快速浏览来看,这听起来会让您在任何当前类路径中找到该名称的第一个资源。

回答by ZZ Coder

You can find the URL for your class first. If it's a JAR, then you load the manifest from there. For example,

您可以先找到您班级的 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");

回答by Anthony Juckel

I believe the most appropriate way to get the manifest for any bundle (including the bundle which loaded a given class) is to use the Bundle or BundleContext object.

我相信获取任何包(包括加载给定类的包)的清单的最合适方法是使用 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();

Note that the Bundle object also provides getEntry(String path)to look up resources contained within a specific bundle, rather than searching that bundle's entire classpath.

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

In general, if you want bundle-specific information, do not rely upon assumptions about the classloaders, just use the OSGi APIs directly.

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

回答by yegor256

You can use Manifestsfrom jcabi-manifestsand read any attribute from any of available MANIFEST.MF files with just one line:

您可以使用Manifestsfrom jcabi-manifests并从任何可用的 MANIFEST.MF 文件中读取任何属性,只需一行:

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

The only dependency you need is:

您需要的唯一依赖是:

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

Also, see this blog post for more details: http://www.yegor256.com/2014/07/03/how-to-read-manifest-mf.html

另外,有关更多详细信息,请参阅此博客文章:http: //www.yegor256.com/2014/07/03/how-to-read-manifest-mf.html

回答by Uto

You can use getProtectionDomain().getCodeSource() like this :

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

回答by user2935659

I have used the solution from Anthony Juckel but in the MANIFEST.MF the key have to start with uppercase.

我使用了 Anthony Juckel 的解决方案,但在 MANIFEST.MF 中,密钥必须以大写开头。

So my MANIFEST.MF file contain a key like:

所以我的 MANIFEST.MF 文件包含一个键,如:

Mykey:value

Mykey:价值

Then in the activator or another class you can use the code from Anthony to read the MANIFEST.MF file and the the value that you need.

然后在激活器或其他类中,您可以使用 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();

回答by Alex Konshin

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

回答by muellair

The following code works with multiple types of archives (jar, war) and multiple types of classloaders (jar, url, vfs, ...)

以下代码适用于多种类型的档案(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);
    }
  }

回答by ayurchuk

The easiest way is to use JarURLConnection class :

最简单的方法是使用 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);

Because in some cases ...class.getProtectionDomain().getCodeSource().getLocation();gives path with vfs:/, so this should be handled additionally.

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