|
Summary: Using Externalizable to boost remote space performance with JavaSpaces operations.
OverviewTo solve the performance problems associated with making a class Serializable, the serialization mechanism allows you to declare that a class is Externalizable. When the ObjectOutputStream writeObject() method is called, it performs the following sequence of actions:
Externalizable is an interface that consists of two methods: public void readExternal(ObjectInput in); public void writeExternal(ObjectOutput out); These have a role similar to the role that the readObject() and writeObject() methods have for serialization. There are, however, some very important differences: The major difference lies in how these methods are used. The serialization mechanism always writes out class descriptions of all the serializable superclasses. Also, it always writes out the information associated with the instance when viewed as an instance of each individual superclass. The Externalization mechanism writes out the identity of the class (which boils down to the name of the class and the appropriate serialVersionUID). It also stores the superclass structure and all the information about the class hierarchy. But instead of visiting each superclass and using it to store some of the state information, it simply calls writeExternal() on the local class definition. When using the Entry class with the JavaSpace API, you may implement the Externalizable mechanism. This can be done to control serialization and deserialization when the Entry is sent into the space (Write and Update Operations) and when it is sent back to the client (read and take operations). This will optimize the remote call when using Remote Space configuration for single, partitioned and replicated space topologies. In order to activate externalizable support, the IJSpace multiple API, that includes the readMultiple, writeMultiple, and takeMultiple operations, must operate with an object array that is an instance of Externalizable[], or have the first object in the array implement Externalizable, for example: public class Message implements Externalizable, Entry { ... } public void writeData() { // create the new array Externalizable[] data = new Externalizable[size]; // fill the data array for (index = 0; index < size; index++) { data[index] = new Message(...); ... } // write the data to space spaceProxy.writeMultiple(data, transaction, lease); } Otherwise, even if an Entry implements the Externalizable interface, it still operates through the regular serialization mechanism.
ExampleMessage Implements EntryHere is a simple class that implements the Entry interface: Message package com.j_spaces.examples.benchmark; import net.jini.core.entry.Entry; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.ArrayList; import java.util.Calendar; import java.util.GregorianCalendar; public class Message implements Entry{ public byte[] m_content; public Long m_counter; private static final long serialVersionUID = 1L; public ArrayList<GregorianCalendar> list; final private static String[] INDEXED_FIELDS = { "m_counter" }; public Message () { } public Message (long initVal, byte[] content) { m_content = content; m_counter = initVal; if (m_content !=null) generateData(10); } public void generateData(int capacity) { if (capacity > 0) { list = new ArrayList<GregorianCalendar>(capacity); for (int i = 0; i < capacity; i++) { int year = (int) (Math.random() * 2000); int month = (int) (Math.random() * 12); int day = (int) (Math.random() * 31); list.add(i, new GregorianCalendar(year, month, day)); } } } public void setContent(byte[] content) { this.m_content = content; generateData(10); } public void setCounter(long counter) { this.m_counter = counter; } @Override public String toString() { return getClass() + "_" + m_counter + "_" + m_content; } public static String[] __getSpaceIndexedFields() { return INDEXED_FIELDS; } } Message Implements ExternalizableHere is the same class with the Externalizable interface implemented. Message package com.j_spaces.examples.benchmark; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.ArrayList; import java.util.Calendar; import java.util.GregorianCalendar; import net.jini.core.entry.Entry; public class Message implements Externalizable,Entry{ private static final long serialVersionUID = 1L; final private static String[] INDEXED_FIELDS = { "m_counter" }; public byte[] m_content; public Long m_counter; public ArrayList<GregorianCalendar> list; public Message() { } public Message(long initVal, byte[] content) { m_content = content; m_counter = initVal; if (m_content !=null) generateData(10); } public static String[] __getSpaceIndexedFields() { return INDEXED_FIELDS; } public void writeExternal(ObjectOutput out) throws IOException { if (m_counter == null) out.writeLong(0); else out.writeLong(m_counter); if (m_content != null ) { out.writeInt(m_content.length); out.write(m_content ,0, m_content.length); } else { out.writeInt(0); } if (list != null) { out.writeBoolean(true); out.writeInt(list.size()); for (GregorianCalendar date : list) { out.writeInt(date.get(Calendar.YEAR)); out.writeInt(date.get(Calendar.MONTH)); out.writeInt(date.get(Calendar.DAY_OF_MONTH)); } } else { out.writeBoolean(false); } } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { m_counter = (Long) in.readLong(); Integer m_content_length =in.readInt(); if (m_content_length >0) { m_content = new byte[m_content_length.intValue()]; in.read(m_content, 0 , m_content_length.intValue()); } boolean islist = in.readBoolean(); if (islist ) { int capacity = (Integer) in.readInt(); list = new ArrayList<GregorianCalendar>(capacity); for (int i = 0; i < capacity; i++) { int year = in.readInt(); int month = in.readInt(); int day = in.readInt(); list.add(i, new GregorianCalendar(year, month, day)); } } } public void generateData(int capacity) { if (capacity > 0) { list = new ArrayList<GregorianCalendar>(capacity); for (int i = 0; i < capacity; i++) { int year = (int) (Math.random() * 2000); int month = (int) (Math.random() * 12); int day = (int) (Math.random() * 31); list.add(i, new GregorianCalendar(year, month, day)); } } } public void setContent(byte[] content) { this.m_content = content; generateData(10); } public void setCounter(long counter) { this.m_counter = counter; } @Override public String toString() { return getClass() + "_" + m_counter + "_" + m_content; } } Performance Tests Results
Limitations and Known Issues
|
(works on Firefox 2 and Internet Explorer 7)

