tags:

views:

1327

answers:

7

When I used to write libraries in C/C++ I got into the habit of having a method to return the compile date/time. This was always a compiled into the library so would differentiate builds of the library. I got this by returning a #define in the code:

C++:

#ifdef _BuildDateTime_
   char* SomeClass::getBuildDateTime() {
      return _BuildDateTime_;
   }
#else
   char* SomeClass::getBuildDateTime() {
      return "Undefined";
   }
#endif

Then on the compile I had a '-D_BuildDateTime_=Date' in the build script.

Is there any way to achieve this or similar in Java without needing to remember to edit any files manually or distributing any seperate files.

One suggestion I got from a co-worker was to get the ant file to create a file on the classpath and to package that into the JAR and have it read by the method.

Something like (assuming the file created was called 'DateTime.dat'):

// I know Exceptions and proper open/closing 
// of the file are not done. This is just 
// to explain the point!
String getBuildDateTime() {
    return new BufferedReader(getClass()
            .getResourceAsStream("DateTime.dat")).readLine();
}

To my mind that's a hack and could be circumvented/broken by someone having a similarly named file outside the JAR, but on the classpath.

Anyway, my question is whether there is any way to inject a constant into a class at compile time

EDIT

The reason I consider using an externally generated file in the JAR a hack is because this is) a library and will be embedded in client apps. These client apps may define their own classloaders meaning I can't rely on the standard JVM class loading rules.

My personal preference would be to go with using the date from the JAR file as suggested by serg10.

Thanks for your help!

+1  A: 

Unless you want to run your Java source through a C/C++ Preprocessor (which is a BIG NO-NO), use the jar method. There are other ways to get the correct resources out of a jar to make sure someone didn't put a duplicate resource on the classpath. You could also consider using the Jar manifest for this. My project does exactly what you're trying to do (with build dates, revisions, author, etc) using the manifest.

You'll want to use this:

Enumeration<URL> resources = Thread.currentThread().getContextClassLoader().getResources("META-INF/MANIFEST.MF");

This will get you ALL of the manifests on the classpath. You can figure out which jar they can from by parsing the URL.

basszero
+2  A: 

AFAIK there is not a way to do this with javac. This can easily be done with ant -- I would create a first class object called BuildTimestamp.java and generate that file at compile time via an ant target.

Here's an ant task that will be helpful:

jon
+1  A: 

Personally I'd go for a separate properties file in your jar that you'd load at runtime... The classloader has a defined order for searching for files - I can't remember how it works exactly off hand, but I don't think another file with the same name somewhere on the classpath would be likely to cause issues.

But another way you could do it would be to use Ant to copy your .java files into a different directory before compiling them, filtering in String constants as appropriate. You could use something like:

public String getBuildDateTime() {
    return "@BUILD_DATE_TIME@";
}

and write a filter in your Ant file to replace that with a build property.

MB
A: 

One suggestion I got from a co-worker was to get the ant file to create a file on the classpath and to package that into the JAR and have it read by the method. ... To my mind that's a hack and could be circumvented/broken by someone having a similarly named file outside the JAR, but on the classpath.

I'm not sure that getting Ant to generate a file is a terribly egregious hack, if it's a hack at all. Why not generate a properties file and use java.util.Properties to handle it?

Paul A. Hoadley
+1  A: 

Perhaps a more Java-style way of indicating your library's version would be to add a version number to the JAR's manifest, as described in the manifest documentation.

Peter Hilton
+6  A: 

I remember seeing something similar in an open source project:

class Version... {
  public static String tstamp() {
    return "@BUILDTIME@";
  }
}

in a template file. With Ant's filtering copy you can give this macro a value:

<copy src="templatefile" dst="Version.java" filtering="true">
    <filter token="BUILDTIME" value="${build.tstamp}" />
</copy>

use this to create a Version.java source file in your build process, before the compilation step.

+8  A: 

I would favour the standards based approach. Put your version information (along with other useful publisher stuff such as build number, subversion revision number, author, company details, etc) in the jar's Manifest File.

This is a well documented and understood Java specification. Strong tool support exists for creating manifest files (a core ant task for example, or the maven jar plugin). These can help with setting some of the attributes automatically - I have maven configured to put the jar's maven version number, Subversion revision and timestamp into the manifest for me at build time.

You can read the contents of the manifest at runtime with standard java api calls - something like:

import java.util.jar.*;

...

JarFile myJar = new JarFile("nameOfJar.jar");    // various constructors available
Manifest manifest = myJar.getManifest();
Map<String,Attributes> manifestContents = manifest.getAttributes();

To me, that feels like a more Java standard approach, so will probably prove more easy for subsequent code maintainers to follow.

serg10