Monday, December 13, 2010

Lightweight Persistence with Java Serialization

The Java SE 6 documentation on Object Serialization states the following about the uses of Java serialization:
Serialization is used for lightweight persistence and for communication via sockets or Java Remote Method Invocation (Java RMI).
The latter uses of Java serialization, for transmission of object data via sockets of RMI, are common everyday uses of Java serialization. However, I sometimes feel that the use of Java serialization for "lightweight persistence" may not be used as often as it might be.

When I first started using Java in the late mid-1990s, I used serialization quite a bit. The advent of web services and other mechanisms that are platform-independent, operating system independent, and programming language independent, coupled with the rise of XML and various sorts of databases, seemed to make it less common to apply Java serialization for persistence. However, although it might not be the first thing to come to mind any longer, Java serialization can be a compelling lightweight persistence solution in the right circumstances.

In this blog post, I look at some simple examples of lightweight persistence with Java serialization.

The first code listing is a simple example of persisting data to a serialized file using Java's standard serialization. The generated file is called dustin.out.

LightweightPersister.java
package dustin.examples;

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.Arrays;
import java.util.List;

import static java.lang.System.out;

public class LightweightPersister
{
   public static void main(final String[] arguments)
   {
      if (arguments.length < 1)
      {
         out.println("Enter Strings separated by spaces to be serialized.");
      }

      final List<String> strings = Arrays.asList(arguments);

      // Serialize the List
      try
      {
         final FileOutputStream fo = new FileOutputStream("dustin.out");
         final ObjectOutputStream oos = new ObjectOutputStream(fo);
         oos.writeObject(strings);
         oos.flush();
         oos.close();
      }
      catch (Exception ex)
      {
         // write stack trace to standard error
         ex.printStackTrace();
      }
   }
}

The following screen image shows the use of the above code and the resultant generated serialization file.


It is not difficult to build a reader that reads this serialized file into the appropriate Java data structures. Code for doing that is shown next.

LightweightReader.java
package dustin.examples;

import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.Arrays;
import java.util.List;

import static java.lang.System.out;

public class LightweightReader
{
   public static void main(final String[] arguments)
   {
      List<String> strings = null;

      // Deserialize what was assumed to be written by LightweightPersister
      try
      {
         final FileInputStream fis = new FileInputStream("dustin.out");
         final ObjectInputStream ois = new ObjectInputStream(fis);  
         final Object deserializedObject = ois.readObject();
         out.println("Object Type to deserialize " + deserializedObject.getClass().getName());
         if (deserializedObject instanceof List)
         {
            strings = (List<String>) deserializedObject;
         }
         else
         {
            out.println("Not expected to deserialize " + deserializedObject.getClass().getName());
         }
         ois.close();

         if (strings != null)
         {
            out.println("The persisted Strings are: " + strings);
         }
      }
      catch (Exception ex)
      {
         // Print stack trace to standard error for simple demonstration
         ex.printStackTrace();
      }
   }
}

The simple code above will read the serialized file and this is demonstrated in the next screen snapshot.


This image and the preceding code listings demonstrate that Java serialization can be an effective method for lightweight persistence.

Serialization is not limited to standard Java SDK classes. It can also be used with custom classes. This is demonstrated in the Java SE 6 documentation Serialization Examples.  Using Serialization with a Custom Data Format is an especially useful example to consider.

Before ending this post, I cannot help but point out how Groovy makes an already easy task in Java even easier. Here are the lines of Groovy needed to read the contents of the serialized file.

def file = new File("dustin.out")
def serializedStrings = []
file.eachObject {serializedStrings << it}
println "The persisted Strings are: ${serializedStrings}"

That's it; four lines! Does it work? That is demonstrated in the next screen snapshot.


It works! Groovy makes reading the serialized file even easier than the corresponding Java code.

The serialization mechanism demonstrated in this post was easy to implement on the writer and reader sides because I used standard Java classes that are inherently Serializable and their serialization is know to be done correctly and consistently. Things can get a little trickier with custom serialization as evidenced by the fact that Josh Bloch devotes an entire chapter of Effective Java to covering serialization in five items.

Besides Effective Java's treatment on serialization, other tremendous resources on Java serialization (that are all also available online) are Java SE 6 Object Serialization (referenced multiple times in this post), 5 Things You Didn't Know About ... Java Object Serialization (first article in the excellent series and covers signing and sealing serialized data), Java Serialization Algorithm Revealed, and, of course, the Java (SE 6) Object Serialization Specification (1.4 PDF).

Java object serialization can be an effective tool for straightforward lightweight persistence when both the application/service saving the data and the applications/services reading the data are all written in Java. This mechanism can be easier than dealing with XML writing/parsing, database manipulation, or object-relational mapping for simpler and lighter persistence needs.

No comments: