Rowanto Luo


Just another blog. or log.


Adding New Currency in Java 8

If you are using the java.lang.Currency as part of your entity, most likely you are already regretting it now. Most of the time, a currency is a three letter word, and we don't need the logic to be bundled in there. Keeping things simple and mapping these currencies as a String or own custom class is the way to go.

For people who did use java.lang.Currency, the pain will arrive when a country changed its currency or added a new one. As example, Belarus changed its currency from BYR to BYN at July 1st, 2016. If you are using java.lang.Currency, you prolly already know by now that Currency.getInstance("BYN") will throw you an exception. Most likely you have also google around, and most likely again, you will stumble upon this or this. It's basically telling you that there is this new feature in java 7, which lets you overwrite the currency. You will happily overwrite BYR to BYN, and now it starts working. This happyness doesn't last long as you realize that now that you support BYN, but not BYR anymore. You might also stumble upon some pages which write that we can't support both currency at the same time.

For those who have these problems, we actually have a solution. There is actually another file called currency.data in your jre/lib/ directory, and if you patch this file here, you will be able to support multi currency. There is even a stackoverflow answer here. The problem is, if you follow the instruction, your new currency.data will not work if you are using java 8.

Currency data format is incorrect  

Well, this is because, if you check the Java8 java.lang.Currency, it has a new currency data format version.

    // Currency data format version
    private static final int VALID_FORMAT_VERSION = 2;
    static {
        ...
                if (formatVersion != VALID_FORMAT_VERSION) {
                    throw new InternalError("Currency data format is incorrect");
                }
        ...
    }

which means, that we will need to also create a version two of the CurrencyData.properties, and use the version two of the GenerateCurrencyData.java to generate the binary file.

The rest is the same as in the stackoverflow answer. Basically you take the currency data, add your currency, recompile it to currency.data using the generator's main method (by supplying System.in using FileStream).

In this BYR and BYN case, we have to change:

    BMD060-BND096-BOB068-BOV984-BRL986-BSD044-BTN064-BWP072-BYB112-BYR974-\

to something like:

    BMD060-BND096-BOB068-BOV984-BRL986-BSD044-BTN064-BWP072-BYB112-BYN933-BYR947-\

and if you want, the other line, which denotes when the change happened and the country:

# BELARUS
BY=BYR  

to something like:

# BELARUS
BY=BYR;2016-06-30-22-00-00;BYN  

and if the new currency doesn't have the new two decimals for minor unit, you have to specify it also at the bottom of the file.

Then, you can use the generator class to compile the binary, and swap the yourjvmfolder/jre/lib/currency.data file. Please make sure you make a backup, in case it doesn't work!

After this, you just need to patch the currency.data of your server as well. In my case, I am using AWS Elasticbeanstalk, so I just need to create an ebextension file with container command which will be executed after environment is bootstrapped but before the application is run.

container_commands:  
  01_downloadpatch:
    command: "curl https://your-website/currency.data > /tmp/currency.data"
  02_patchjava:
    command: "cp /tmp/currency.data /usr/lib/jvm/jre/lib/currency.data"

And all is good. Well, hopefully it works for you. Anyway, please remember the best solution is to not use any kind of enum, if it's not really an enum. In this case, it translates to not use java.lang.Currency if you don't have any currency logic.