1

I have a Swing GUI application. I'd like to have a traditional "About" dialog box that displays some copyright information and the application version to the user.

Our build system (based on Gradle) already keeps track of versions, and there is a property file in the source tree containing the major+minor+patch version number.

It would be fairly easy to have Gradle generate a text file (or use the existing properties file), which is installed with the app. The app could read from the file when displaying the About box. I'd like to avoid that, as I don't like the idea of users being able to modify that file.

Maybe the build could hard-code the version somehow into the built Jar?

Or maybe my app could read the data from the Jar files' Manifest somehow? Updating the MANIFEST.MF is easy in Gradle.

I think lots of other must have solved this well before, but I couldn't find anything on Google or here on StackOverflow which solves this for my particular situation. Ideally, I'm looking for consensus on a best practice

Jolta
  • 2,620
  • 1
  • 29
  • 42
  • 1
    _"I'd like to avoid that, as I don't like the idea of users being able to modify that file."_ for once the file with the build number will be buried within your Jar, so your users woul hafe some efforrt to change it and for second: why would your users want to change that? – Timothy Truckle Oct 27 '16 at 12:27
  • If this is worth the effort you could use *asynchronous encryption* to encrypt the version number text file during build and decypt it for display. This would exchange the usual meaning of "public" and "private" key tough. – Timothy Truckle Oct 27 '16 at 12:33
  • Standard manifest attributes can be read with methods of java.lang.Package, such as [Package.getSpecificationVersion()](http://docs.oracle.com/javase/8/docs/api/java/lang/Package.html#getSpecificationVersion--) and [Package.getImplementationVersion()](http://docs.oracle.com/javase/8/docs/api/java/lang/Package.html#getImplementationVersion--). In fact, I use these when building About dialogs in my applications. – VGR Oct 27 '16 at 14:19

3 Answers3

3

It would be easy to use ClassLoader.getResourceAsStream("/META-INF/MANIFEST.MF") to open the manifest file and then parse out what you need. This would work in or out of the jar file.

Ernest Friedman-Hill
  • 80,601
  • 10
  • 150
  • 186
  • @TimothyTruckle I think OP dislikes a text file that is lying around on the disk. IMHO it would be ok to read the Manifest from the (signed?) Jar. – Axel Oct 27 '16 at 12:35
  • files generated during build process do not "lay around on disk" either... – Timothy Truckle Oct 27 '16 at 12:37
  • *"IMHO it would be ok to read the Manifest from the (signed?) Jar."* That's the approach I take to app. versioning (when I bother to implement it). Also note the existence of the [`Manifest`](http://docs.oracle.com/javase/8/docs/api/java/util/jar/Manifest.html) API to make things easier. So while I've seen some great advice from @TimothyTruckle, I disagree with him on this point. ;) – Andrew Thompson Oct 27 '16 at 14:04
  • 2
    Oh wait.. forget the `Manifest` API - I thought there was something better, and it is the [`Package`](http://docs.oracle.com/javase/8/docs/api/java/lang/Package.html) API! It provides many handy methods, including the **[`getImplementationVersion()`](http://docs.oracle.com/javase/8/docs/api/java/lang/Package.html#getImplementationVersion--)** method! – Andrew Thompson Oct 27 '16 at 14:07
  • @AndrewThompson: *"I disagree with him on this point."* you do not disagree with me, only with your own requirement... ;o) – Timothy Truckle Oct 27 '16 at 14:22
  • I tried this, but it's not so easy to get the manifest of the right Jar. My problem is, I have 62 dependency jars and the above seems to return the manifest of the first one. – Jolta Oct 28 '16 at 12:15
  • In the end I went with this solution, which solves the problem of finding the right Jar file in what seems to be the minimum lines of code: http://stackoverflow.com/a/35455854/501739 – Jolta Oct 28 '16 at 12:16
1

Just an idea, but...

During build process you could run an external shellscript to do a replacement in one of your classes' source code. It would take the version as an argument (from Gradle) and hardcode it in your sources. Then, it will get compiled with it.

Should not be too difficult to write. Hope that helps.

Boris Schegolev
  • 3,601
  • 5
  • 21
  • 34
0

Since I want to display a custom attribute (CopyrightYear) I couldn't use the Package API's method to automatically get attributes of the current Jar.

Getting the right data into my jar file's Manifest was the easy part, using Gradle:

jar {
    manifest {
        attributes("Version": project.version, "CopyrightYear": new Date().format("yyyy"))
    }
}

And the java code to retrieve the attributes from the same is adapted from this answer:

private Attributes getMyManifestAttributes() throws IOException {
    String className = getClass().getSimpleName() + ".class";
    String classPath = getClass().getResource(className).toString();
    if (!classPath.startsWith("jar")) {
        throw new IOException("I don't live in a jar file");
    }
    URL url = new URL(classPath);
    JarURLConnection jarConnection = (JarURLConnection) url.openConnection();
    Manifest manifest = jarConnection.getManifest();
    return manifest.getMainAttributes();
}

Using the ClassLoader to locate the manifest as a Resource turned out to deliver me the Manifest files of all my dependency jars, but not of my own one. Go figure. Moreover, it needed more code, to iterate over various resources.

Community
  • 1
  • 1
Jolta
  • 2,620
  • 1
  • 29
  • 42