Tuesday, February 24, 2009

Sending Date Objects Over RMI

Had a funny issue with java Dates today which had me tearing my hair out: I was sending a java.sql.Date from one machine to another, via RMI and then storing it into the database, but the date being stored was incorrect!

The flow of the system can be described as follows:

  • CalcServer running on host A gets a "date-string" e.g. 20090220 and converts it to a java.sql.Date object using a SimpleDateFormat("yyyyMMdd")
  • CalcServer then sends the java.sql.Date object to the ResultServer, running on host B, via RMI.
  • ResultServer stores the java.sql.Date object to a java.sql.Types.DATE column in my Oracle database.

BUT, when I query my database I see 19-FEB-2009, instead of 20-FEB-2009! What is going on?

After a lot of investigation I found out that host A and host B were in different regions; one in Frankfurt and the other in London. The CalcServer in Frankfurt converts "20090220" into a date of "Fri Feb 20 00:00:00 CET 2009". When the ResultServer in London, deserialises the date it gets "Thu Feb 19 23:00:00 GMT 2009" and hence 19-FEB-2009 is stored in the database.

I have now fixed this by sending the date-string (not the java.sql.Date object) to the ResultServer, which will then parse it into a java.sql.Date object for storage.

Here is sample code which can be used to serialise a date on one host and deserialise it on another.

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

public class SerializationTest {

  public static void main(String[] args) throws Exception {

    final String yyyyMMdd = "20090220";
    final Date date = new SimpleDateFormat("yyyyMMdd").parse(yyyyMMdd);

    if (args.length != 1) {
      System.out.println("Usage SerializationTest S|D");
    }

    boolean serialise = false;
    if (args[0].equals("S")) {
      serialise = true;
    }
    else if (args[0].equals("D")) {
      serialise = false;
    }

    String filename = "date.ser";
    if (serialise) {
      // write the object to file
      FileOutputStream fos = new FileOutputStream(filename);
      BufferedOutputStream bos = new BufferedOutputStream(fos);
      ObjectOutputStream outputStream = new ObjectOutputStream(bos);
      outputStream.writeObject(date);
      outputStream.flush();
      outputStream.close();

      System.out.println("Serialised: " + date);
    }
    else {
      FileInputStream fis = new FileInputStream(filename);
      BufferedInputStream bis = new BufferedInputStream(fis);
      ObjectInputStream inputStream = new ObjectInputStream(bis);
      Date outDate = (Date) inputStream.readObject();
      inputStream.close();

      // print the object
      System.out.println(outDate);
    }
  }
}
Link to Stack Overflow Question

No comments:

Post a Comment