Automatic Version Identifier

I’m not sure if this is a common knowledge thing that I’ve not picked up in the past but here we go… For those of us who have used C and C++ the idea of having a string generated based on the compilation date of the code is pretty normal. Its really useful for identifing the version of the software the end user has. In C this is done with a preprocessor macro that gets converted into today date.

I’ve been wanting something like this for Java for a while and this morning I had an idea which I’m now using happily. I simple grab the date of the “main” class file from my game java like this:


URL versionUrl = getClass().getClassLoader().getResource("org/newdawn/grav2/GameWindow.class");
try {
                // update the title of the window based on the version
      title += " ("+new Date(versionUrl.openConnection().getLastModified())+")";
} catch (Exception e) {
      title += " (Version Undetermined)";
}

Maybe theres a better way that I don’t already know about? I quite like this tho since it gives you a different date even if you just repackage your jar.

Kev

Nice.

What I do is use the Version info in the JAR Manifest file. I generate the JAR with Ant so I have Ant put the build date and other version info from Ant properties (supplied on ant command line) into the manifest version line. Then I extract that info at runtime.

I think this does the trick:


String appVersion =  new JarFile("test.jar").
   getManifest().
       getMainAttributes().
         getValue("Specification-version");

[quote]Nice.

What I do is use the Version info in the JAR Manifest file. I generate the JAR with Ant so I have Ant put the build date and
[/quote]
IME this is usually the best way to do it, since it’s compatible with all auto-build systems (shrug; I don’t use ANT, but anything that’s able to auto-build a JAR should have a macro to let you do this stuff!). Our build system uniquely versions every build with an incremented version number automatically, so it’s trivial.

The only problem is that the JAR file manifest is a shoddy hacked-together afterthought, and has some big holes in it’s design. It’s OK at first, but for large projects it quickly becomes useless, and you have to start inventing lots of extra proprietary meta-data because Sun didn’t :(.

So, if anyone knows of slightly-less-proprietary package-versioning system for java, I beg you to enlighten me! :slight_smile:

OOI, how do you get hold of the jar file when you’re running in webstart? You can’t incidently look up meta-data type information from the jar file using the classpath (since its not really in the classpath).

Kev

This should work with both Applets and WebStart applications (off the top of my head… haven’t actually tested:)):

      public String getAppVersion(Class cls) throws IOException {
            String classFile = cls.getName().replaceAll("\\.", "/") + ".class";
            //System.out.println("class file:" + classFile);
            URL classLocation = cls.getResource(classFile);
            //System.out.println("class location: " + classLocation);            
            JarFile jarFile = ((JarURLConnection)classLocation.openConnection()).getJarFile();
            Manifest mf = jarFile.getManifest();
            String appVersion = mf.getMainAttributes().getValue(Attributes.Name.SPECIFICATION_VERSION);
            //System.out.println("application version: " + appVersion);
            
            return appVersion;
      }

Hmmm… Kevin…

versionUrl.openConnection().getLastModified()

“Returns the value of the last-modified header field. The result is the number of milliseconds since January 1, 1970 GMT.”

So we get a long which accurately tells us the date (pretty ok-ish if you use .toHexString()). However, that new Date() thingy (and it’s toString()) produces a date which is somewhat difficult to compare. Eg I get it in “CEST” - my local timezone.

Something like this would be better:


import java.text.DateFormat;
[...]
DateFormat df = DateFormat.getDateTimeInstance(DateFormat.MEDIUM,DateFormat.SHORT,Locale.ENGLISH);
df.setTimeZone(TimeZone.getTimeZone("GMT"));
System.out.println(df.format(new Date(versionUrl.openConnection().getLastModified())));

The locale was explictly set in order to get identical formatting.

Now I get:
Jul 11, 2004 10:56 PM

Instead of:
Mon Jul 12 00:56:48 CEST 2004 (and everyone would get something else here)

getLastModified() with toHexString and toUpperCase looks like this:
FDB06C3780 (also ok and comparable)

Anyways… great idea :slight_smile:

Erg, fair point :wink: Thanks for the point out.

Will fix,

Kev

Well, the documentation wasn’t that specific (acutally it was pretty generic and vague… returns a String representation blabla ::)).

Oh and that hex timestamp (FDB06C3780)… I’ll send that value together with highscores. So if a change comes (which invalidates older scores), I’ll be able to filter those scores out. I don’t think that something like that could happen, but you can never know :wink:

I also implemented error codes for version missmatch, because it’s a thing which just will happen (“offline allowed” is to blame btw). Hm… eventually I should put another version number into it (which indicates changes in the scoring system)? :slight_smile:

[quote]This should work with both Applets and WebStart applications (off the top of my head… haven’t actually tested:)):

      public String getAppVersion(Class cls) throws IOException {
            String classFile = cls.getName().replaceAll("\\.", "/") + ".class";
            //System.out.println("class file:" + classFile);
            URL classLocation = cls.getResource(classFile);
            //System.out.println("class location: " + classLocation);            
            JarFile jarFile = ((JarURLConnection)classLocation.openConnection()).getJarFile();
            Manifest mf = jarFile.getManifest();
            String appVersion = mf.getMainAttributes().getValue(Attributes.Name.SPECIFICATION_VERSION);
            //System.out.println("application version: " + appVersion);
            
            return appVersion;
      }

[/quote]
Even better, if using ant/other automatic build system, is IMO:


public String getAppVersion(Class clazz) {
    Package p = clazz.getPackage();
    // or is it p.getImplementationVersion()?
    return (p == null) ? null : p.getSpecificationVersion(); 
}

Haven’t tested in applets, works in webstart though IIRC