Tuesday, November 2, 2010

The Mystery behind Thread (Un)Safety of Format Classes

Various Formatters like (DateFormat, MessageFormat, SimpleDateFormat, so on and so forth) are very commonly used classes in Java based applications. A very simplistic approach that I have been following for using these classes has been:

class MyFormatter {
  private static SimpleDateFormat sdf = new SimpleDateFormat();
  public static String formatDate(Date inputDate) {
     return sdf.format(inputDate);
  }
}

My reasons for using such an approach was to prevent multiple creation of expensive SimpleDateFormat objects. However, few days back I came across a post that said that the above implementation is thread un-safe. At first I was taken by surprise, and couldn't find a reason for this being un-safe, as the format operation is happening on the local variable (stored on stack).

Then I thought of opening the Java Source Code and understanding the reasons behind this being thread un-safe. The culprit was a class level variable of type Calendar, which is used by the SimpleDateFormat class. Whenever a call to format is invoked, the input date passed is set into this calender object, and then all the operations are performed on this calendar object.

So, if there are 2 threads that are sharing the same SDF object and are running concurrently, the operations will NOT be thread safe and unexpected results can occur.

To resolve this problem, one of the way can be to create a new SDF object everytime in the formatDate method. See below:

class MyFormatter {
  public static String formatDate(Date inputDate) {
    SimpleDateFormat sdf = new SimpleDateFormat();
     return sdf.format(inputDate);
  }
}

This will result into the expensive SDF object to be created on every invocation of the formatDate method, however, this is completely thread safe. The even better answer is to use the thread-safe Joda time libraries, instead.

Hoping this helps others as well!!

Thursday, March 4, 2010

Java & Axis - Calendar & Date problems

Recently, I was working on SOAP based Web-services in Java, where the web-service client was sending Calendar object as a part of the request.

I am writing this post keeping in mind that the reader has basic understanding about web-services, Java Calendar and Date Classes.

Moving further, my client was running on a machine in a different timezone and the actual web-service was deployed on a different server, running in a different timezone. The problem that I faced was, the date entered by the user, and the date that was received at the Web-service end were different.

At the web-service end, the date was 1 day less than the actual date entered by the user. Initially I thought this might be a coding issue, however, I wasn't able to find any coding flaws. Then I serialized the SOAP request to see the XML that was floating between the client and the server.

To my surprise the XML on the client side had the date as 1 day less. On further investigation, I found that, the culprit is actually the way how Java creates Date Object from Calendar Object and vice-a-versa.

We can set the timezone of the Calendar object, and then we think that if we do "getTime()" on that Calendar object, the Date object will also have the same timezone, as was for the Calendar. Unfortunately, this is not the case and this is by design.

Let's get deeper into this by example: Suppose I have a Calendar Object (with no time quotient attached to it i.e. the time is midnight), and I set the timezone of the Calendar object as "GMT". To add another assumption, let the machine be running in "CST" timezone. Now, if you would do Calendar object.getTime(), it will return you a Date Object. This Date object will NOT be in GMT timezone, rather it will be in CST timezone.

This is where the problem lies, and results in the date getting shorter by 1 day. Now, the bigger question, how to solve this problem. Java has got very good utility class by the name SimpleDateFormat. SimpleDateFormat allows you to set the timezone in which you require the Date. So, the better way is to create a SimpleDateFormat object, set the timezone on that object, and then create a Date Object using the Calendar Object.

This solved the problem @ the client side. On server side too, I had to do the same thing when retrieving the Date Object from the Calendar Object and all was done.

Hope this helps others as well.

Do feel free to post your views and suggestions.