我需要阅读该Manifest
文件,该文件提供了我的课程,但是当我使用时:
getClass().getClassLoader().getResources(...)
我MANIFEST
从第一个.jar
加载到Java运行时中就得到了。
我的应用程序将通过applet或Webstart运行,
所以我无法访问自己的.jar
文件。
我实际上是想Export-package
从.jar
启动Felix OSGi的中读取属性,因此可以将这些包公开给Felix。有任何想法吗?
我需要阅读该Manifest
文件,该文件提供了我的课程,但是当我使用时:
getClass().getClassLoader().getResources(...)
我MANIFEST
从第一个.jar
加载到Java运行时中就得到了。
我的应用程序将通过applet或Webstart运行,
所以我无法访问自己的.jar
文件。
我实际上是想Export-package
从.jar
启动Felix OSGi的中读取属性,因此可以将这些包公开给Felix。有任何想法吗?
Answers:
您可以执行以下两项操作之一:
调用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
}
}
您可以尝试检查是否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
}
您可以先找到课程的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");
classPath.replace("org/example/MyClass.class", "META-INF/MANIFEST.MF"
getSimpleName
删除了外部类名称。这将适用于内部类:clazz.getName().replace (".", "/") + ".class"
。
您可以Manifests
从jcabi-manifests中使用,并且只需一行就可以从任何可用的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
<logger name="com.jcabi.manifests" level="OFF"/>
我将首先承认这个答案不能回答最初的问题,即通常能够访问清单的问题。但是,如果真正需要读取许多“标准”清单属性之一,则以下解决方案比上面发布的解决方案简单得多。所以我希望主持人允许。请注意,此解决方案是在Kotlin中而不是Java中进行的,但是我希望Java的移植是微不足道的。(尽管我承认我不知道Java的“ .`package`”。
在我的情况下,我想读取属性“ Implementation-Version”,因此我从上面给出的解决方案开始获取流,然后对其进行读取以获取值。在此解决方案有效的同时,一位同事检查了我的代码,向我展示了一种更轻松的方法来完成我想要的事情。请注意,此解决方案位于Kotlin中,而不是Java中。
val myPackage = MyApplication::class.java.`package`
val implementationVersion = myPackage.implementationVersion
再次注意,这并不能回答原始问题,特别是“ Export-package”似乎不是受支持的属性之一。也就是说,有一个myPackage.name返回一个值。也许有人比我理解得更多,所以我无法评论这是否返回了原始发布者所要求的值。
String implementationVersion = MyApplication.class.getPackage().getImplementationVersion();
我相信获取任何捆绑软件(包括已加载给定类的捆绑软件)清单的最合适方法是使用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。
以下代码适用于多种类型的存档(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);
}
}
clz.getResource(resource).toString()
反斜线吗?
最简单的方法是使用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:/
,所以应该另外处理。
您可以像这样使用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();
}
DataUtilities
进口?它似乎不在JDK中。
为什么要包括getClassLoader步骤?如果您说“ this.getClass()。getResource()”,则应该获取相对于调用类的资源。我从未使用过ClassLoader.getResource(),尽管快速浏览一下Java Docs听起来会为您提供在任何当前类路径中找到的具有该名称的第一个资源。
class.getResource("myresource.txt")
将尝试从中加载该资源com/mypackage/myresource.txt
。您将如何使用这种方法来获取清单?
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
}
}
}
}
cl.getResourceAsStream("META-INF/MANIFEST.MF")
。
classLoader.getResource(..)
和之间的所有代码url.openStream()
都是完全不相关的,并且容易出错,因为它试图做到与之相同classLoader.getResourceAsStream(..)
。
ClassLoader classLoader = cl.getClassLoader(); return new Manifest(classLoader.getResourceAsStream("/META-INF/MANIFEST.MF"));
我使用了Anthony Juckel的解决方案,但在MANIFEST.MF中,密钥必须以大写字母开头。
所以我的MANIFEST.MF文件包含一个像这样的密钥:
我的钥匙:价值
然后,在激活器或其他类中,您可以使用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();
我有这个奇怪的解决方案,可以在嵌入式Jetty服务器中运行war应用程序,但是这些应用程序也需要在标准Tomcat服务器上运行,并且清单中有一些特殊的属性。
问题是,在Tomcat中时,可以读取清单,但是在码头中时,会拾取随机清单(错过了特殊属性)
根据Alex Konshin的回答,我提出了以下解决方案(然后在Manifest类中使用inputstream):
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;
}
}