26 Using Portable Object Format
For information on how to work with POF when building .NET and C++ extend clients, see Building Integration Objects for .NET Clients and Building Integration Objects for C++ Clients in Developing Remote Clients for Oracle Coherence..
This chapter includes the following sections:
- Overview of POF Serialization
POF is a language agnostic binary format. POF is efficient in both space and time and is a cornerstone technology in Coherence. - Using the POF API to Serialize Objects
POF requires serialization routines that know how to serialize and deserialize an object. - Using POF Annotations to Serialize Objects
POF annotations provide an automated way to implement the serialization and deserialization routines for an object. - Using POF Extractors and POF Updaters
- Serializing Keys Using POF
Key objects, like value objects, can be serialized using POF.
Parent topic: Performing Data Grid Operations
Overview of POF Serialization
There are several options available for 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 is restricted to only Java objects.
POF 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
, along
, and threeints
, (de)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 to allow for extracting values without deserializing the whole object. See Using POF Extractors and POF Updaters.
Parent topic: Using Portable Object Format
Using the POF API to Serialize Objects
com.tangosol.io.pof.PortableObject
interface and the com.tangosol.io.pof.PofSerializer
interface. POF also supports annotations that automatically implement serialization with out having to implement the PortableObject
or PofSerializer
interfaces. See Using POF Annotations to Serialize Objects.
This section includes the following topics:
- Implementing the PortableObject Interface
- Implementing the PofSerializer Interface
- Guidelines for Assigning POF Indexes
- Using POF Object References
- Registering POF Objects
- Configuring Coherence to Use the ConfigurablePofContext Class
Parent topic: Using Portable Object Format
Implementing the PortableObject Interface
The PortableObject
interface is an interface made up of two methods:
-
public void readExternal(PofReader reader)
-
public void writeExternal(PofWriter writer)
POF elements are indexed by providing a numeric value 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. The following example demonstrates implementing 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); }
Parent topic: Using the POF API to Serialize Objects
Implementing the PofSerializer Interface
The PofSerializer
interface provides a way to externalize the 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 deserialize(PofReader in)
-
public void serialize(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:
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 reading the object is done in.readRemainder(); 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 writing the object is done out.writeRemainder(null); }
Parent topic: Using the POF API to Serialize Objects
Guidelines for Assigning POF Indexes
Use the following guidelines when assigning POF indexes to an object's attributes:
-
Order your reads and writes: start with the lowest index value in the serialization routine and finish with the highest. When deserializing a value, perform reads in the same order as writes.
-
Non-contiguous indexes are acceptable but must be read/written 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 indexes: to support Evolvable, it's imperative that indexes of attributes are not re-purposed across class revisions.
-
Label indexes: indexes that are labeled with a
public static final int
, are much easier to work with, especially when using POF Extractors and POF Updaters. See Using POF Extractors and POF Updaters. Indexes that are labeled must still be read and written out in the same order as mentioned above.
Parent topic: Using the POF API to Serialize Objects
Using POF Object References
This section includes the following topics:
- Overview of Using POF Object References
- Enabling POF Object References
- Registering POF Object Identities for Circular and Nested Objects
Parent topic: Using the POF API to Serialize Objects
Overview of Using POF Object References
POF supports the use of object identities and references for objects that occur more than once in a POF stream. Objects are labeled with an identity and subsequent instances of a labeled object within the same POF stream are referenced by its identity.
Using references avoids encoding the same object multiple times and helps reduce the data size. References are typically used when a large number of sizeable objects are created multiple times or when objects use nested or circular data structures. However, for applications that contain large amounts of data but only few repeats, the use of object references provides minimal benefits due to the overhead incurred in keeping track of object identities and references.
The use of object identity and references has the following limitations:
-
Object references are only supported for user defined object types.
-
Object references are not supported for
Evolvable
objects. -
Object references are not supported for keys.
-
Objects that have been written out with a POF context that does not support references cannot be read by a POF context that supports references. The opposite is also true.
-
POF objects that use object identity and references cannot be queried using POF extractors. Instead, use the
ValueExtractor
API to query object values or disable object references. -
The use of the
PofNavigator
andPofValue
API has the following restrictions when using object references:-
Only read operations are allowed. Write operations result in an
UnsupportedOperationException
. -
User objects can be accessed in non-uniform collections but not in uniform collections.
-
For read operations, if an object appears in the data stream multiple times, then the object must be read where it first appears before it can be read in the subsequent part of the data. Otherwise, an
IOException: missing identity:
<ID>
may be thrown. For example, if there are 3 lists that all contain the same person object,p
. Thep
object must be read in the first list before it can be read in the second or third list.
-
Parent topic: Using POF Object References
Enabling POF Object References
Object references are not enabled by default and must be enabled either within a pof-config.xml
configuration file or programmatically when using the SimplePofContext
class.
To enable object references in the POF configuration file, include the <enable-references>
element, within the <pof-config>
element, and set the value to true
. For example:
<?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"> ... <enable-references>true</enable-references> </pof-config>
To enable object references when using the SimplePofContext
class, call the setReferenceEnabled
method with a property set to true
. For example:
SimplePofContext ctx = new SimplePofContext(); ctx.setReferenceEnabled(true);
Parent topic: Using POF Object References
Registering POF Object Identities for Circular and Nested Objects
Circular or nested objects must manually register an identity when creating the object. Otherwise, a child object that references the parent object will not find the identity of the parent in the reference map. Object identities can be registered from a serializer during the deserialization routine using the com.tangosol.io.pof.PofReader.registerIdentity
method.
The following examples demonstrate two objects (Customer
and Product
) that contain a circular reference and a serializer implementation that registers an identity on the Customer
object.
The Customer
object is defined as follows:
public class Customer { private String m_sName; private Product m_product; public Customer(String sName) { m_sName = sName; } public Customer(String sName, Product product) { m_sName = sName; m_product = product; } public String getName() { return m_sName; } public Product getProduct() { return m_product; } public void setProduct(Product product) { m_product = product; } }
The Product
object is defined as follows:
public class Product { private Customer m_customer; public Product(Customer customer) { m_customer = customer; } public Customer getCustomer() { return m_customer; } }
The serializer implementation registers an identity during deserialization and is defined as follows:
public class CustomerSerializer implements PofSerializer
{
@Override
public void serialize(PofWriter pofWriter, Object o) throws IOException
{
Customer customer = (Customer) o;
pofWriter.writeString(0, customer.getName());
pofWriter.writeObject(1, customer.getProduct());
pofWriter.writeRemainder(null);
}
@Override
public Object deserialize(PofReader pofReader) throws IOException
{
String sName = pofReader.readString(0);
Customer customer = new Customer(sName);
pofReader.registerIdentity(customer);
customer.setProduct((Product) pofReader.readObject(1));
pofReader.readRemainder();
return customer;
}
}
Parent topic: Using POF Object References
Registering POF Objects
Coherence provides the com.tangosol.io.pof.ConfigurablePofContext
serializer class which is responsible for mapping a POF serialized object to an appropriate serialization routine (either a PofSerializer
implementation or by calling through the PortableObject
interface).
Once your classes have serialization routines, the classes are registered with the ConfigurablePofContext
class using a pof-config.xml
configuration file. The POF configuration file 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). See POF User Type Configuration Elements.
The following is an example of a POF configuration file:
<?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. As shown in the above example, the <user-type-list>
includes the coherence-pof-config.xml
file that is located in the root of the coherence.jar
file. This is where Coherence specific user types are defined and should be included in all of your POF configuration files.
Parent topic: Using the POF API to Serialize Objects
Configuring Coherence to Use the ConfigurablePofContext Class
This section includes the following topics:
- Overview of Using the ConfigurablePofContext Class
- Configuring the ConfigurablePofContext Class Per Service
- Configuring the ConfigurablePofContext Class for All Services
- Configuring the ConfigurablePofContext Class for a JVM Instance
Parent topic: Using the POF API to Serialize Objects
Overview of Using the ConfigurablePofContext Class
Coherence can be configured to use the ConfigurablePofContext
serializer class in three different ways based on the level of granularity that is required:
-
Per Service – Each service provides a full
ConfigurablePofContext
serializer class configuration or references a predefined configuration that is included in the operational configuration file. -
All Services – All services use a global
ConfigurablePofContext
serializer class configuration. Services that provide their own configuration override the global configuration. The global configuration can also be a full configuration or reference a predefined configuration that is included in the operational configuration file. -
JVM – The
ConfigurablePofContext
serializer class is enabled for the whole JVM.
Configuring the ConfigurablePofContext Class Per Service
To configure a service to use the ConfigurablePofContext
class, add a <serializer>
element to a cache scheme in a cache configuration file. See serializer.
The following example demonstrates a distributed cache that is configured to use the ConfigurablePofContext
class and defines a custom POF configuration file:
<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>
The following example references the default definition in the operational configuration file. See serializer.
<distributed-scheme> <scheme-name>example-distributed</scheme-name> <service-name>DistributedCache</service-name> <serializer>pof</serializer> </distributed-scheme>
Configuring the ConfigurablePofContext Class for All Services
To globally configure the ConfigurablePofContext
class for all services, add a <serializer>
element within the <defaults>
element in a cache configuration file. Both of the below examples globally configure a serializer for all cache scheme definitions and do not require any additional configuration within individual cache scheme definitions. See defaults.
The following example demonstrates a global configuration for the ConfigurablePofContext
class and defines a custom POF configuration file:
<?xml version='1.0'?> <cache-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.oracle.com/coherence/coherence-cache-config" xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-cache-config coherence-cache-config.xsd"> <defaults> <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> </defaults> ...
The following example references the default definition in the operational configuration file. See serializer.
<?xml version='1.0'?> <cache-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.oracle.com/coherence/coherence-cache-config" xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-cache-config coherence-cache-config.xsd"> <defaults> <serializer>pof</serializer> </defaults> ...
Configuring the ConfigurablePofContext Class for a JVM Instance
You can configure an entire JVM instance to use POF, using the following system properties:
-
coherence.pof.enabled=true
- Enables POF for the entire JVM instance. -
coherence.pof.config=
CONFIG_FILE_PATH
- The path to the POF configuration file you want to use. If the files is not in the classpath, then it must be presented as a file resource (for example,file:///opt/home/coherence/mycustom-pof-config.xml
).
Using POF Annotations to Serialize Objects
PofAnnotationSerializer
class which is an implementation of the PofSerializer
interface. Annotations offer an alternative to using the PortableObject
and PofSerializer
interfaces and reduce the amount of time and code that is required to make objects serializable.
This section includes the following topics:
- Annotating Objects for POF Serialization
- Registering POF Annotated Objects
- Generating a POF Configuration File
- Enabling Automatic Indexing
- Providing a Custom Codec
Parent topic: Using Portable Object Format
Annotating Objects for POF Serialization
Two annotations are available to indicate that a class and its properties are POF serializable:
-
@Portable
– Marks the class as POF serializable. The annotation is only permitted at the class level and has no members. -
@PortableProperty
– Marks a member variable or method accessor as a POF serialized attribute. Annotated methods must conform to accessor notation (get
,set
,is
). Members can be used to specify POF indexes as well as custom codecs that are executed before or after serialization or deserialization. Index values may be omitted and automatically assigned. If a custom codec is not entered, the default codec is used.
The following example demonstrates annotating a class, method, and properties and assigning explicit property index values. See Guidelines for Assigning POF Indexes.
@Portable public class Person { @PortableProperty(0) public String getFirstName() { return m_firstName; } private String m_firstName; @PortableProperty(1) private String m_lastName; @PortableProperty(2) private int m_age; }
Parent topic: Using POF Annotations to Serialize Objects
Registering POF Annotated Objects
POF annotated objects, like all POF objects, must be registered in a pof-config.xml
file within a <user-type>
element. See POF User Type Configuration Elements. As an alternative to manually creating a POF configuration file, the POF Configuration Generator tool can be used to automatically create a POF configuration file based on objects that use the @Portable
annotation. See Generating a POF Configuration File.
POF annotated objects use the PofAnnotationSerializer
serializer if an object does not implement PortableObject
and is annotated as Portable
; however, the serializer is automatically assumed if an object is annotated and does not need to be included in the user type definition. The following example registers a user type for an annotated Person
object:
<?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.Person</class-name> </user-type> </user-type-list> </pof-config>
Parent topic: Using POF Annotations to Serialize Objects
Generating a POF Configuration File
The POF Configuration Generator command line tool automatically creates a POF configuration file that includes user type entries for the classes that contain the @Portable
annotation. The tool is an alternative to manually creating a POF configuration file and is ideal as part of a build process.
Start the POF Configuration Generator command line tool by using the COHERENCE_HOME
/bin/pof-config-gen
script (.cmd
or .sh
) or by directly running the com.tangosol.io.pof.generator.Executor
class. Use the -help
argument for detailed usage instructions. The usage is as follows:
pof-config-gen [OPTIONS] -root
The -root
argument is required and lists the locations (separated directories, JAR files, or GAR file) to scan for POF annotated classes. A user type entry and ID is generated for each annotated class that is found and the resulting pof-config.xml
file is written to the current working directory. For example:
pof-config-gen.cmd -root c:\src\myPofClasses.jar
Note:
If you specify a GAR file as the root, the output is a new GAR file with a count suffix (filename
-
n
.gar
) that includes the generated POF configuration file.
The following optional arguments may be provided:
-
-out
: Use the-out
argument to specify either the path to an output directory or a path and filename. The default output directory is the working directory. The default filename if only a directory is specified ispof-config.xml
. If a directory is specified and apof-config.xml
file already exists, then a new file is created with a count suffix (pof-config-
n
.xml
). If a path and filename are specified and the file currently exists, then the file is overwritten. -
-config
: Use the-config
argument to specify the path and filename of an existing POF configuration file from which existing user types must be added to the generated POF configuration file. Existing user type IDs are retained in the generated file. This argument can be used to support backwards compatibility when generating a POF configuration file multiple times. -
-include
: Use the-include
argument to specify whether an existing POF configuration file (as specified by the-config
argument) should only be referenced in the generated POF configuration file. The argument results in an<include>
element that references the existing file. Existing user types and IDs are not recreated in the generated file. At runtime, the referenced file must be located in the classpath.Note:
A reference to the Coherence-specific POF configuration file (
coherence-pof-config.xml
) is automatically added to the generated POF configuration file if it is not found in an existing POF configuration file and does not need to be added using the-include
argument. -
-packages
: Use the-packages
argument to constrain the class scan to specific packages. The packages are entered as a comma separated list. -
-startTypeId
: Use the-startTypeId
argument to specify the user type ID number from which to start allocating IDs. IDs up to 1000 are reserved for Coherence-specific types and cannot be used.
The following example scans the c:\classes\pof
directory and creates a new POF configuration file called my-pof-config.xml
in the c:\tmp
directory that includes (by reference) the existing c:\tmp\pof-config.xml
file.
pof-config-gen.cmd -out c:\tmp\my-pof-config.xml -config c:\tmp\pof-config.xml -include -root c:\classes\pof
Parent topic: Using POF Annotations to Serialize Objects
Enabling Automatic Indexing
POF annotations support automatic indexing which alleviates the need to explicitly assign and manage index values. Omit the index value when defining the @PortableProperty
annotation. Index allocation is determined by the property name. Any property that does assign an explicit index value is not assigned an automatic index value. The following table demonstrates the ordering semantics of the automatic index algorithm. Notice that automatic indexing maintains explicitly defined indexes (as shown for property c
) and assigns an index value if an index is omitted.
Property Name | Explicit Index | Determined Index |
---|---|---|
c |
1 |
1 |
a |
omitted |
0 |
b |
omitted |
2 |
Note:
Automatic indexing does not currently support evolvable classes.
To enable automatic indexing, the PofAnnotationSerializer
serializer class must be explicitly defined when registering the object as a user type in the POF configuration file. The fAutoIndex
boolean parameter in the constructor enables automatic indexing and must be set to true
. For example:
<user-type>
<type-id>1001</type-id>
<class-name>com.examples.Person</class-name>
<serializer>
<class-name>com.tangosol.io.pof.PofAnnotationSerializer</class-name>
<init-params>
<init-param>
<param-type>int</param-type>
<param-value>{type-id}</param-value>
</init-param>
<init-param>
<param-type>class</param-type>
<param-value>{class}</param-value>
</init-param>
<init-param>
<param-type>boolean</param-type>
<param-value>true</param-value>
</init-param>
</init-params>
</serializer>
</user-type>
Parent topic: Using POF Annotations to Serialize Objects
Providing a Custom Codec
Codecs allow code to be executed before or after serialization or deserialization. The codec defines how to encode and decode a portable property using the PofWriter
and PofReader
interfaces. Codecs are typically used for concrete implementations that could get lost when being deserialized or to explicitly call a specific method on the PofWriter
interface before serializing an object.
To create a codec, create a class that implements the com.tangosol.io.pof.reflect.Codec
interface. The following example demonstrates a codec that defines the concrete implementation of a linked list type:
public static class LinkedListCodec implements Codec { public Object decode(PofReader in, int index) throws IOException { return (List<String>) in.readCollection(index, new LinkedList<String>()); } public void encode(PofWriter out, int index, Object value) throws IOException { out.writeCollection(index, (Collection) value); { }
To assign a codec to a property, enter the codec as a member of the @PortableProperty
annotation. If a codec is not specified, a default codec (DefaultCodec
) is used. The following example demonstrates assigning the above LinkedListCodec
codec:
@PortableProperty(codec = LinkedListCodec.class) private List<String> m_aliases;
Parent topic: Using POF Annotations to Serialize Objects
Using POF Extractors and POF Updaters
In Coherence, the ValueExtractor
and ValueUpdater
interfaces are used to extract and update values of objects that are stored in the
cache.
The PofExtractor
and PofUpdater
interfaces take
advantage of the POF indexed state to extract or update values without the requirement
to go through the full serialization/deserialization routines.
PofExtractor
and PofUpdater
adds flexibility in working with non-primitive types in Coherence. For many extend client cases, a corresponding Java classes in the grid is no longer required. Because POF extractors and POF updaters can navigate the binary, the entire key and value does not have to be deserialized into object form. This implies that indexing can be achieved by simply using POF extractors to pull a value to index on. However, a corresponding Java class is still required when using a cache store. In this case, the deserialized version of the key and value is passed to the cache store to write to the back end.
This section includes the following topics:
Parent topic: Using Portable Object Format
Navigating a POF object
Due to the fact that POF is indexed, it's possible to quickly traverse the binary to a specific element for extraction or updating. It's the responsibility of the PofNavigator
interface to traverse a POF value object and return the desired POF value object. Out of the box, Coherence provides a SimplePofPath
class that can navigate a POF value based on integer indexes. In the simplest form, provide the index of the attribute to be extracted/updated. Consider the following example:
public class Contact implements PortableObject { ... // ----- PortableObject interface --------------------------------------- public void readExternal(PofReader reader) throws IOException { m_sFirstName = reader.readString(FIRSTNAME); m_sLastName = reader.readString(LASTNAME); m_addrHome = (Address) reader.readObject(HOME_ADDRESS); m_addrWork = (Address) reader.readObject(WORK_ADDRESS); m_mapPhoneNumber = reader.readMap(PHONE_NUMBERS, null); } public void writeExternal(PofWriter writer) throws IOException { writer.writeString(FIRSTNAME, m_sFirstName); writer.writeString(LASTNAME, m_sLastName); writer.writeObject(HOME_ADDRESS, m_addrHome); writer.writeObject(WORK_ADDRESS, m_addrWork); writer.writeMap(PHONE_NUMBERS, m_mapPhoneNumber); } .... // ----- constants ------------------------------------------------------- public static final int FIRSTNAME = 0; public static final int LASTNAME = 1; public static final int HOME_ADDRESS = 2; public static final int WORK_ADDRESS = 3; public static final int PHONE_NUMBERS = 4; ... }
Notice that there's a constant for each data member that is being written to and from the POF stream. This is an excellent practice to follow as it simplifies both writing your serialization routines and makes it easier to work with POF extractors and POF updaters. By labeling each index, it becomes much easier to think about the index. As mentioned above, in the simplest case, the work address can be pulled out of the contact by using the WORK_ADDRESS
index. The SimplePofPath
also allows using an Array
of ints
to traverse the PofValues
. For example, to get the zip code of the work address use [WORK_ADDRESS, ZIP]
. The example are discussed in more detail below.
Parent topic: Using POF Extractors and POF Updaters
Using POF Extractors
POF extractors are typically used when querying a cache and improves query performance. For example, using the class demonstrated above, to query the cache for all contacts with the last names Jones
, the query is as follows:
ValueExtractor veName = new PofExtractor(String.class, Contact.LASTNAME); Filter filter = new EqualsFilter(veName, "Jones"); // find all entries that have a last name of Jones Set setEntries = cache.entrySet(filter);
In the above case, PofExtractor
has a convenience constructor that uses a SimplePofPath
to retrieve a singular index, in our case the Contact.LASTNAME
index. To find all contacts with the area code 01803
, the query is as follows:
ValueExtractor veZip = new PofExtractor( String.class, new SimplePofPath(new int[] {Contact.WORK_ADDRESS, Address.ZIP})); Filter filter = new EqualsFilter(veZip, "01803"); // find all entries that have a work address in the 01803 zip code Set setEntries = cache.entrySet(filter);
Notice that in the previous examples, the PofExtractor
constructor has a first argument with the class of the extracted value or null
. The reason for passing type information is that POF uses a compact form in the serialized value when possible. For example, some numeric values are represented as special POF intrinsic types in which the type implies the value. As a result, POF requires the receiver of a value to have implicit knowledge of the type. PofExtractor
uses the class supplied in the constructor as the source of the type information. If the class is null
, PofExtractor
infers the type from the serialized state, but the extracted type may differ from the expected type. String
types, in fact, can be correctly inferred from the POF stream, so null
is sufficient in the previous examples. In general, however, null
should not be used.
Parent topic: Using POF Extractors and POF Updaters
Using POF Updaters
POF updaters work in the same way as POF extractors except that they update the value of an object rather than extract it. To change all entries with the last name of Jones
to Smith
, use the UpdaterProcessor
class as follows:
ValueExtractor veName = new PofExtractor(String.class, Contact.LASTNAME); Filter filter = new EqualsFilter(veName, "Jones"); ValueUpdater updater = new PofUpdater(Contact.LASTNAME); // find all Contacts with the last name Jones and change them to have the last // name "Smith" cache.invokeAll(filter, new UpdaterProcessor(updater, "Smith"));
Note:
while these examples operate on String
based values, this functionality works on any POF encoded value.
Parent topic: Using POF Extractors and POF Updaters
Serializing Keys Using POF
-
POF defines a cross-platform object format, it cannot always provide a symmetrical conversion. That is, when a serialized object is deserialized, the object type is different than the original type. This occurs because some data types in Java do not have equivalents in the .NET and C++ platforms. As a result, avoid using classes that potentially have an asymmetrical POF conversion as keys, or parts of keys, for caches and other Java collections.
-
Avoided using the
java.util.Date
type. POF is designed to serialize tojava.sql.Timestamp
(which extendsjava.util.Date
). The wire formats for those two classes are identical, and a deserialization of that wire representation always results in ajava.sql.Timestamp
instance. Unfortunately, theequals
method of those two base classes breaks the symmetry requirement for keys in Java collections. That is, if you have two objects:D
(ofjava.util.Date
) andT
(ofjava.sql.Timestamp
) that are equivalent from the POF wire format perspective, thenD.equals(T)
yields true, whileT.equals(D)
yields false. Therefore, the use ofjava.util.Date
must be avoided. Use aLong
representation of the date or thejava.sql.Timestamp
type to avoid breaking the key symmetry requirement. -
Keys that are using POF object references cannot be serialized. In addition, POF object references support circular references. Therefore, you must ensure that your key class does not have circular references.
Parent topic: Using Portable Object Format