Java Serialization & final class attributes

Today I had to face a problem with Java Serialization and here is the report of what I’ve achieved.
The SmartWeb BusinessObject class defines a protected attribute named logger carrying the logger for subclasses. The BusinessObject class implements Serializable thus it needs to define the logger attribute as transient because Commons Logging loggers are non serializable.

The problem arises whenever you deserialize a BusinessObject subclass because the logger attribute will not be deserialized (it has not be serialized at all!) and this makes all your logging statements producing NullPointerExceptions! BTW, those errors are very difficult to understand for two reasons:

  1. you always consider that attribute valid and you will hardly consider tha logger attribute to be null
  2. every logging statement you try to add to your code to understand what’s going wrong will fail on it’s own

Well, the solution to the problem is re initialize the logger attribute upon object deserialization implementing a custom readObject method as stated in the Serializable interface documentation:

private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException;

The preceeding code is not going to work in my specific case because the logger attribute has been declared as final to avoid unwanted replacements and potential errors. The first option I took in consideration was “ok, I’ve no exit, let’s make that attribute non final” but the idea was suddenly replaced by “but standard Java Serialization is normally able to deserialize final fields… how?” and I googled and digged a little bit into the problem ending to the following solution:

<br />	/**<br />	 * Custom deserialization. We need to re-initialize a logger instance as loggers<br />	 * can't be serialized.<br />	 */<br />	private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {<br />		try {<br />			Class type = BusinessObject.class;<br />			// use getDeclaredField as the field is non public<br />			Field logger = type.getDeclaredField("logger");<br />			// make the field non final<br />			logger.setAccessible(true);<br />			logger.set(this, LogFactory.getLog(type));<br />			// make the field final again<br />			logger.setAccessible(false);<br />		} catch (Exception e) {<br />			LogFactory.getLog(this.getClass())<br />				.warn("unable to recover the logger after deserialization: logging statements will cause null pointer exceptions", e);<br />		}<br />		in.defaultReadObject();<br />	}<br />
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s