One reader commented that the Cachetable I presented in last month's column violates the Liskov Substitution Principle (see Resources). This principle basically states that if B is a subclass of A, and C is a subclass of A, then B should be able to subclass
C without any problems, and vice versa. In other words, any class that currently extends Hashtable should be able to also extend Cachetable without any side-effects. It wasn't my original intention to support this notion, but the reader is correct in asserting
that it is good object-oriented design.
A few programming veterans immediately noticed that the ping() method has a glaring flaw: it's synchronized! This means that the cache is locked during house cleaning, which leads to performance issues if you have a rather large
collection. This issue can be resolved by incorporating reader/writer locks, but that topic is out of the scope of this series.
If you're interested, you can read more about reader/writer locks in Part 7 of Java Toolbox columnist Allen Holub's series on thread issues.
One reader pointed out that the get() method has a potential for NullPointerExceptions. The return value from super.get() isn't checked, and could therefore be null if a bad key is passed in. We can avoid this with a simple change:
public synchronized Object get( Object key ) {
Object o = super.get( key );
if( o != null )
{
return( ( (TimeWrapper) o ).getObject() );
}
else
{
return null;
}
}
This month's tool allows you to share objects between different virtual machines. It also allows you to store an object, have a process die, and then be able to retrieve that object once the process restarts. All of this is achievable without the complexity of database mapping or remote object architectures (like CORBA and EJB). The key concepts for this tool are serialization, persistence, and JNDI. Serialization allows you to "package" an object instance for storage and transfer. Persistence is the storage part that requires the serialization. And JNDI is Sun's standard API for interfacing with directory and naming servers. I will discuss each concept briefly, then dive into the tool itself.
Serialization is the technique by which an object can store and restore its state, usually to and from a stream of bytes. When you serialize an object, you're basically breaking it down into its most primitive values (integers, booleans, and strings). These primitive values must be managed in a predetermined format and order so that you can deserialize the object, thus restoring the object to its original state.
Support for serialization allows an object to be persistent. In the simplest terms, this means that an object can survive from one program instance to another. For example, let's say you start up a Java program with some sort of widget. You modify the widget in some manner -- changing its color, for example. You stop the application. Then, the next time you run the application, the widget is magically the same color as when you last stopped the application. This is accomplished by simply serializing the widget to a file when the application stops, and deserializing the widget from the same file when the application starts. The file will be very small, because it only contains the integer value representing the color of the widget.
Hashtable interface