Serializing and De-Serializing entire java hierarchies to a document

This weekend I was messing around with a polling system I'm writing in the hopes of it being usable for our flying club. I was speaking with Tim Tripcony about the data structure and I told him it was "f#@%ing ugly". A Poll document with Questions as responses to the Poll, Answers as responses to the question and user responses as responses to the answers. Pretty ugly huh? Well Tim brought up the method he used for storing data in WatrCoolr using what he called the MIMEBean. This is a class where you pass it a Document, an Item name and a java object and it then serializes that java object into whatever field name you passed the method. There is then a method to deserialize that saved data back into a new instance of that object.

Using my polling application as an example. I created java classes representing the Poll which has a questions property that contains Question objects. The Question object has a property called answers which contains Answer objects. The Answer object has a property called responses which contains Response objects. So we've got a rather complex hierarchy containing all the data having to do with an individual poll. The MIMEBean allows us to serialize the Poll object which then saves that entire hierarchy. This gains us quite a few benefits, we don't have to create a bunch of views just to be able to find what we're looking for, you don't get the performance hit of doing multiple lookups and it's cool.

So how do we pull this off? Well, with Tim's MIMEBean it's much easier than having to figure it out for yourself that's for sure. I asked Tim if it was alright if I blogged this as I didn't want to "steal his thunder" since it was his technique. He informed me "steal away" so here we are. I'm going to lay this out for you using a very simple application, not the polling system as that will probably show up on OpenNTF sooner or later and it's a lot of code, we need something simple that shows the technique in a digestible way.

For the demo I'm using a very simple application for tracking assets (in a very loose definition of asset tracking).  It'll be an User object and an Asset object each with only a couple of properties. The User object has "name", "unid" and "assets". The Asset object has "name" only. There will be 1 form with 1 rich text field called UserData where we'll store the serialized data. In order for this to work our User and Asset classes need to implement java.io.Serializable, this is very important as this technique will not work unless you do this.

To start with here is the MIMEBean class:

package com.keithstric.demo.utils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

import lotus.domino.Database;
import lotus.domino.Document;
import lotus.domino.MIMEEntity;
import lotus.domino.Session;
import lotus.domino.Stream;

import com.ibm.xsp.extlib.util.ExtLibUtil;

/**
 * This is the MIMEBean from the OpenNTF project titled WatrCoolr. Much of this
 * class has been removed as it is not used. The methods here have not been
 * modified in any way.
 * 
 * @author Tim Tripcony
 * 
 */

public class MIMEBean {

	public static Serializable restoreState(Document doc, String itemName) {
		Serializable result = null;
		try {
			Session session = ExtLibUtil.getCurrentSession();
			Stream mimeStream = session.createStream();
			MIMEEntity entity = doc.getMIMEEntity(itemName);
			entity.getContentAsBytes(mimeStream);
			ByteArrayOutputStream streamOut = new ByteArrayOutputStream();
			mimeStream.getContents(streamOut);
			byte[] stateBytes = streamOut.toByteArray();
			ByteArrayInputStream byteStream = new ByteArrayInputStream(stateBytes);
			ObjectInputStream objectStream = new ObjectInputStream(byteStream);
			Serializable restored = (Serializable) objectStream.readObject();
			result = restored;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	public static void saveState(Serializable object, Document doc, String itemName) {
		try {
			String unid = doc.getUniversalID();
			Database docParent = doc.getParentDatabase();
			String server = docParent.getServer();
			String filePath = docParent.getFilePath();
			saveStateAsSigner(object, server, filePath, unid, itemName);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void saveStateAsSigner(Serializable object, String server, String filePath, String unid, String itemName) {
		try {
			Session signerSession = ExtLibUtil.getCurrentSessionAsSigner();
			signerSession.setConvertMIME(false);
			Database database = signerSession.getDatabase(server, filePath);
			Document doc = database.getDocumentByUNID(unid);
			ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
			ObjectOutputStream objectStream = new ObjectOutputStream(byteStream);
			objectStream.writeObject(object);
			objectStream.flush();
			objectStream.close();
			Stream mimeStream = signerSession.createStream();
			MIMEEntity entity = null;
			MIMEEntity previousState = doc.getMIMEEntity(itemName);
			if (previousState == null) {
				// Do Nothing
			} else {
				previousState.remove();
				doc.save();
				doc.recycle();
				doc = database.getDocumentByUNID(unid);
			}
			entity = doc.createMIMEEntity(itemName);
			ByteArrayInputStream byteIn = new ByteArrayInputStream(byteStream.toByteArray());
			mimeStream.setContents(byteIn);
			entity.setContentFromBytes(mimeStream, "text/plain", MIMEEntity.ENC_QUOTED_PRINTABLE);
			doc.save();
			doc.recycle();
			database.recycle();
			signerSession.setConvertMIME(true);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}

The methods here we're concerned with are saveState and restoreState. The saveState method accepts the object to be serialized, a document and an item name to save the serialized data in. The saveStateAsSigner method then takes care of the rest of the serialization process. The restoreState method accepts a document and an item name where the serialized data is saved and then deserializes this data back into a java object.

Share This: