Leyendo mi propio Manifiesto Jar

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?

Puedes hacer una de las dos cosas:

  1. 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 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. Puede intentar verificar si getClass().getClassLoader() es una instancia de java.net.URLClassLoader . La mayoría de los cargadores de clases de Sun son, incluido AppletClassLoader . A continuación, puede convertirlo y llamar a findResource() 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 } 

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-manifiestos 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:

  com.jcabi jcabi-manifests 0.7.5  

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

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.

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

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

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.

¿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.

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

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(); 

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.

Tengo esta extraña solución que ejecuta aplicaciones de guerra en un servidor Jetty incrustado, pero estas aplicaciones también necesitan ejecutarse en servidores Tomcat estándar, y tenemos algunas propiedades especiales en el evento Manfest.

El problema era que cuando en Tomcat, el manifiesto se podía leer, pero cuando estaba en el muelle, se levantaba un manifiesto aleatorio (que no detectaba las propiedades especiales).

En base a la respuesta de Alex Konshin, se me ocurrió la siguiente solución (el inputstream luego se usa en una clase 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; } } 
Intereting Posts