Lire mon propre manifeste de Jar

Je dois lire le fichier Manifest , qui a livré ma classe, mais quand j’utilise:

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

MANIFEST le MANIFEST du premier .jar chargé dans Java Runtime.
Mon application sera lancée depuis une applet ou un webstart,
donc je n’aurai pas access à mon propre fichier .jar , je suppose.

Je souhaite en fait lire l’atsortingbut Export-package du .jar qui a démarré Felix OSGi, afin de pouvoir exposer ces packages à Felix. Des idées?

Vous pouvez faire l’une des deux choses suivantes:

  1. Appelez getResources() et parcourez la collection d’URL renvoyée, en les lisant comme manifestes jusqu’à ce que vous trouviez le vôtre:

     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. Vous pouvez essayer de vérifier si getClass().getClassLoader() est une instance de java.net.URLClassLoader . La majorité des chargeurs de AppletClassLoader Sun sont, y compris AppletClassLoader . Vous pouvez alors le lancer et appeler findResource() connu pour que les applets, au moins, renvoient directement le manifeste nécessaire:

     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 } 

Vous pouvez trouver l’URL de votre classe en premier. Si c’est un JAR, alors vous chargez le manifeste à partir de là. Par exemple,

 Class clazz = MyClass.class; Ssortingng className = clazz.getSimpleName() + ".class"; Ssortingng classPath = clazz.getResource(className).toSsortingng(); if (!classPath.startsWith("jar")) { // Class not from JAR return; } Ssortingng manifestPath = classPath.subssortingng(0, classPath.lastIndexOf("!") + 1) + "/META-INF/MANIFEST.MF"; Manifest manifest = new Manifest(new URL(manifestPath).openStream()); Atsortingbutes attr = manifest.getMainAtsortingbutes(); Ssortingng value = attr.getValue("Manifest-Version"); 

Vous pouvez utiliser Manifests de jcabi-manifestests et lire n’importe quel atsortingbut de n’importe lequel des fichiers MANIFEST.MF disponibles avec une seule ligne:

 Ssortingng value = Manifests.read("My-Atsortingbute"); 

La seule dépendance dont vous avez besoin est:

  com.jcabi jcabi-manifests 0.7.5  

Aussi, voir cet article de blog pour plus de détails: http://www.yegor256.com/2014/07/03/how-to-read-manifest-mf.html

Je pense que le moyen le plus approprié d’obtenir le manifeste pour un ensemble (y compris le groupe qui a chargé une classe donnée) consiste à utiliser l’object Bundle ou 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(); 

Notez que l’object Bundle fournit également getEntry(Ssortingng path) pour rechercher des ressources contenues dans un bundle spécifique, plutôt que de rechercher l’intégralité du classpath de ce bundle.

En général, si vous souhaitez des informations spécifiques à un bundle, ne vous fiez pas aux hypothèses concernant les chargeurs de classes, utilisez simplement les API OSGi directement.

Le code suivant fonctionne avec plusieurs types d’archives (jar, war) et plusieurs types de chargeurs de classes (jar, url, vfs, …)

  public static Manifest getManifest(Class clz) { Ssortingng resource = "/" + clz.getName().replace(".", "/") + ".class"; Ssortingng fullPath = clz.getResource(resource).toSsortingng(); Ssortingng archivePath = fullPath.subssortingng(0, fullPath.length() - resource.length()); if (archivePath.endsWith("\\WEB-INF\\classes") || archivePath.endsWith("/WEB-INF/classes")) { archivePath = archivePath.subssortingng(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); } } 

Vous pouvez utiliser getProtectionDomain (). GetCodeSource () comme ceci:

 URL url = Menu.class.getProtectionDomain().getCodeSource().getLocation(); File file = DataUtilities.urlToFile(url); JarFile jar = null; try { jar = new JarFile(file); Manifest manifest = jar.getManifest(); Atsortingbutes atsortingbutes = manifest.getMainAtsortingbutes(); return atsortingbutes.getValue("Built-By"); } finally { jar.close(); } 

Le plus simple est d’utiliser la classe JarURLConnection:

 Ssortingng className = getClass().getSimpleName() + ".class"; Ssortingng classPath = getClass().getResource(className).toSsortingng(); if (!classPath.startsWith("jar")) { return DEFAULT_PROPERTY_VALUE; } URL url = new URL(classPath); JarURLConnection jarConnection = (JarURLConnection) url.openConnection(); Manifest manifest = jarConnection.getManifest(); Atsortingbutes atsortingbutes = manifest.getMainAtsortingbutes(); return atsortingbutes.getValue(PROPERTY_NAME); 

Parce que dans certains cas ...class.getProtectionDomain().getCodeSource().getLocation(); donne le chemin avec vfs:/ , donc cela doit être traité en plus.

Pourquoi incluez-vous l’étape getClassLoader? Si vous dites “this.getClass (). GetResource ()”, vous devriez obtenir des ressources relatives à la classe appelante. Je n’ai jamais utilisé ClassLoader.getResource (), mais après avoir jeté un coup d’œil sur les documents Java, il semblerait que cela vous fournisse la première ressource de ce nom trouvée dans n’importe quel classpath actuel.

  public static Manifest getManifest( Class cl ) { InputStream inputStream = null; try { URLClassLoader classLoader = (URLClassLoader)cl.getClassLoader(); Ssortingng classFilePath = cl.getName().replace('.','/')+".class"; URL classUrl = classLoader.getResource(classFilePath); if ( classUrl==null ) return null; Ssortingng classUri = classUrl.toSsortingng(); 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 } } } } 

J’ai utilisé la solution d’Anthony Juckel mais dans le MANIFEST.MF, la clé doit commencer par des majuscules.

Donc, mon fichier MANIFEST.MF contient une clé comme:

Mykey: valeur

Ensuite, dans l’activateur ou une autre classe, vous pouvez utiliser le code d’Anthony pour lire le fichier MANIFEST.MF et la valeur dont vous avez besoin.

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

J’admets d’emblée que cette réponse ne répond pas à la question initiale, à savoir pouvoir généralement accéder au manifeste. Cependant, si ce qui est vraiment nécessaire est de lire l’un des nombreux atsortingbuts de manifeste “standard”, la solution suivante est beaucoup plus simple que celles affichées ci-dessus. J’espère donc que le modérateur le permettra. Notez que cette solution est dans Kotlin, pas dans Java, mais je m’attendrais à ce qu’un port Java soit sortingvial. (Bien que j’avoue que je ne connais pas l’équivalent Java de “.`package`”.

Dans mon cas, je voulais lire l’atsortingbut “Version-d’implémentation”. J’ai donc commencé avec les solutions données ci-dessus pour obtenir le stream et ensuite le lire pour obtenir la valeur. Alors que cette solution fonctionnait, un collègue examinant mon code m’a montré un moyen plus facile de faire ce que je voulais. Notez que cette solution est dans Kotlin, pas dans Java.

 val myPackage = MyApplication::class.java.`package` val implementationVersion = myPackage.implementationVersion 

Notez encore une fois que cela ne répond pas à la question initiale, en particulier “Export-package” ne semble pas être l’un des atsortingbuts pris en charge. Cela dit, il y a un myPackage.name qui renvoie une valeur. Peut-être quelqu’un qui comprend cela plus que je ne peux dire si cela renvoie la valeur demandée par l’affiche originale.

J’ai cette solution bizarre qui exécute des applications de guerre dans un serveur Jetty intégré, mais ces applications doivent également être exécutées sur des serveurs Tomcat standard, et nous avons des propriétés spéciales dans manfest.

Le problème était que, dans Tomcat, le manifeste pouvait être lu, mais quand il était dans jetty, un manifeste aléatoire était détecté (qui manquait les propriétés spéciales).

Sur la base de la réponse d’Alex Konshin, j’ai proposé la solution suivante (le stream d’entrée est ensuite utilisé dans une classe Manifest):

 private static InputStream getWarManifestInputStreamFromClassJar(Class cl ) { InputStream inputStream = null; try { URLClassLoader classLoader = (URLClassLoader)cl.getClassLoader(); Ssortingng classFilePath = cl.getName().replace('.','/')+".class"; URL classUrl = classLoader.getResource(classFilePath); if ( classUrl==null ) return null; Ssortingng classUri = classUrl.toSsortingng(); 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; } }