Reading my own Jar's Manifest
Solution 1
You can do one of two things:
-
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 } }
-
You can try checking whether
getClass().getClassLoader()
is an instance ofjava.net.URLClassLoader
. Majority of Sun classloaders are, includingAppletClassLoader
. You can then cast it and callfindResource()
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 }
Solution 2
You can find the URL for your class first. If it's a JAR, then you load the manifest from there. For example,
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");
Solution 3
You can use Manifests
from jcabi-manifests and read any attribute from any of available MANIFEST.MF files with just one line:
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
Solution 4
I will admit up front that this answer does not answer the original question, that of generally being able to access the Manifest. However if what is really required is to read one of a number of "standard" Manifest attributes, the following solution is much simpler than those posted above. So I hope that the moderator will allow it. Note that this solution is in Kotlin, not Java, but I would expect that a port to Java would be trivial. (Although I admit I don't know the Java equivalent of ".`package`".
In my case I wanted to read the attribute "Implementation-Version" so I started with the solutions given above to obtain the stream and then read it to obtain the value. While this solution worked, a coworker reviewing my code showed me an easier way to do what I wanted. Note that this solution is in Kotlin, not Java.
val myPackage = MyApplication::class.java.`package`
val implementationVersion = myPackage.implementationVersion
Once again note that this does not answer the original question, in particular "Export-package" does not seem to be one of the supported attributes. That said, there is a myPackage.name that returns a value. Perhaps someone who understands this more than I can comment on whether that returns the value the original poster is requesting.
Solution 5
The easiest way is to use JarURLConnection class :
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.
Comments
-
Houtman over 3 years
I need to read the
Manifest
file, which delivered my class, but when I use:getClass().getClassLoader().getResources(...)
I get the
MANIFEST
from the first.jar
loaded into the Java Runtime.
My app will be running from an applet or a webstart,
so I will not have access to my own.jar
file, I guess.I actually want to read the
Export-package
attribute from the.jar
which started the Felix OSGi, so I can expose those packages to Felix. Any ideas?-
Chris Dolan almost 12 yearsI think the FrameworkUtil.getBundle() answer below is the best. It answers what you actually want to do (get the bundle's exports) rather than what you asked (read the manifest).
-
-
ChssPly76 almost 15 yearsIf your class is named "com.mypackage.MyClass", calling
class.getResource("myresource.txt")
will try to load that resource fromcom/mypackage/myresource.txt
. How exactly are you going to use this approach to get the manifest? -
Houtman almost 15 yearsPerfect! I never knew you could iterate through resources with the same name.
-
Jay almost 15 yearsOkay, I have to backtrack. That's what comes of not testing. I was thinking that you could say this.getClass().getResource("../../META-INF/MANIFEST.MF") (However many ".."'s are necessary given your package name.) But while that works for class files in a directory to work your way up a directory tree, it apparently doesn't work for JARs. I don't see why not, but that's how it is. Nor does this.getClass().getResource("/META-INF/MANIFEST.MF") work -- that gets me the manifest for rt.jar. (To be continued ...)
-
Jay almost 15 yearsWhat you can do is use getResource to find the path to your own class file, then strip off everything after the "!" to get the path to the jar, then append "/META-INF/MANIFEST.MF". Like Zhihong suggested, so I'm voting his up.
-
Jay almost 15 yearsI like this solution as it gets your own manifest directly rather than having to search for it.
-
Jason S almost 15 yearsHow do you know the classloader is only aware of a single .jar file? (true in many cases I suppose) I would much rather use something associated directly with the class in question.
-
Alba Mendez about 13 yearsit's a good practice to make separate answers for each one, instead of including the 2 fixes in one answer. Separate answers can be voted independently.
-
Gregor over 12 yearsjust a note: I needed something similar but I'm inside a WAR on JBoss, so the second approach didn't work for me. I ended up with a variant of stackoverflow.com/a/1283496/160799
-
Jay over 11 yearscan be improved a little bit by removing condition check
classPath.replace("org/example/MyClass.class", "META-INF/MANIFEST.MF"
-
Petr Gladkikh almost 11 years@chris-dolan Gave the correct answer to this question (see comment above).
-
assylias almost 11 yearsVery nice libraries. Is there a way to control the log level?
-
yegor256 almost 11 yearsAll jcabi libs log through SLF4J. You can dispatch log messages using any facility you wish, for example log4j or logback
-
ceving over 10 yearsWho closes the stream?
-
ceving over 10 yearsThis does not work in inner classes, because
getSimpleName
removes the outer class name. This will work for inner classes:clazz.getName().replace (".", "/") + ".class"
. -
ceving over 10 years
getCodeSource
may returnnull
. What are the criteria, that this will work? The documentation does not explain this. -
robermann over 10 years+1; BTW, I've just found that EAR/META-INF/... is not included into the runtime classpath (on Weblogic 10.3.5), so it cannot be inspected as a resource.
-
kevinarpe about 9 yearsThanks to add the wiring around
java.util.jar.Manifest
. I didn't know about that class before I read that post. I probably would have dumbly / manually parsed the Manifest file myself... -
BrianT. almost 9 yearsYou need to close the stream, the manifest constructor does not.
-
floer_m almost 9 yearsif using logback.xml the line you need to add is like
<logger name="com.jcabi.manifests" level="OFF"/>
-
Gordon almost 9 yearsManifest is giving me a rather useless empty map, so I still have to parse it manually. Still useful code to get the resource at first, though.
-
Jolta over 7 yearsWhere is
DataUtilities
imported from? It doesn't seem to be in the JDK. -
Jolta over 7 yearsThe first option didn't work for me. I got the manifests of my 62 dependency jars, but not the one where the current class was defined...
-
Robert almost 7 yearsThis answer uses a very complex and error prone way of loading the Manifest. the much simpler solution is to use
cl.getResourceAsStream("META-INF/MANIFEST.MF")
. -
Alex Konshin almost 7 yearsDid you try it? What jar manifest it will get if you have multiple jars in classpath? It will take the first one that is not what you need. My code solves this problem and it really works.
-
Robert almost 7 yearsI did not criticize the way how you us e the classloader for loading a specific resource. I was pointing out that all the code between
classLoader.getResource(..)
andurl.openStream()
is totally irrelevant and error prone as it tries to do the same asclassLoader.getResourceAsStream(..)
does. -
Alex Konshin almost 7 yearsNope. It is different. My code takes manifest from the specific jar where the class is located rather than from the the first jar in the classpath.
-
Robert almost 7 yearsYour "jar specific loading code" is equivalent to the following two lines:
ClassLoader classLoader = cl.getClassLoader(); return new Manifest(classLoader.getResourceAsStream("/META-INF/MANIFEST.MF"));
-
aprodan over 6 yearsindeed this solution can be refactored but it works. The two liners solution proposed by Robert ends-up with NullPointerException in spring boot applications.
-
Chris Janicki over 6 yearsNote that functions attr.containsKey("Manifest-Version") and attr.get("Manifest-Version") will return null since those functions require arguments that are of the inner class Attribute.Name. Unfortunately the functions accept Object... so there's no compile-time error when giving it a String, but it will return a null value. So stick with Attribute.getValue() as show in the example above. Attributes.getValue() explicitly accepts a String, and then wraps it in a new Attributes.Name for you. (I know this is because Attributes implements Map, but it's just not safe API coding in my opinion.)
-
basin over 6 yearscan result of
clz.getResource(resource).toString()
have backslashes? -
guai about 6 yearsMultiple manifests from the same classloader overlap and overwrite each other
-
Ian Robertson over 5 yearsIndeed, the java port is straightforward:
String implementationVersion = MyApplication.class.getPackage().getImplementationVersion();
-
Aleksander Stelmaczonek over 5 yearsIt fact this is what I was looking for. I'm also happy that Java has also an equivalent.
-
walen almost 5 yearsThis is, by far, the easiest and cleanest way to do it.
-
Matthew about 4 yearsOption 2 appears to have stopped working as-of java 11 (maybe java 9).
-
Greg Brown about 2 yearsThis doesn't answer the original question specifically, but it is exactly what I was looking for.