The following sections are included in this chapter:
Serialization is the process of encoding an object into a binary format. It is a critical component to working with Coherence as data must be moved around the network. The Portable Object Format (also referred to as POF) is a language agnostic binary format. POF was designed to be incredibly efficient in both space and time and has become a cornerstone element in working with Coherence. For more information on the POF binary stream, see "The PIF-POF Binary Format" in Oracle Coherence Developer's Guide. See "Using the Portable Object Format" for details on using the POF API.
There are several options available with serialization including standard Java serialization, POF, and your own custom serialization routines. Each has their own trade-offs. Standard Java serialization is easy to implement, supports cyclic object graphs and preserves object identity. Unfortunately, it's also comparatively slow, has a verbose binary format, and restricted to only Java objects.
The Portable Object Format has the following advantages:
It's language independent with current support for Java, .NET, and C++.
It's very efficient, in a simple test class with a String
, a long
, and three ints
, deserialization and serialization was seven times faster, and the binary produced was one sixth the size compared with standard Java serialization.
It's versionable, objects can evolve and have forward and backward compatibility.
It supports the ability to externalize your serialization logic.
It's indexed which allows for extracting values without deserializing the whole object.
POF requires you to implement serialization routines that know how to serialize and deserialize your objects. There are two ways to do this:
Have your objects implement the com.tangosol.io.pof.PortableObject
interface.
Implement a serializer for your objects using the com.tangosol.io.pof.PofSerializer
interface.
The PortableObject
interface is a simple interface made up of two methods:
public void readExternal(PofReader reader)
public void writeExternal(PofWriter writer)
As mentioned above, POF elements are indexed. This is accomplished by providing a numeric index for each element that you write or read from the POF stream. It's important to keep in mind that the indexes must be unique to each element written and read from the POF stream, especially when you have derived types involved because the indexes must be unique between the super class and the derived class.
Example 16-1 is a simple example of implementing the PortableObject
interface:
Example 16-1 Implementation of the PortableObject Interface
public void readExternal(PofReader in) throws IOException { m_symbol = (Symbol) in.readObject(0); m_ldtPlaced = in.readLong(1); m_fClosed = in.readBoolean(2); } public void writeExternal(PofWriter out) throws IOException { out.writeObject(0, m_symbol); out.writeLong(1, m_ldtPlaced); out.writeBoolean(2, m_fClosed); }
The PofSerializer
interface provide you with a way to externalize your serialization logic from the classes you want to serialize. This is particularly useful when you do not want to change the structure of your classes to work with POF and Coherence. The PofSerializer
interface is also made up of two methods:
public Object deserializer(PofReader in)
public void writeObject(PofWriter out, Object o)
As with the PortableObject
interface, all elements written to or read from the POF stream must be uniquely indexed. Below is an example implementation of the PofSerializer
interface:
Example 16-2 Implementation of the PofSerializer Interface
public Object deserialize(PofReader in) throws IOException { Symbol symbol = (Symbol)in.readObject(0); long ldtPlaced = in.readLong(1); bool fClosed = in.readBoolean(2); // mark that the read is complete in.readRemainder(null); return new Trade(symbol, ldtPlaced, fClosed); } public void serialize(PofWriter out, Object o) throws IOException { Trade trade = (Trade) o; out.writeObject(0, trade.getSymbol()); out.writeLong(1, trade.getTimePlaced()); out.writeBoolean(2, trade.isClosed()); // mark that write is complete out.writeRemainder(null); }
When assigning POF indexes to your object's attributes, it's important to keep a few things in mind:
Order your reads and writes: you should start with the lowest index value in your serialization routine and finish with the highest. When deserializing a value, you want to read in the same order you've written.
It's ok to have non-contiguous indexes but you must read/write sequentially.
When Subclassing reserve index ranges: index's are cumulative across derived types. As such, each derived type must be aware of the POF index range reserved by its super class.
Do not re-purpose your indexes: if you ever want Evolvable support, it's imperative that you don't re-purpose the indexes of your attributes across class revisions.
Label your indexes: if you label your indexes with a public static final int
, it is much easier to work with the object, especially when using PofExtractors and PofUpdaters. After the indexes are labeled, ensure that they are read and written out in order as mentioned above.
Coherence provides a ConfigurablePofContext
class which is responsible for mapping a POF serialized object to an appropriate serialization routine (either a PofSerializer or by calling through the PortableObject
interface). Each class is given a unique type-id in POF that can be mapped to an optional PofSerializer. After the classes have serialization routines, they must be registered with the ConfigurablePofContext
. Custom user types are registered with the ConfigurablePofContext
using a POF configuration file. This is an XML file which has a <user-type-list>
element that contains a list of classes that implement PortableObject
or have a PofSerializer associated with them. The type-id for each class must be unique, and must match across all cluster instances (including extend clients).
The following is an example of what a pof-config.xml
file would look like:
<?xml version='1.0'?> <pof-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.oracle.com/coherence/coherence-pof-config" xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-pof-config coherence-pof-config.xsd"> <user-type-list> <include>coherence-pof-config.xml</include> <!-- User types must be above 1000 --> <user-type> <type-id>1001</type-id> <class-name>com.examples.MyTrade</class-name> <serializer> <class-name>com.examples.MyTradeSerializer</class-name> </serializer> </user-type> <user-type> <type-id>1002</type-id> <class-name>com.examples.MyPortableTrade</class-name> </user-type> </user-type-list> </pof-config>
Note:
Coherence reserves the first 1000 type-id's for internal use. If you look closely you'll see that the user-type-list includes thecoherence-pof-config.xml
file. This is where the Coherence specific user types are defined and should be included in all of your POF configuration files.In order to start using POF, you must also configure each service to use the ConfigurablePofContext
. This is accomplished using the <serializer>
element of your cache scheme in your cache configuration file. The ConfigurablePofContext
takes a string based <init-param>
that points to your pof-configuration file.
Below is an example of a distributed cache scheme configured to use POF:
<distributed-scheme> <scheme-name>example-distributed</scheme-name> <service-name>DistributedCache</service-name> <serializer> <instance> <class-name>com.tangosol.io.pof.ConfigurablePofContext</class-name> <init-params> <init-param> <param-type>String</param-type> <param-value>my-pof-config.xml</param-value> </init-param> </init-params> </instance> </serializer> </distributed-scheme>
Alternatively you can configure an entire JVM instance to use POF using the following system properties:
tangosol.pof.enabled=true
- This turns on POF for the entire JVM instance.
tangosol.pof.config=
CONFIG_FILE_PATH
- The path to the POF configuration file you want to use. Note that if this is not in the class path it must be presented as a file resource (for example file:///opt/home/coherence/mycustom-pof-config.xml
).
Using POF has many advantages ranging from performance benefits to language independence. It's recommended that you look closely at POF as your serialization solution when working with Coherence. For information on how to work with POF in C++, see "Building Integration Objects for C++ Clients" in Oracle Coherence Client Guide. For information on how to work with POF in .NET, see "Building Integration Objects for .NET Clients" in Oracle Coherence Client Guide.