Tuesday, March 29, 2011

Replacing sun.misc.Base64Encoder and sun.misc.Base64Decoder with alternatives from Apache Commons Codec API

Today at work I had to replace sun.misc.Base64Encoder and sun.misc.Base64Decoder implementations of base64 decoding and encoding, since these packages may not be available in every JRE.

A bit of theory: Classes in JRE/JDK are in packages java.*, javax.*, org.* and sun.*. All but the sun.* packages are a standard part of the Java platform and will be supported into the future. Packages that are in sun.* can be different from OS to OS, and can change without notice.  Thus the programs written using classes from these packages may not work in future releases of Java. Thus they should NOT be used by developers.

There are also packages that start with com.sun.*. As explained in blog entry from Sun about their built-in Httpserver:
You are probably wondering already about the package names, and why they are com.sun.httpserver rather than something like java.net.httpserver. What this distinction means, is that the API and implementation are a fully supported, publicly accessible component of Sun's implementation of Java SE 6. It does mean however, that the packages are not formally part of the Java SE platform, and are therefore not guaranteed to be available on all other (non Sun) implementations of Java SE 6.
Thus we must replace Sun's base64 implementation with the one from Apache Commons Codec library (or any other one of your choice), and here is how I did it.

1. Getting Apache Commons Codec into Maven
Open pom.xml file in Eclipse (with m2eclipse plugin installed), and switch to "Dependencies" tab.Click "Add..." button, and search for "codec". You can also optionally select "Include Javadoc" and "Include Sources" to have javadocs and sources for the package. I would recommend doing so, since you are highly likely to want to see the sources and/or javadocs for method documentations. Find commons-coded from results, and select version 1.4, that is commons-codec-1.4.jar. Click Ok, and you will have this package installed in your classpath.

2. Replacing encoder
Replace all BASE64Encoder instances with Base64. You can then call encode() method of that instance to replace encodeBuffer, or alternatively you can use static methods from Base64 class.

3. Replacing decoder
The steps are almost same as above, since Apache Commons Codec library uses the same class for both encoding and decoding. You just have to use decode() instead of encode() or decodeBase64() instead of encodeBase64().

Done! Another thing you would want to pay attention to is character encoding. Converting to String from bytes, Apache Commons Codec by default uses UTF-8 encoding, so you may encounter problems if your setup's default charset is not UTF-8. Workaround would be using only byte accepting and returning methods from Base64 class for base64 calculations, and using new String(bytes[] bytes) constructor and String.getBytes() method for "setting" (better to say "constructing", since String is immutable) and getting bytes respectively.

Happy coding!

Resources:
http://java.sun.com/products/jdk/faq/faq-sun-packages.html - Explanation why developers should not use sun.* packages.
http://blogs.sun.com/michaelmcm/entry/http_server_api_in_java - A blog entry from Sun about their built-in HttpServer

4 comments:

  1. Now there's a 1 line replacement that doesn't need any dependencies:
    For the Base64Encoder.encode() it's the static
    javax.xml.bind.DataTypeConverter.printBase64Binary()
    and for decode there's parseBase64Binary()

    ReplyDelete
    Replies
    1. I'm getting a warning using the the DatatypeConverter, it's the same as using the com.sung.* package:
      Access restriction: The method printBase64Binary(byte[]) from the type DatatypeConverter is not accessible due to restriction on required library /Library/Java/JavaVirtualMachines/1.6.0_37-b06-434.jdk/Contents/Classes/classes.jar

      Best option to go with Apache commons

      Delete
  2. not work for me. It isnt the same encoding.
    I use:
    new org.apache.commons.codec.binary.Base64().encodeAsString("TXT");

    ReplyDelete
  3. Thank you, this is very helpful indeed

    ReplyDelete