8

I am trying to get the compilation time of the runnable JAR file I am exporting from Eclipse. One way to do this would probably be to get the modification time of the META-INF/MANIFEST.MF file. Unfortunately I can't seem to find a way to get this information (I know how to read the manifest itself using getResourceAsStream("/META-INF/MANIFEST.MF"), but I can't seem to be able to read its modification time).

Has anybody some insight on how to do it?

CAFxX
  • 28,060
  • 6
  • 41
  • 66

2 Answers2

7

If you have access to the jar file itself, you should be able to read the jar file using java.util.jar package, then get ZipEntry for the Manifest file and use getTime() on it to get its last update time.

JarFile jf = new JarFile("myfile.jar");
ZipEntry manifest = jf.getEntry("META-INF/MANIFEST.MF");
long manifestTime = manifest.getTime();  //in standard millis

If you need to deal with the specific class, then you may find this code also useful;

String rn = this.getClass().getName().replace('.', '/') + ".class";
String path = getClass().getClassLoader().getResource(rn).getPath();
String jarFile = path.substring(0, path.indexOf("!"));

Then jarFile will contain the path name of your jar. Note that if the class is not loaded from the jar file, then the third line will fail, as path.indexOf will return -1.

Aleks G
  • 56,435
  • 29
  • 168
  • 265
  • Sure, but this breaks horribly if the jar is renamed. Can I get a reference to the actual JarFile my main class is being loaded from? – CAFxX Mar 29 '12 at 14:57
  • Yes, you should be able to get that through the classloader object. – Aleks G Mar 29 '12 at 14:58
  • @CAFxX I updated my answer to show how to get the name of the jar file having the class. – Aleks G Mar 29 '12 at 15:03
  • Almost there. The problem is that the `path` variable won't contain an absolute path (when run from a jar file), but only the relative path inside the jar itself (i.e. something like `com/strayorange/test/test.class`). This means the following line will inevitably fail. – CAFxX Mar 30 '12 at 07:58
  • @CAFxX That would be rather strange. As you're requesting the load path of the resource straight from the classloader, it should know the full path of the jar file. – Aleks G Mar 30 '12 at 08:07
  • jarFile is prefixed with "file:". You need something like jarFile = jarFile.replace("file:", "") to get rid of it else the new JarFile() will fail. – Ian Apr 07 '18 at 05:35
5

In the end, based on Aleks G's answer and others found elsewhere, I came up with a more robust solution (that e.g. works also on network shares):

public static Long getTime(Class<?> cl) {
    try {
        String rn = cl.getName().replace('.', '/') + ".class";
        JarURLConnection j = (JarURLConnection) cl.getClassLoader().getResource(rn).openConnection();
        return j.getJarFile().getEntry("META-INF/MANIFEST.MF").getTime();
    } catch (Exception e) {
        return null;
    }
}

I was hoping for a better way to go from the Class object to the resource name but I guess this will have to do.

CAFxX
  • 28,060
  • 6
  • 41
  • 66
  • 1
    You might want to replace `ClassLoader.getSystemResource(rn)` with `cl.getClassLoader().getResource(rn)` in case the class was not loaded using the system classloader – Olivier Gérardin Apr 03 '18 at 14:26