Is there any way to define a constant value to Java at compile time
Asked Answered
F

8

14

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.

Felicidad answered 19/9, 2008 at 11:50 Comment(1)
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. In what working environment do you program that you are worried about people "circumventing" this? Having Ant generate the file is not a hack at all. Of course the manifest approach is even better Java-ism (and you can have Ant automate that too).Rampageous
S
16

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.

Sheet answered 19/9, 2008 at 15:21 Comment(0)
W
11

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.

Wembley answered 19/9, 2008 at 12:16 Comment(0)
S
2

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 type that will be helpful.

Suburbanite answered 19/9, 2008 at 12:2 Comment(0)
F
1

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.

Fascista answered 19/9, 2008 at 11:56 Comment(0)
H
1

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.

Hagbut answered 19/9, 2008 at 12:6 Comment(0)
B
1

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.

Bow answered 19/9, 2008 at 12:11 Comment(0)
F
0

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?

Fluctuation answered 19/9, 2008 at 12:10 Comment(0)
B
0

This can be done with Annotation Processing. You could add a @BuildTime to a class, then the Annotation Processing would create a static method in this class that would return the date it was compiled.

I don't know if there's already one for that, but here is a repo with some: https://github.com/gunnarmorling/awesome-annotation-processing

Another approach (hack) is to get the lastModifiedDate of some file that you know was changed/created at compile time ...

I tested a Spring project, and all the files in the webapp folder had the same lastModifiedDate, which was the date the jar was created.

Brittnee answered 6/1 at 14:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.