Getting Automated Build Information For Your About Dialog

Most all applications have some sort of about dialog which users can access to get basic information about the application they are using. One thing that I feel is important is to know is the build number and date, essentially the version of the software being used.

Figure 1 About Dialog

For Java applications, this would require updating either a class variable or a file with current information. This is error prone and you may forget to change the values before releasing your software. I have written before about automating builds using Jenkins and this is another problem you can solve with Jenkins and an Ant build script.

Jenkins has a set of environment variables which are available for builds. The variable of importance for this issue is the BUILD_NUMBER variable. Each time Jenkins builds a project, this number is incremented. This gives you a way to know exactly which release the user has and you can, if needed, roll your software back to that instance in time using version control. To make this variable available, you need to add the following in your Ant script. You will then be able to access environment variables with “env.VARIABLE”.

<property environment="env" />

Another element necessary for solving this is how you update your application at build time. One solution is creating a file with the necessary data for your dialog. Then, when your application is running you can read this file information, parse it, and display the data in the dialog. This is where Ant comes in.

<tstamp>
  <format property="TODAY" pattern="yyyy-MM-dd" />
</tstamp>

In your Ant script, you need to format the TODAY property with your desired date format as shown above. The current application I am working uses yyyy-MM-dd. Next, you need to determine where you would like the file to be written. I want the file to exist in the same package as my dialog class. I read through my Ant script and determined the best place to insert the build information file. I located where all the source files are just prior to being copied to the build directory and inserted the file there. Then, when they are copied to the build directory  for compiling the informational file gets brought along.

<!-- Create build information file -->
    <echo file="project/src/ffl/dialog/buildInfo.txt" message="${env.BUILD_NUMBER}|${TODAY}" />

The above Ant task, echo, creates the file. A simple message with the BUILD_NUMBER property and the TODAY property is written to the file location specified with the file attribute. I use the pipe to delimit the data so the application can parse it easily. As you can see, the build number is ${env.BUILD_NUMBER} and the date is ${TODAY}. Now your application just has to read the informational data.

//Load the data from the build info file
InputStream inStream = this.getClass().getResourceAsStream("buildInfo.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(inStream));
String dataString = reader.readLine();
String[] dataArray = dataString.split("\\|");
buildLabel.setText(dataArray[0]);
dateLabel.setText(dataArray[1]);
reader.close();

The example above shows how you can read the generated data file. Each time Jenkins builds your project the date and build number are automatically updated and whenever you view the about dialog you are able to know exactly which version is being used.

A JavaFX ImageViewPane

When working with images in a JavaFX application you may want to create a view which will allow an image to be displayed and then remain centered as the application is re-sized. In JavaFX there is no out of the box solution for this.

You may first consider placing an ImageView object in an HBox or VBox, but maintaining aspect ratio and centering in these components is quite challenging. A solution to this is to create a custom ImageViewPane object. The ImageViewPane keeps an image centered and re-sized as the application is re-sized. The code for the class is as follows:

public class ImageViewPane extends AnchorPane {
    
    private Image image;
    
    public ImageViewPane(Image image)
    {
        this.image = image;
    }
    
    public void init()
    {
        ImageView displayImage = new ImageView();
        displayImage.setImage(image);
        displayImage.setPreserveRatio(true);
        displayImage.fitWidthProperty().bind(this.widthProperty());
        displayImage.fitHeightProperty().bind(this.heightProperty());
        
        VBox vbox = new VBox();
        AnchorPane.setTopAnchor(vbox, 0.0);
        AnchorPane.setBottomAnchor(vbox, 0.0);
        AnchorPane.setLeftAnchor(vbox, 0.0);
        AnchorPane.setRightAnchor(vbox, 0.0);
        vbox.setFillWidth(true);
        vbox.centerShapeProperty().set(true);
        vbox.setAlignment(Pos.CENTER);
        
        HBox hbox = new HBox();
        hbox.setCenterShape(true);
        hbox.setAlignment(Pos.CENTER);
        hbox.getChildren().add(displayImage);
        
        vbox.getChildren().add(hbox);
        VBox.setVgrow(hbox, Priority.ALWAYS);
        
        
        this.getChildren().add(vbox);
    }

}

As you can see, the ImageViewPane extends the AnchorPane and takes in the Image object to display in the constructor. Lines 12-16 create the ImageView, set the preservation of the image size ratio and then binds its size to the containing AnchorPane. Lines 18-25 create a VBox to keep the image vertically centered and lines 27-30 create a HBox to keep the image horizontally centered.

Now you can place the ImageViewPane wherever you would like to have an image displayed in your application. You could improve this class by implementing the Initializable interface and placing the contents of the init() in the initialize(URL location, ResourceBundle resources) method. That is all there is to it. Please feel free to make your suggestions for using or improving the ImageViewPane.