Dienstag, 3. Dezember 2013

Reading Manifest attributes from a JAR

In C it was common practice to define a program version number in a file called VERSION. In the Make file there was a line which reads the file and stores its in a variable.
VERSION=$(shell cat VERSION)
And later on it was possible to use the variable as a definition argument for the C compiler:
program.o: program.c
	$(CC) -DVERSION=$(VERSION) -o $@
In the code it was possible to craft a version message by the use of this definition.
fprintf (stderr, "program version %s\n", VERSION);

In Java things are much more complicated. It is common practice in Java to store the version number of a program not in the code of the program itself but in the Manifest file of the JAR, which contains the program. The correct manifest attribute is call Implementation-Version. But there is no simple way to reference the Manifest attributes in a program. Instead it is necessary to calculate a resource URL for the Manifest file based on a class, which is in the same JAR. This URL can be opened as a stream and this stream can be used to parse the contents of the Manifest. The following example shows the class ManifestAttributes, which does this.

class manifest
{
  static int n = 0;
  static void debug (Object message)
  {
    System.err.println ("#" + n + "# " + message);
    n = n + 1;
  }

  public static class ManifestAttributes
  {
    private java.util.jar.Attributes attributes;

    public ManifestAttributes ()
    {
      Class this_class = ManifestAttributes.class;
      String class_name = this_class.getName();
      String class_path = class_name.replace (".", "/") + ".class";
      String class_url = this_class.getResource(class_path).toString();
      String manifest_url = class_url.replace (class_path,
                                               "META-INF/MANIFEST.MF");
      java.io.InputStream stream = null;
      java.util.jar.Manifest manifest;
      try {
        stream = new java.net.URL(manifest_url).openStream();
        manifest = new java.util.jar.Manifest(stream);
      }
      catch (java.io.IOException exception) {
        throw new RuntimeException (exception);
      }
      finally {
        try {
          if (stream != null) stream.close();
        }
        catch (java.io.IOException exception) {
          throw new RuntimeException (exception);
        }
      }
      attributes = manifest.getMainAttributes();
    }

    public String get (String key)
    {
      return attributes.getValue(key);
    }
  }

  public static void main (String[] args)
  {
    ManifestAttributes attributes = new ManifestAttributes();
    debug (attributes.get("Manifest-Version"));
    debug (attributes.get("x"));
  }
}

// Local Variables:
// compile-command: "javac manifest.java && jar cvfe manifest.jar manifest manifest*.class && java -jar manifest.jar"
// End: