Thursday 15 August 2013

Thread safe Java DateFormat

Not that the JDK documentation doesn't say it clearly: "Date formats are not synchronized. It is recommended to create separate format instances for each thread. If multiple threads access a format concurrently, it must be synchronized externally."
Nevertheless there are still programmers who seem to forget this fact when using date formats in their web applications, and you can count me among this sloppy bunch - so very expected from a Oracle Certified Java Professional!

It is common practice to declare utility objects as public class variables or even constants so that they can be used globally in our application, but we can't do that with Date formats if our application is multi-threaded, as most enterprise applications are. Only three options left:
  • Create an instance of DateFormat every time we need it, even if we know that instantiation is an expensive process.
  • Synchronize our calls to DateFormat methods.
  • Use ThreadLocal if your application server uses a thread pool.
There's a good experiment about these three methods in this article. It's worth reading.

But there's one more way of getting out of this trap: use FastDateFormat from apache commons-lang library. It is, as its name suggest, fast and - more important - thread safe, so we can use it just the way we want to use this kind of classes: instantiate it once and use it everywhere.
Well, Alfresco - my favorite playground - happens to include commons-lang.jar in its distribution, so I'm going to show an example of how to use FastDateFormat in Alfresco.

First we define a few date format java beans:
<bean id="dateFormat1" class="org.apache.commons.lang.time.FastDateFormat"
  factory-method="getInstance">
 <constructor-arg value="dd/MM/yyyy"/>
</bean>

<bean id="dateFormat2" class="org.apache.commons.lang.time.FastDateFormat"
  factory-method="getInstance">
 <constructor-arg value="dd/MM/yy"/>
</bean>
We take advantage of the static createInstance method and use it as bean factory method.

We could inject directly one of these date formats into any bean that requires it, or we can bundle them into a factory bean, as below:
<util:map id="dateFormats">
 <entry key="dd/MM/yyyy" value-ref="dateFormat1" />
 <entry key="dd/MM/yy" value-ref="dateFormat2" />
</util:map>

<bean id="dateFormatFactory"
  class="ie.paco.alfresco.util.impl.DateFormatFactory">
 <property name="formats" ref="dateFormats" />
</bean>
DateFormatFactory encapsulates a Map of DateFormats, any of which may be retrieved by invoking getFormat(String key):
package ie.paco.alfresco.util;
import java.text.Format;

public interface FormatFactory {
 public Format getFormat(String key);
}
package ie.paco.alfresco.util.impl;
import java.text.Format;
import java.util.Map;
import ie.paco.alfresco.util.FormatFactory;

public class DateFormatFactory implements FormatFactory {
 
 private Map formats;

 @Override
 public Format getFormat(String key) {
  return formats.get(key);
 }

 public Map getFormats() {
  return formats;
 }

 public void setFormats(Map formats) {
  this.formats = formats;
 }

}
And last, make some client code for our new date formats, for example a PropertyDecorator:
...
public class DatePropertyDecorator extends BasePropertyDecorator {
 
 private FormatFactory formatFactory;

 @SuppressWarnings("unchecked")
 @Override
 public JSONAware decorate(QName propertyName, NodeRef nodeRef, Serializable value) {
        JSONObject map = new JSONObject();
        Format dateFormat = formatFactory.getFormat("dd/MM/yy");
        String formatted = dateFormat.format(value);
        map.put("raw", value.toString());
        map.put("formatted", formatted);
  return map;
 }

 public FormatFactory getFormatFactory() {
  return formatFactory;
 }

 public void setFormatFactory(FormatFactory formatFactory) {
  this.formatFactory = formatFactory;
 }
}
This property decorator is meant to be used on date properties. It will output both the raw String value of the date and its formatted value. Its bean definition follows
<bean id="datePropertiesDecorator" parent="baseDecorator" class="ie.paco.alfresco.repo.jscript.app.DatePropertyDecorator">
 <property name="formatFactory" ref="dateFormatFactory"/>
 <property name="propertyNames">
  <set>
   <value>cm:created</value>
   <value>cm:modified</value>
   <value>cm:accessed</value>
  </set>
 </property>
</bean>

The decorator will modify the output of the properties cm:created, cm:modifed and cm:accessed. So when we go to the document library the doclist webscript will return something like this:


We just need a Share extention to exploit this decorator. But that will be the subject of another post.
And remember; when it comes to formatting dates, FastDateFormat is your man.

You can get the code of this tutorial from here.

The Free Verse

This section has nothing to do with technology.

Just a reading recommendation. Two classics from the twentieth century:

Harper Lee. To Kill a Mockingbird

and

Friedrich Hayek. The Road to Serfdom

No comments:

Post a Comment