Running on Java 24-ea+25-3155 (Preview)
Home of The JavaSpecialists' Newsletter

026Package Versioning

Author: Herman LintveltDate: 2001-07-25Java Version: 1.3Category: Language
 

Abstract: Packages should have versions numbers embedded in the Jar file's manifest. Our guest author Herman Lintvelt shows us how.

 

Welcome to the 26th issue of The Java(tm) Specialists' Newsletter. My silence these past two weeks was due to having my hands full moving my outlook contacts list to the new mailserver. Yes, it has finally happened! In addition, I've done some cosmetic changes to the archive of newsletters and as you can see, the newsletter is now sent out in HTML format with syntax highlighted code. I want to thank Peter Carruthers for letting me use some ideas from his newsletter for this new format.

I am very grateful to fellow Java guru Herman Lintvelt for authoring this week's newsletter. He is the only person I know who understands the javax.swing.JTree, perhaps I should call him Dr. Swing. In case you need some serious Java GUI development done, please send him an email, or if you have GUI code written by amateurs that needs to be cleaned up.

Herman did his Bachelor of Science Degree in Computer Science in 1998 at "University" of Stellenbosch, and since then has worked almost exclusively in Java, especially using Swing and JDBC, with bits of JAI and JMF. After only 2 years as sucker^H^H^H^H^H^Hemployee, he decided it's not for him and recently started Polymorph Systems to enter the "freelance contractor" market.

Please remember to forward this newsletter to as many Java enthusiasts as you know who might be interested in receiving such a newsletter. Special thanks to the person who sent the last newsletter to their local JUG, it caused about 30 subscriptions :-).

And now for Herman's first newsletter...


javaspecialists.teachable.com: Please visit our new self-study course catalog to see how you can upskill your Java knowledge.

Package Versioning

While having a (rare) bit of idle time in this coldest, wettest winter of probably the last decade down here at the Southern tip of Africa, I thought it might be good to write about package versioning. Talking of wettest, I live on a wine-farm near Stellenbosch, and were it not for my trusty old '76 Jeep, I would have been cut off from civilization last week, as the dirt road was turned into a muddy river, but this is not a 4x4 newsletter, so back to Java(tm)...

Versioning of packages is a very good discipline to follow. Yes, if you have source-control package like SourceSafe, CVS, Clearcase, etc, you can (probably) be rest assured that you can keep track of your sourcecode versions. But what happens if your jar-files are finally (after months of intensive testing by expert testers ;-) distributed to the client and the totally unexpected happens: a bug shows up! You need to be able to determine whether the client had an old version of the package, as the newest version definitely fixed all the remaining bugs.

I like simple things (that's why this newsletter is not complex), and with package versioning it must be the same: it must be easy and fast to keep the package version information up to date (else I will be too tempted to ignore the versioning process). In a project I worked on previously, we added a class to each java package which implemented the same interface and returned the major, minor, patch and build numbers as hard-coded strings. Added to this was a "PackageVersionInterrogator" class that would then get these strings from all the packages when the debug logger required them. This had a few disadvantages:

  1. the debug logger had to know all the packages for which it had to get the package versions (thus any packages from where a class might *possibly* be loaded, would have to be included in the list of known packages for the debug logger)
  2. if the Java app could not even start up, then the client could not browse the jar-file for versioning information, as the version info was contained in class files.

But Alas!!, once again our friends at Sun had a lot of foresight. A while back I "discovered" the existence of the java.lang.Package class, and what's more: this class contains all kinds of nice accessor methods for versioning information! How do you use it? Easy, for every jar file in your project (where every jar file can contain one or more java packages), you need to create your own manifest.mf file (or whatever name you give to it), and invoke the option of the jar-tool to use this manifest file.

Let's look at how this manifest file will look. Suppose we have some classes in a package called com.worldsgreatestApps.java.media and some in a package called com.worldsgreatestApps.java.media.ui. When we run the jar-tool to create a jar-file for these classes, we use the following manifest.mf file (which will be put in the \META-INF subdir inside the jar file):

Manifest-Version: 1.0
Main-Class: jmftest.PackageTest
Specification-Title: Java Media Framework
Specification-Version: 2.0
Specification-Vendor: Sun

Name: com/worldsgreatestApps/java/media/
Implementation-Title: WorldsGreatestApps Super Media Framework
Implementation-Version: 1.0.2
Implementation-Vendor: Polymorph Systems

Name: com/worldsgreatestApps/java/media/ui
Specification-Title: Java Media Framework
Specification-Version: 2.0
Specification-Vendor: Sun
Implementation-Title: WorldsGreatestApps Super Media Framework UI classes
Implementation-Version: 1.1.1
Implementation-Vendor: Polymorph Systems

Some observations:

  1. Main-Class is not part of this versioning, it indicates the main class to run in the jar file, and thus enables you to "run" this jar file directly by using java -jar jarfilename.jar.
  2. Specification-[Title/Version/Vendor] specifies information about the specification implemented inside the package(s)
  3. Implementation-[Title/Version/Vendor] specifies information about the specific implementation contained in the package.
  4. Name (and the empty line in front of it) specifies the name of a package, and must preceed the section for every package in the manifest file. Thus all packages in the jar-file for which we need versioning info (should be all the packages) will have a section in the manifest file.
  5. Both the Specification and Implementation entries can be specified in the "general" section of the manifest file (like the first Specification entries in the example), and these entries will then be used to describe packages that do not have specific entries in the manifest file.
  6. The X-Version entry values are -by convention- in the form of major.minor.micro(/patch), but it can basically be any descriptive string.
  7. The last line of the manifest file should be ended with a newline, else the last line will not be read in probably by the classloader.

How do I use this information in my java code? Well, if the class loader finds package information like this for the package that a class belongs to, it will create a java.lang.Package object for that class, which you can get a handle to by calling on your classinstance.getClass().getPackage(). This object has some methods to get all the info specified in the manifest file.

If no information is specified for a specific package, the classloader first looks to see if there is a "general section" for the version information (i.e. Specification and Implementation entries at the top of the file, before the first empty line). If it is there, it will use this info to populate the Package object; else it will create a Package object that will return null on the version-info method calls.

What's the advantages of using this method to do your package versioning? Well, it's simple and easy to use. Your debug logger can use the standard Java(tm) API to get package version information, and can get a handle on all the loaded packages by calling Package.getPackages(), for example:

/** simple class to list all loaded packages with */
public class PackageLister {
  //...
  public static void listPackages() {
    Package[] allPackages = Package.getPackages();
    System.out.println("All loaded packages:");
    for(int i=0; i < allPackages.length; i++) {
          System.out.println("" + (i+1) +
            ": " + allPackages[i].getName() +
            ": " + allPackages[i].getImplementationTitle() +
            ", version: " + allPackages[i].getImplementationVersion());
    }
  }
  //...
}

Another big advantage: on the client site it's easy to open the jar file and view the manifest file manually if (by some operating system or network error of course) your application won't even start up enough to produce debug output.

I hope this inspired those *few* of you who are not using package versioning techniques to start doing it, and those who are using overly complex techniques or tools, to simplify. I'm very interested to know what versioning "systems" are being used out there, so please feel free to email me at herman@polymorph.co.za with your ideas and descriptions of the systems you are using for versioning your projects.

Vriendelike groete

Herman


P.S. In case you cannot get this working, make sure that you have at least one class from the specified package loaded by the classloader at the time of the call to Package.getPackage() or that call will return null. Thanks to Victor Grazi for pointing that out!

 

Comments

We are always happy to receive comments from our readers. Feel free to send me a comment via email or discuss the newsletter in our JavaSpecialists Slack Channel (Get an invite here)

When you load these comments, you'll be connected to Disqus. Privacy Statement.

Related Articles

Browse the Newsletter Archive

About the Author

Heinz Kabutz Java Conference Speaker

Java Champion, author of the Javaspecialists Newsletter, conference speaking regular... About Heinz

Superpack '23

Superpack '24 Our entire Java Specialists Training in one huge bundle more...

Free Java Book

Dynamic Proxies in Java Book
Java Training

We deliver relevant courses, by top Java developers to produce more resourceful and efficient programmers within their organisations.

Java Consulting

We can help make your Java application run faster and trouble-shoot concurrency and performance bugs...