PRODUCTS AND SERVICES INDUSTRIES SUPPORT PARTNERS COMMUNITIES ABOUT
  Coherence 3.5 User Guide
  PofExtractors and PofUpdaters
Added by Noah Arliss, last edited by David Guy on Oct 16, 2009  (view change)

Labels

 
(None)

Overview

In Coherence, extractors and updaters are used to extract and update values of objects that are stored in the cache. PofExtractors and PofUpdaters take advantage of POF'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 a PofExtractor 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 is indexed, it's possible to quickly traverse the binary to a specific element for extraction or updating. It's the responsibility of the com.tangosol.io.pof.reflect.PofNavigator interface to traverse a POF value object and return the desired POF value object. Out of the box, coherence provides a com.tangosol.io.pof.reflect.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 you have a Contact class:

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;

    ...
    }

You'll 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 will simplify both writing your serialization routines as well as making it easier to work with PofExtractors and Updaters. 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

Please note that in 3.5.2, a Class parameter was added to the PofExtractor constructors.

Extractors are typically used when Query the Cache, and using a PofExtractor 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(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 use case, the 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(
        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 (added in release 3.5.2) 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 will infer the type from the serialized state, but the extracted type may differ from the expected type. Strings, 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.

Using PofUpdaters

PofUpdaters work in the same way as PofExtractors except that they will update the value of an object rather than extract it. So if we wanted to change all entries with the last name of Jones to Smith, we would use the UpdaterProcessor like this:

ValueExtractor veName  = new PofExtractor(String.class, Contact.LASTNAME);
Filter         filter  = new EqualsFilter(veName, "Jones");
ValueUpdater   updater = new PofUpdator(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"));

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