25 PofExtractors and PofUpdaters

The following sections are included in this chapter:

Overview

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 Portable Object Format's indexed state to extract or update an object without the need to go through the full serialization/deserialization routines.

The addition of PofExtractors and PofUpdaters adds flexibility in working with non-primitive types in Coherence. For most cases where you're working with extend clients, it's no longer required to have corresponding Java classes in the grid. Because POFExtractors/PofUpdaters can navigate the binary, we don't need to deserialize the entire key/value into Object form. This implies that indexing can be achieved by simply using PofExtractors to pull a value to index on. There are however circumstances where you must provide a corresponding Java class:

  • Key Association – When using key association, Coherence always deserializes keys to determine whether they implement KeyAssociation.

  • Cache Stores – When using a cache store, Coherence passes the deserialized version of the key and value to the cache store to write to the back end.

Navigating a POF object

Due to the fact that the Portable Object Format (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 indices. In the simplest form, all you need to do is to provide the index of the attribute that you want to extract/update.Consider the following example:

public class Contact
        implements PortableObject
    {
    ...
    // ----- PortableObject interface ---------------------------------------
 
    /**
    * {@inheritDoc}
    */
    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);
        }
 
    /**
    * {@inheritDoc}
    */
    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 -------------------------------------------------------
 
    /**
    * The POF index for the FirstName property
    */
    public static final int FIRSTNAME = 0;
 
    /**
    * The POF index for the LastName property
    */
    public static final int LASTNAME = 1;
 
    /**
    * The POF index for the HomeAddress property
    */
    public static final int HOME_ADDRESS = 2;
 
    /**
    * The POF index for the WorkAddress property
    */
    public static final int WORK_ADDRESS = 3;
 
    /**
    * The POF index for the PhoneNumbers property
    */
    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 as well as making it easier to work with PofExtractors and PofUpdaters. By labeling each index, it becomes much easier to think about what we're working with. As mentioned above, in the simplest case, we could pull the work address out of the contact by using the WORK_ADDRESS index. The SimplePofPath also allows using an Array of ints to traverse the PofValues. So for example if we wanted the zip code of the work address we would use [WORK_ADDRESS, ZIP]. We'll go through the example in more detail below.

Using PofExtractors

PofExtractors are typically used when querying a cache and should greatly improve the performance of your queries. If we were to use the example class above, and wanted to query the cache for all Contacts with the last names Jones, the query would look something like this:

ValueExtractor veName = new PofExtractor(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 use case, PofExtractor has a convenience constructor that will use a SimplePofPath to retrieve a singular index, in our case the Contact.LaSTNAME index. Now if we wanted to find all Contacts with the area code 01803, the query would look like this:

ValueExtractor veZip = new PofExtractor(
        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);

Using PofUpdaters

PofUpdater works in the same way as PofExtractor except that they will update the value of an object rather than extract it. So if we wanted to change John Jones' last name to Smith, we would use the UpdaterProcessor like this:

ValueExtractor veName  = new PofExtractor(Contact.LASTNAME);
Filter         filter  = new EqualsFilter(veName, "Jones");
ValueUpdater   updater = new PofUpdator(Contact.LASTNAME);

Note:

while these examples operate on String based values, this functionality will work on any POF encoded value.