Quick-Tip Thursday: Avoid Future Base64 Trouble In Java
Thu Nov 14 09:35:21 EST 2019
TL;DR: Don't use com.ibm.misc.BASE64Encoder
or com.ibm.misc.BASE64Decoder
.
If you've been doing Java development for a while, you've likely run into a situation where you need to Base64-encode or -decode something. It's used commonly in MIME documents, data URIs, and various other areas typically related to data transfer.
Unfortunately, using Base64 in Java was awkward for a long time, with no natural choice in the core JDK until Java 8. The trouble over the years, though, is that there are choices in previous releases, and it became somewhat common in the community to use sun.misc.BASE64Encoder
for this need. The problem with that is that sun.*
packages are officially not part of the JDK and can't be relied upon to exist in every Java implementation. Unfortunately, a combination of a lack of enforcement of this in the tooling and the fact that those classes are in practice pretty universal has let them creep into Java code.
On the Domino side, we have a second problem: com.ibm.misc.BASE64Encoder
.
Sidebar: The Different Flavors of Java
For the most part, setting aside Android, it's safe to think of "Java" as a monolithic entity, where having a JVM of a given version number on one system will be equivalent to the same on another. There are subtle differences, though, and several vendors have long maintained their own variants of Sun/Oracle's JVM (which is named HotSpot).
IBM has maintained one such variant, called J9, and infused all of their Java-using products, Domino included, with it. J9 has since been open-sourced and donated to Eclipse and, renamed to OpenJ9, has carved out a niche for itself by being particularly lean and speedy to spin up.
The Snag We Hit With J9
For the most part, the differences in JVM flavors don't matter to developers, but IBM played a bit of a trick on us by making duplicates of some sun.*
classes under the com.ibm.*
package. Unlike sun.*
, which at least has a little community knowledge of being internal-only (and also just looks odd compared to standard classes), com.ibm.*
has no such connotation, and there's nothing in Designer or the classes themselves to indicate that com.ibm.misc.BASE64Encoder
(internal and non-portable) is any different from, say, com.ibm.commons.util.io.base64.Base64
(portable via IBM Commons).
So, over the years, use of that class has crept into XPages and Java agent code - it does the job and it's always been safe to assume that Domino-the-IBM-product would use J9-the-IBM-JVM. Domino isn't an IBM product anymore, however, and, to add to potential future trouble, even OpenJ9 builds from AdoptOpenJDK don't come with those com.ibm.misc.*
classes.
The upshot is that, if your Java code were to run on a JVM other than an fully-IBM-style one (whether intentionally or otherwise), you're liable to run into trouble with code that casually used those JVM-internal classes.
The Options
If you're running a version of Domino using Java 8 (9.0.1FP8+), which you'd darn well better be at this point, you're in luck: the built-in java.util.Base64
class has a nice API and is guaranteed to be present on every JVM. This is your best bet, since it doesn't require any other dependencies beyond the core JDK and it has some nice features like variant encoders for MIME- and URL-safe use.
If you're targeting a version of Domino that still uses Java 6 (don't), you do have one other safe-enough option that's available in both XPages and Java agents: javax.xml.bind.DatatypeConverter
. This class is technically part of the JAX-B specification but is included in JVMs from version 6 through version 10. This is less of a clean choice than the java.util.Base64
class both because it's not as nice of an API and because use on Java 11+ will require bringing in an external dependency. Still, if you're stuck on an old release, it'll at least get you through any potential rough patches in the near future.
Finally, in XPages, you have the aforementioned com.ibm.commons.util.io.base64.Base64
class, which will continue to be present due to being included in a base library of the XPages stack, but which is rendered obsolete by java.util.Base64
.
So I recommend taking some time to look through any running Java code you have for this potential hiccup and making the switch. It's admittedly a little awkward to do, but it's still a good idea.
Richard Moy - Thu Nov 14 22:58:26 EST 2019
Why not use the Apache Commons library
Jesse Gallagher - Fri Nov 15 08:21:41 EST 2019
That'd certainly work too, and would have been preferable in the Java 6 era over using the internal classes.