views:

3844

answers:

5

I have a webapp which contains a manifest file, in which I write the current version of my application during an ant build task. The manifest file is created correctly, but when I try to read it in during runtime, I get some strange side-effects. My code for reading in the manifest is something like this:

    InputStream manifestStream = Thread.currentThread()
                                 .getContextClassLoader()
                                 .getResourceAsStream("META-INFFFF/MANIFEST.MF");
    try {
     Manifest manifest = new Manifest(manifestStream);
     Attributes attributes = manifest.getMainAttributes();
     String impVersion = attributes.getValue("Implementation-Version");
     mVersionString = impVersion;
    }
    catch(IOException ex) {
     logger.warn("Error while reading version: " + ex.getMessage());
    }

When I attach eclipse to tomcat, I see that the above code works, but it seems to get a different manifest file than the one I expected, which I can tell because the ant version and build timestamp are both different. Then, I put "META-INFFFF" in there, and the above code still works! This means that I'm reading some other manifest, not mine. I also tried

this.getClass().getClassLoader().getResourceAsStream(...)

But the result was the same. What's the proper way to read the manifest file from inside of a webapp running in tomcat?

Edit: Thanks for the suggestions so far. Also, I should note that I am running tomcat standalone; I launch it from the command line, and then attach to the running instance in Eclipse's debugger. That shouldn't make a difference, should it?

+1  A: 

Don't know about a "official" way to read it, but if the MANIFEST.MF can't be properly loaded as a resource, how about trying to derive its path from a "ServletContext.getRealPath()" on some web path defined in your app?

Writing the app version also to some else place (a property file in WEB-INF/classes) by ant during build is another solution that comes to my mind.

david a.
+8  A: 

Maybe your side-effects come from the fact that almost all jars include a MANIFEST.MF and you're not getting the right one. To read the MANIFEST.MF from the webapp, I would say:

ServletContext application = getServletConfig().getServletContext();
InputStream inputStream = application.getResourceAsStream("/META-INF/MANIFEST.MF");
Manifest manifest = new Manifest(inputStream);

Please note that running Tomcat from Eclipse is not the same as running Tomcat alone as Eclipse plays with the classloader.

Pascal Thivent
Indeed, the war manifest wont be on the classpath.
Tom Hawtin - tackline
+1 for giving actual code that actually works.
Bennett McElwee
+1  A: 

The default way class loaders work is to defer to the parent before attempting to lookup their own resources. So if a parent class loader has any manifest available, that's what you'll get. In fact, app servers don't necessarily do this, to allow applications to override versions of libraries. Further, class loaders can have multiple jars and hence multiple manifests.

It may be able to get a resource URL of one of your uniquely named resource. Open a connection. Cast to JarURLConnection. Get the JarFile. Load the manifest from that. That may not work, particularly if Tomcat explodes the war.

[Update] Of course, the war file itself isn't on the classpath. The classpath will have something like WEB-INF/lib/(.jar|.zip) and WEB-INF/classes/. Getting a resource from the ServletContext should work.

Best solution: Do something different. :)

Tom Hawtin - tackline
+2  A: 

Generally this will not work as david a. said. You can work around it like described here http://forums.sun.com/thread.jspa?threadID=642761 but it's not guaranteed that this will work in any servlet engine.

I'll suggest to put the information you need from the manifest in another file during build which you can load as a resource.

ordnungswidrig
+2  A: 

a bit late, but this works for me (web appl in Glassfish)

Properties prop = new Properties();
prop.load(getServletContext().getResourceAsStream("/META-INF/MANIFEST.MF"));
System.out.println("All attributes:" + prop.stringPropertyNames());
System.out.println(prop.getProperty("{whatever attribute you want}"));
devdude