java - Leyendo mi propio Manifiesto Jar
osgi manifest.mf (11)
Necesito leer el archivo Manifest
, que entregó mi clase, pero cuando uso:
getClass().getClassLoader().getResources(...)
Obtengo el MANIFEST
del primer .jar
cargado en Java Runtime.
Mi aplicación se ejecutará desde un applet o un webstart,
así que no tendré acceso a mi propio archivo .jar
, supongo.
De hecho, quiero leer el atributo Export-package
del .jar
que inició el Felix OSGi, por lo que puedo exponer esos paquetes a Felix. ¿Algunas ideas?
¿Por qué estás incluyendo el paso getClassLoader? Si dice "this.getClass (). GetResource ()" debería obtener recursos relativos a la clase de llamada. Nunca he usado ClassLoader.getResource (), aunque de una mirada rápida a los Java Docs parece que obtendrá el primer recurso de ese nombre encontrado en cualquier classpath actual.
Creo que la forma más adecuada de obtener el manifiesto para cualquier paquete (incluido el paquete que cargó una clase determinada) es usar el objeto Bundle o 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();
Tenga en cuenta que el objeto Bundle también proporciona getEntry(String path)
para buscar recursos contenidos dentro de un paquete específico, en lugar de buscar el classpath completo de ese paquete.
En general, si desea información específica del paquete, no confíe en suposiciones sobre los cargadores de clases, solo use las API OSGi directamente.
Debo admitir desde el principio que esta respuesta no responde a la pregunta original, la de poder acceder en general al Manifiesto. Sin embargo, si lo que realmente se requiere es leer uno de una serie de atributos de Manifiesto "estándar", la siguiente solución es mucho más simple que las publicadas anteriormente. Así que espero que el moderador lo permita. Tenga en cuenta que esta solución está en Kotlin, no en Java, pero esperaría que un puerto a Java fuera trivial. (Aunque admito que no conozco el equivalente Java de ".`package`".
En mi caso, quería leer el atributo "Implementation-Version", así que comencé con las soluciones dadas anteriormente para obtener el flujo y luego lo leí para obtener el valor. Si bien esta solución funcionó, un compañero de trabajo que revisó mi código me mostró una forma más fácil de hacer lo que quería. Tenga en cuenta que esta solución está en Kotlin, no en Java.
val myPackage = MyApplication::class.java.`package`
val implementationVersion = myPackage.implementationVersion
Una vez más, tenga en cuenta que esto no responde a la pregunta original, en particular "Exportar-paquete" no parece ser uno de los atributos admitidos. Dicho esto, hay un myPackage.name que devuelve un valor. Tal vez alguien que entiende esto más de lo que puedo comentar sobre si eso devuelve el valor que el cartel original está solicitando.
El siguiente código funciona con varios tipos de archivos (jar, war) y varios tipos de cargadores de clases (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);
}
}
He usado la solución de Anthony Juckel pero en MANIFEST.MF la clave tiene que comenzar en mayúsculas.
Entonces mi archivo MANIFEST.MF contiene una clave como:
Mykey: valor
Luego, en el activador u otra clase, puede usar el código de Anthony para leer el archivo MANIFEST.MF y el valor que necesita.
// 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();
La forma más fácil es usar la clase 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);
Porque en algunos casos ...class.getProtectionDomain().getCodeSource().getLocation();
da la ruta con vfs:/
, por lo que esto debe manejarse de manera adicional.
Primero puede encontrar la URL de su clase. Si es un JAR, entonces carga el manifiesto desde allí. Por ejemplo,
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");
Puede usar Manifests
de jcabi-manifests y leer cualquier atributo de cualquiera de los archivos MANIFEST.MF disponibles con solo una línea:
String value = Manifests.read("My-Attribute");
La única dependencia que necesitas es:
<dependency>
<groupId>com.jcabi</groupId>
<artifactId>jcabi-manifests</artifactId>
<version>0.7.5</version>
</dependency>
Además, consulte esta publicación en el blog para obtener más información: http://www.yegor256.com/2014/07/03/how-to-read-manifest-mf.html
Puede usar getProtectionDomain (). GetCodeSource () de esta manera:
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();
}
Puedes hacer una de las dos cosas:
Llame a
getResources()
e itere a través de la colección de URL devuelta, leyéndolas como manifiestos hasta que encuentre la suya: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 } }
Puede intentar verificar si
getClass().getClassLoader()
es una instancia dejava.net.URLClassLoader
. La mayoría de los cargadores de clases de Sun son, incluidoAppletClassLoader
. A continuación, puede convertirlo y llamar afindResource()
que se conoce, al menos para los applets, para devolver el manifiesto necesario directamente: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 }
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
}
}
}
}