4 Working with Complex Objects

In this exercise, you work with complex objects located in the cache. It highlights the use of the Coherence PofReader, PofWriter, and PortableObject API. Using Eclipse, you create a new Contact class, and then store and retrieve Contact objects in the cache using Portable Object Format (POF) serialization.

This chapter contains the following sections:

4.1 Introduction

Until now, you have been putting and getting String objects as the value in a NamedCache cache. Many of the implementations of the get and put methods in the Coherence Java API define the values and keys to be of type Object, for example:

public java.lang.Object get(java.lang.Object oKey)
public void put(java.lang.Object oKey, java.lang.Object oValue)

Any object can be used as a value or key. This enables you to store complex objects as values in the cache.

Because Coherence might send the object across the wire, the object must be serializable. Object serialization is the process of saving an object's state into a sequence of bytes, and then rebuilding (deserializing) the bytes into an active object at a future time. For example, objects that implement the java.io.Serializable interface are serializable.

As an alternative to using the Java java.io.Serializable interface, you can improve performance by using Coherence's own class for high-performance serialization, com.tangosol.io.pof.PortableObject. PortableObject format is up to six times faster than the standard Serializable and the serialized result set is smaller.

The PortableObject interface provides two simple methods, readExternal and writeExternal, that permit you to explicitly read and write serialized object attributes from the provided PofReader and PofWriter streams respectively. By taking control over the serialization format, Coherence provides a way to improve the performance of the process. Using POF reduces the size of the resulting binary file. The size of the binary file is often 5 to 10 times smaller, and the conversion to or from the binary file can be between 5 and 20 times faster, depending on the size of the object.

4.2 Creating and Caching Complex Objects

In this exercise, you create a Contact object that contains names, addresses, dates of birth, and telephone numbers for employees. You also use POF serialization to put the objects in the cache and retrieve them by implementing the PortableObject interface.

  1. Create the Data Objects

  2. Create the Complex Object

  3. Create the Driver Class

  4. Create the POF and Cache Configuration Files

  5. Run the Sample Project

4.2.1 Create the Data Objects

This section describes how to create two data objects that will later be incorporated into another data object. An Address object will provide employee address information and a PhoneNumber object will provide telephone contact information.

  1. Create an Address object to store address information for an employee.

    1. Create a new Application Client Project in Eclipse called Contacts. Ensure that the CoherenceConfig is selected in the Configuration field on the opening page and the Create a default main is not selected on the Application Client module page.

      See "Creating a New Project in the Eclipse IDE" for detailed information.

    2. Create a new Java class called Address. Ensure that the Default Package is com.oracle.handson. Do not select the Main Method check box. See "Creating a Java Class" for detailed information.

    3. Write the class to use the PortableObject interface for data serialization. In the Eclipse code editor, change your generated Address class to implement com.tangosol.io.pof.PortableObject. Add an import statement for the PortableObject interface.

    4. Import the com.tangosol.io.pof.PofReader, com.tangosol.io.pof.PofWriter and java.io.IOException classes required by the PortableObject interface.

    5. Add the default public constructor for Address that is required by the PortableObject interface.

    6. Enter the following private attributes for your Address class. You can add others if you like.

      String Street1

      String Street2

      String City

      String State

      String Country

      At this point, the Address class should look similar to the following:

      package com.oracle.handson;
      
      import com.tangosol.io.pof.PofReader;
      import com.tangosol.io.pof.PortableObject;
      import com.tangosol.io.pof.PofWriter;
      
      import java.io.IOException;
      
      public class Address  implements PortableObject 
      {
                 private String Street1;
                 private String Street2;
                 private String City;
                 private String State;
                 private String Zip;
                 private String Country;
          /**
          * Default constructor (necessary for PortableObject implementation).    */ 
          public Address()
              {
              }
      }
      
    7. Eclipse can generate the default get and set methods for your attributes. From the Source menu, select Generate Getters and Setters. Click Select All to select all of the attributes in the class. All of the attributes are now automatically selected. Click OK to continue.

      Figure 4-1 illustrates the Generate Getters and Setters dialog box with the generated accessors for the Address class.

      Figure 4-1 Generate Getters and Setters Dialog Box

      Generate Getters and Setters Dialog Box
      Description of "Figure 4-1 Generate Getters and Setters Dialog Box"

    8. You can also generate the default constructor and equals methods automatically. From the Source menu, select Generate Constructor using Fields, click Select All, and then click OK.

      Figure 4-2 illustrates the Generate Constructor using Fields dialog box with the Street1, Street2, City, State, Zip, and Country fields selected.

      Figure 4-2 Generate Constructors using Fields Dialog Box

      Generate Constructors using Fields Dialog Box
      Description of "Figure 4-2 Generate Constructors using Fields Dialog Box"

      Add "this" to the members of the generated constructor. The generated constructor should then look similar to the following:

          public Address(String Street1, String Street2, String City, String State, String Zip, String Country) {
              super();
              this.Street1 = Street1;
              this.Street2 = Street2;
              this.City = City;
              this.State = State;
              this.Zip = Zip;
              this.Country = Country;
          }
      
    9. Implement the readExternal and writeExternal methods as required by the PortableObject interface. For example, the following implementation of the readExternal method enables the values for the street, city, state, and country to be read as POF objects.

      public void readExternal(PofReader reader)
              throws IOException
           {
           setStreet1(reader.readString(0));
           setStreet2(reader.readString(1));
           setCity(reader.readString(2));
           setState(reader.readString(3));
           setZip(reader.readString(4));
           setCountry(reader.readString(5));
      } 
       
      
    10. Implement the equals, hashCode, and toString object methods.

      Note:

      Cache keys and values must be serializable (for example, java.io.Serializable). Cache keys must also provide an implementation of the hashCode() and equals() methods, and those methods must return consistent results across cluster nodes. This implies that the implementation of hashCode() and equals() must be based solely on the object's serializable state (that is, the object's nontransient fields); most built-in Java types, such as String, Integer and Date, meet this requirement. Some cache implementations (specifically the partitioned cache) use the serialized form of the key objects for equality testing, which means that keys for which the equals() method returns true must serialize identically; most built-in Java types meet this requirement.

      To support these methods, import the com.tangosol.util.Base and com.tangosol.util.HashHelper classes. The Base class provides support for the equals method. The HashHelper class contains helper functions for calculating hash code values for any group of Java intrinsics.

      The following code illustrates a sample implementation of the equals() method:

          public boolean equals(Object oThat)
              {
              if (this == oThat)
                  {
                  return true;
                  }
              if (oThat == null)
                  {
                  return false;
                  }
       
              Address that = (Address) oThat;
              return Base.equals(getStreet1(), that.getStreet1()) &&
                     Base.equals(getStreet2(), that.getStreet2()) &&
                     Base.equals(getCity(),    that.getCity())    &&
                     Base.equals(getState(),   that.getState())   &&
                     Base.equals(getZip(),     that.getZip())     &&
                     Base.equals(getCountry(), that.getCountry());
              }
           
      

      The following code illustrates a sample implementation of the hashCode() method:

          public int hashCode()
              {
              return HashHelper.hash(getStreet1(),
                     HashHelper.hash(getStreet2(),
                     HashHelper.hash(getZip(), 0)));
              }
      

      The following code illustrates a sample implementation of the toString() method:

          public String toString()
              {
              return  getStreet1() + "\n" +
                      getStreet2() + "\n" +
                      getCity() + ", " + getState() + " "  + getZip() + "\n" +
                      getCountry();
              }
      
    11. The resulting class should look similar to Example 4-1.

      Example 4-1 Implementation of an Address Class

      package com.oracle.handson;
      
      import com.tangosol.io.pof.PofReader;
      import com.tangosol.io.pof.PortableObject;
      import com.tangosol.io.pof.PofWriter;
       
      import com.tangosol.util.Base;
      import com.tangosol.util.HashHelper;
       
      import java.io.IOException;
       
      public class Address  implements PortableObject 
                 {
                 private String Street1;
                 private String Street2;
                 private String City;
                 private String State;
                 private String Zip;
                 private String Country;
          /**
          * Default constructor (necessary for PortableObject implementation).
          */
          public Address() {
              }
       
          public Address(String Street1, String Street2, String City, String State,
                         String Zip, String Country) 
             {
              super();
              this.Street1 = Street1;
              this.Street2 = Street2;
              this.City    = City;
              this.State   = State;
              this.Zip     = Zip;
              this.Country = Country;
          }
        
          //------------ accessors--------------------------------
        
          public void setStreet1(String Street1) 
              {
              this.Street1 = Street1;
              }
       
          public String getStreet1() 
              {
              return Street1;
              }
       
          public void setStreet2(String Street2) 
              {
              this.Street2 = Street2;
              }
       
          public String getStreet2() 
              {
              return Street2;
              }
       
          public void setCity(String City)  
              {
              this.City = City;
              }
       
          public String getCity() 
              {
              return City;
              }
       
          public void setState(String State) 
              {
              this.State = State;
              }
       
          public String getState()  
              {
              return State;
              }
       
          public void setZip(String Zip) 
              {
              this.Zip = Zip;
              }
       
          public String getZip() 
              {
              return Zip;
              }
       
          public void setCountry(String Country) 
              {
              this.Country = Country;
              }
       
          public String getCountry() 
              {
              return Country;
              }
         // -------- PortableObject Interface------------------------------
          
          public void readExternal(PofReader reader)
                  throws IOException
              {
              setStreet1(reader.readString(0));
              setStreet2(reader.readString(1));
              setCity(reader.readString(2));
              setState(reader.readString(3));
              setZip(reader.readString(4));
              setCountry(reader.readString(5));
              }
       
          public void writeExternal(PofWriter writer)
                  throws IOException
              {
              writer.writeString(0, getStreet1());
              writer.writeString(1, getStreet2());
              writer.writeString(2, getCity());
              writer.writeString(3, getState());
              writer.writeString(4, getZip());
              writer.writeString(5, getCountry());
              }
          // ----- Object methods --------------------------------------------------
       
          
          public boolean equals(Object oThat)
              {
              if (this == oThat)
                  {
                  return true;
                  }
              if (oThat == null)
                  {
                  return false;
                  }
       
              Address that = (Address) oThat;
              return Base.equals(getStreet1(), that.getStreet1()) &&
                     Base.equals(getStreet2(), that.getStreet2()) &&
                     Base.equals(getCity(),    that.getCity())    &&
                     Base.equals(getState(),   that.getState())   &&
                     Base.equals(getZip(),     that.getZip())     &&
                     Base.equals(getCountry(), that.getCountry());
              }
       
          
          public int hashCode()
              {
              return HashHelper.hash(getStreet1(),
                     HashHelper.hash(getStreet2(),
                     HashHelper.hash(getZip(), 0)));
              }
       
          
          public String toString()
              {
              return  getStreet1() + "\n" +
                      getStreet2() + "\n" +
                      getCity() + ", " + getState() + " "  + getZip() + "\n" +
                      getCountry();
              }
      }
      
  2. Create a PhoneNumber class to store telephone contact data.

    1. Create a new Java class called PhoneNumber. Do not include a main method.

      See "Creating a Java Class" for detailed information.

    2. Use the PortableObject interface for data serialization. In the Eclipse code editor, change your generated PhoneNumber class to implement PortableObject. Add an import statement for the com.tangosol.io.pof.PortableObject interface.

    3. Import the com.tangosol.io.pof.PofReader, com.tangosol.io.pof.PofWriter, and java.io.IOException classes required by the PortableObject interface.

    4. Add the default public constructor for the PhoneNumber class that is required by the PortableObject interface.

    5. Enter the following private attributes for your PhoneNumber class. You can add others.

      short AccessCode

      short CountryCode

      short AreaCode

      int LocalNumber

    6. Eclipse can generate the default get and set methods for your attributes. From the Source menu, select Generate Getters and Setters. Click Select All to select all of the attributes in the class. All of the attributes are now automatically selected. Click OK to continue.

    7. You can also generate the default constructor and equals methods automatically. From the Source menu, select Generate Constructor using Fields, click Select All, and then click OK.

      Add "this" to the members of the generated constructor. The generated constructor then looks similar to the following:

      public PhoneNumber(short AccessCode, short CountryCode, short AreaCode,
                   int LocalNumber) {
          super();
          this.AccessCode = AccessCode;
          this.CountryCode = CountryCode;
          this.AreaCode = AreaCode;
          this.LocalNumber = LocalNumber; 
          }
      
    8. Implement the readExternal and writeExternal methods as required by the PortableObject interface.

    9. Implement the equals, hashCode and toString object methods.

    10. The resulting class looks similar to Example 4-2.

      Example 4-2 Implementation of a PhoneNumber Class

      package com.oracle.handson;
       
      import com.tangosol.io.pof.PofReader;
      import com.tangosol.io.pof.PofWriter;
      import com.tangosol.io.pof.PortableObject;
       
      import com.tangosol.util.HashHelper;
       
      import java.io.IOException;
       
      public class PhoneNumber implements PortableObject 
      {
          
          private short AccessCode;
          private short CountryCode;
          private short AreaCode;
          private int LocalNumber;
          
      
          //------------ constructors --------------------------------
      
          /**
          * Default constructor (necessary for PortableObject implementation).
          */
          public PhoneNumber() {
          }
       
          public PhoneNumber(short AccessCode, short CountryCode, short AreaCode,
                       int LocalNumber) 
              {
              super();
              this.AccessCode = AccessCode;
              this.CountryCode = CountryCode;
              this.AreaCode = AreaCode;
              this.LocalNumber = LocalNumber;
              }
       
          //------------ accessors--------------------------------
       
          public void setAccessCode(short AccessCode) 
              {
              this.AccessCode = AccessCode;
              }
       
          public short getAccessCode() 
              {
              return AccessCode;
              }
       
          public void setCountryCode(short CountryCode) 
              {
              this.CountryCode = CountryCode;
              }
       
          public short getCountryCode() 
              {
              return CountryCode;
              }
       
          public void setAreaCode(short AreaCode) 
              {
              this.AreaCode = AreaCode;
              }
       
          public short getAreaCode() 
              {
              return AreaCode;
              }
       
          public void setLocalNumber(int LocalNumber) 
              {
              this.LocalNumber = LocalNumber;
              }
       
          public int getLocalNumber() 
             {
              return LocalNumber;
              }
          // -------- PortableObject Interface------------------------------
          
          public void readExternal(PofReader reader)
                  throws IOException
              {
              setAccessCode(reader.readShort(0));
              setCountryCode(reader.readShort(1));
              setAreaCode(reader.readShort(2));
              setLocalNumber(reader.readInt(3));
              }
       
          
          public void writeExternal(PofWriter writer)
                  throws IOException
              {
              writer.writeShort(0, getAccessCode());
              writer.writeShort(1, getCountryCode());
              writer.writeShort(2, getAreaCode());
              writer.writeInt(3, getLocalNumber());
              }   
          // ----- Object methods -------------------------------------------------
       
          /**
          * {@inheritDoc}
          */
          public boolean equals(Object oThat)
              {
              if (this == oThat)
                  {
                  return true;
                  }
              if (oThat == null)
                  {
                  return false;
                  }
       
              PhoneNumber that = (PhoneNumber) oThat;
              return getAccessCode()  == that.getAccessCode()  &&
                     getCountryCode() == that.getCountryCode() &&
                     getAreaCode()    == that.getAreaCode()    &&
                     getLocalNumber() == that.getLocalNumber();
              }
       
          /**
          * {@inheritDoc}
          */
          public int hashCode()
              {
              return HashHelper.hash(getAreaCode(),
                     HashHelper.hash(getLocalNumber(), 0));
              }
       
          /**
          * {@inheritDoc}
          */
          public String toString()
              {
              return "+" + getAccessCode() + " " + getCountryCode() + " "
                         + getAreaCode()   + " " + getLocalNumber();
              }
      }
      

4.2.2 Create the Complex Object

The Contact object provides the name, address, and telephone information of employees by incorporating the Address and PhoneNumber data objects.

  1. Create a new Java class called Contact. Do not include a main method.

    See "Creating a Java Class" for more information.

  2. Because the class uses the PortableObject interface for data serialization, change your generated Contact class to implement PortableObject in the Eclipse code editor. Add an import statement for the com.tangosol.io.pof.PortableObject interface.

  3. Import the com.tangosol.io.pof.PofReader and com.tangosol.io.pof.PofWriter and java.io.IOException classes required by PortableObject.

  4. Add the default public constructor for Contact that is required by PortableObject.

  5. Enter the following private attributes for your Contact class. You can add others.

    • String FirstName

    • String LastName

    • Address HomeAddress

    • Address WorkAddress

    • Map TelephoneNumbers

    • java.sql.Date BirthDate

  6. Eclipse can generate the default get and set methods for your attributes. From the Source menu, select Generate Getters and Setters. Click Select All to select all of the attributes in the class. Click OK to continue.

  7. Create an accessor, getAge, to calculate the age of an employee:

    public int getAge()
            {
            return (int) ((System.currentTimeMillis() - BirthDate.getTime()) /  MILLIS_IN_YEAR);  
            }
    
  8. Add a definition for MILLIS_IN_YEAR.

    public static final long MILLIS_IN_YEAR = 1000L * 60L * 60L * 24L * 365L; 
    
  9. You can generate the default constructor automatically. From the Source menu, select Generate Constructor using Fields, click Select All, and then click OK.

    The generated constructor looks similar to the following:

    public Contact(String FirstName, String LastName, Address HomeAddress,
                   Address WorkAddress, Map TelephoneNumbers, Date BirthDate) {
        super();
        this.FirstName = FirstName;
        this.LastName = LastName;
        this.HomeAddress = HomeAddress;
        this.WorkAddress = WorkAddress;
        this.TelephoneNumbers = TelephoneNumbers;
        this.BirthDate = BirthDate;
    } 
    
  10. Implement the readExternal and writeExternal methods as required by the PortableObject interface.

  11. Implement the equals, hashCode, and toString object methods.

  12. The resulting class looks similar to Example 4-3.

    Example 4-3 Sample Contact Class

    package com.oracle.handson;
    
    import com.tangosol.io.pof.PortableObject;
     
    import com.tangosol.io.pof.PofReader;
    import com.tangosol.io.pof.PofWriter;
     
    import java.io.IOException;
     
    import java.sql.Date;
     
    import java.util.Iterator;
    import java.util.Map;
     
    public class Contact implements PortableObject
           {
           private String FirstName;
           private String LastName;
           private Address HomeAddress;
           private Address WorkAddress;
           private Map TelephoneNumbers;
           private java.sql.Date BirthDate;
        
        // ----- constructors ---------------------------------------------------
     
        /**
        * Default constructor (necessary for PortableObject implementation).
        */
        public Contact()
            {
            }
     
        public Contact(String FirstName, String LastName, Address HomeAddress,
                       Address WorkAddress, Map TelephoneNumbers, Date BirthDate) 
            {
            super();
            this.FirstName = FirstName;
            this.LastName = LastName;
            this.HomeAddress = HomeAddress;
            this.WorkAddress = WorkAddress;
            this.TelephoneNumbers = TelephoneNumbers;
            this.BirthDate = BirthDate;
            }
        
        // -------- accessors --------------------------------------------
     
        public void setFirstName(String FirstName) 
            {
            this.FirstName = FirstName;
            }
     
        public String getFirstName() 
            {
            return FirstName;
            }
     
        public void setLastName(String LastName) 
            {
            this.LastName = LastName;
            }
     
        public String getLastName() 
            {
            return LastName;
            }
     
        public void setHomeAddress(Address HomeAddress) 
            {
            this.HomeAddress = HomeAddress;
            }
     
        public Address getHomeAddress() 
            {
            return HomeAddress;
            }
     
        public void setWorkAddress(Address WorkAddress) 
            {
            this.WorkAddress = WorkAddress;
            }
     
        public Address getWorkAddress() 
            {
            return WorkAddress;
            }
     
        public void setTelephoneNumbers(Map TelephoneNumbers) 
            {
            this.TelephoneNumbers = TelephoneNumbers;
            }
     
        public Map getTelephoneNumbers() 
            {
            return TelephoneNumbers;
            }
     
        public void setBirthDate(Date BirthDate) 
            {
            this.BirthDate = BirthDate;
            }
     
        public Date getBirthDate() 
            {
            return BirthDate;
            }
        /**
        * Get age.
        *
        * @return age
        */
        public int getAge()
            {
            return (int) ((System.currentTimeMillis() - BirthDate.getTime()) /
                    MILLIS_IN_YEAR);
            }
     
     
        // ----- PortableObject interface ---------------------------------------
     
        /**
        * {@inheritDoc}
        */
        public void readExternal(PofReader reader)
                throws IOException
            {
            setFirstName(reader.readString(0));
            setLastName(reader.readString(1));
            setHomeAddress((Address) reader.readObject(2));
            setWorkAddress((Address) reader.readObject(3));
            setTelephoneNumbers(reader.readMap(4, null));
            setBirthDate(new Date(reader.readLong(5)));
            }
     
        /**
        * {@inheritDoc}
        */
        public void writeExternal(PofWriter writer)
                throws IOException
            {
            writer.writeString(0, getFirstName());
            writer.writeString(1, getLastName());
            writer.writeObject(2, getHomeAddress());
            writer.writeObject(3, getWorkAddress());
            writer.writeMap(4, getTelephoneNumbers());
            writer.writeLong(5, getBirthDate().getTime());
            }
     
     
        // ----- Object methods -------------------------------------------------
     
        /**
        * {@inheritDoc}
        */
        public String toString()
            {
            StringBuffer sb = new StringBuffer(getFirstName())
                    .append(" ")
                    .append(getLastName())
                    .append("\nAddresses")
                    .append("\nHome: ").append(getHomeAddress())
                    .append("\nWork: ").append(getWorkAddress())
                    .append("\nTelephone Numbers");
     
            for (Iterator iter = TelephoneNumbers.entrySet().iterator();
                 iter.hasNext(); )
                {
                Map.Entry entry = (Map.Entry) iter.next();
                sb.append("\n")
                   .append(entry.getKey()).append(": ").append(entry.getValue());
                }
            return sb.append("\nBirth Date: ").append(getBirthDate()).toString();
            }
        /**
        * Approximate number of millis in a year ignoring things such as leap
        * years. Suitable for example use only.
        */
        public static final long MILLIS_IN_YEAR = 1000L * 60L * 60L * 24L * 365L;
     
    }
    

4.2.3 Create the Driver Class

Create a driver class called ContactDriver to put Contact entries into the cache and retrieve them.

  1. Create a new Java class called ContactDriver in the Contacts project. Ensure that it includes a main method.

    See "Creating a Java Class" for detailed information.

  2. In the ContactDriver class, create a new NamedCache called contact and put a new instance of the Contact object in it. Get the Contact object from the cache and ensure that the two objects are identical. Example 4-4 illustrates a sample implementation of this class.

    Example 4-4 Sample ContactDriver Class

    package com.oracle.handson;
    
    import com.tangosol.net.CacheFactory;
    import com.tangosol.net.NamedCache;
    
    import java.sql.Date;
    
    import java.util.GregorianCalendar;
    import java.util.Map;
    import java.util.HashMap;
    
    public class ContactDriver {
    
        public ContactDriver() 
        {
        }
                
        public static void main(String[] args) {
           NamedCache contact = CacheFactory.getCache("contact");
               
           Address homeAddress = new Address ("4157 Wash Ave", "Suite 4",    
                                        "Burlingame", "CA", "94407", "USA");
           Address workAddress = new Address ("500 Oracle Pkwy", "MS989", 
                                        "Redwood Shores", "CA", "94065", "USA");
           Date date = new java.sql.Date(new GregorianCalendar(2011, 05, 01).getTime().getTime());
           PhoneNumber phonenumber = new PhoneNumber ((short)11, (short)650, (short)506, 7000);
           Map map = new HashMap();
           map.put("home", phonenumber);
                   
           Contact con1 = new Contact("Tom", "Dunn", homeAddress, workAddress, 
                                          map, date);
    
           contact.put(con1.getFirstName(),con1);
     
           Contact con2 = (Contact)contact.get(con1.getFirstName());
    
           if (con2.getFirstName().equals(con1.getFirstName())) 
           {
           System.out.println("They are the same!!");
           } 
        }
    }
    

4.2.4 Create the POF and Cache Configuration Files

To use POF serialization, you must register your user-defined objects in a POF configuration file. The configuration associates the class of a user-defined object with a numeric value. You must also specify POF serialization and the name of the POF configuration file in the cache configuration file.

  1. Create a POF configuration file for the Contact, Address, and PhoneNumber objects.

    1. To create a POF configuration file for your data types, use the pof-config.xml file that was created with your project.

    2. Define <user-type> elements for the Contact, Address, and PhoneNumber objects, assign type IDs 1001, 1002, and 1003 to them and provide their full class names. The file must include the coherence-pof-config.xml file which reserves the first 1000 IDs for Coherence data types.

    3. Rename the file and save it as contacts-pof-config.xml (it will be saved to the C:\home\oracle\workspace\Contacts\appClientModule folder). Example 4-5 illustrates a sample contacts-pof-config.xml file.

      Example 4-5 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>
      
          <!-- coherence POF user types -->
          <include>coherence-pof-config.xml</include>
      
          <!-- com.tangosol.examples package -->
          <user-type> 
            <type-id>1001</type-id> 
            <class-name>com.oracle.handson.Contact</class-name> 
          </user-type> 
          <user-type> 
            <type-id>1002</type-id> 
            <class-name>com.oracle.handson.Address</class-name> 
          </user-type> 
          <user-type> 
              <type-id>1003</type-id> 
            <class-name>com.oracle.handson.PhoneNumber</class-name> 
          </user-type> 
        </user-type-list>
        <allow-interfaces>true</allow-interfaces>
        <allow-subclasses>true</allow-subclasses>
      </pof-config>
      
  2. Create a cache configuration file. You can use the coherence-cache-config.xml file in the Project Explorer, which is based on the coherence-cache-config.xsd file. You can also find a copy of coherence-cache-config.xml file in the coherence.jar file.

    1. Use ExamplesPartitionedPocScheme as the scheme-name and PartitionedPofCache as the service-name.

    2. The serializer section is responsible for mapping a POF serialized object to an appropriate serialization routine which is either a PofSerializer or by calling through the PortableObject interface. In this case, use the com.tangosol.io.pof.ConfigurablePofContext class.

    3. Use the <init-param> section to point to the name of the POF configuration file, in this case contacts-pof-config.xml.

    4. Rename the file in the Project Explorer and save it as contacts-cache-config.xml (it will be saved to the C:\home\oracle\workspace\Contacts\appClientModule folder). Example 4-6 illustrates a sample contacts-cache-config.xml file.

    Example 4-6 Cache Configuration File

    <?xml version="1.0"?>
    
    <!--
    Note: This XML document is an example Coherence Cache Configuration deployment descriptor that should be customized (or replaced) for your particular caching requirements. The cache mappings and schemes declared in this descriptor are  strictly for demonstration purposes and are not required.
    
    For detailed information on each of the elements that can be used in this descriptor please see the Coherence Cache Configuration deployment descriptor guide included in the Coherence distribution or the "Cache Configuration Elements" page on the Coherence Wiki (http://wiki.tangosol.com).
    -->
    
    <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 http://xmlns.oracle.com/coherence/coherence-cache-config/1.2/coherence-cache-config.xsd">
      <!--
      The defaults element defines factory-wide default settings.
      -->
      <defaults>
        <!--
        Note: This element defines the default serializer for all services defined within this cache configuration descriptor.
    
        Valid values include full serializer definitions, as well as named references to serializers defined within the "serializers" element of the operational configuration.
    
        Example values include: java, pof. Default value is java.
        -->
        <serializer system-property="tangosol.coherence.serializer"/>
    
        <!--
        Note: This element defines the default socket-provider for all Coherence    Extend services defined within this cache configuration descriptor.
    
        Valid values include full socket-provider definitions, as well as named    references to providers defined within the "socket-providers" element of the    operational configuration.
    
        This setting only specifies the default socket-provider for Coherence    Extend services; TCMP's socket-provider is specified within the "unicast-listener" of the operational configuration.
    
        Example values include: system, ssl. Default value is system.
        -->
        <socket-provider system-property="tangosol.coherence.socketprovider"/>
      </defaults>
    
      <caching-scheme-mapping>
        <cache-mapping>
          <cache-name>*</cache-name>
          <scheme-name>ExamplesPartitionedPofScheme</scheme-name>
        </cache-mapping>
      </caching-scheme-mapping>
    
      <caching-schemes>
        <distributed-scheme>
          <scheme-name>ExamplesPartitionedPofScheme</scheme-name> 
          <service-name>PartitionedPofCache</service-name> 
          <serializer>  
           <instance>
            <class-name>com.tangosol.io.pof.ConfigurablePofContext</class-name> 
              <init-params> 
                <init-param> 
                  <param-type>String</param-type>  
                  <param-value>contacts-pof-config.xml</param-value> 
                </init-param> 
              </init-params> 
           </instance>
          </serializer>  
          <backing-map-scheme>
            <local-scheme>
              <!-- each node will be limited to 250MB -->
              <high-units>250M</high-units> 
              <unit-calculator>binary</unit-calculator> 
            </local-scheme>
          </backing-map-scheme>
          <autostart>true</autostart>
        </distributed-scheme>
      </caching-schemes>
    </cache-config>
    

4.2.5 Run the Sample Project

The contacts project requires a cache server to be running. The cache server and the application must reference the same POF and cache configuration files. This section describes how to create run configurations for the cache server and the application.

  1. Stop any cache servers that are running. See "Stopping Cache Servers" for detailed information.

  2. Create an executable file to start the cache server.

    1. Right click the project and select Run As then Run Configurations. In the Run Configurations dialog box, enter ContactsCacheServer in the Name field.

    2. In the Main tab, ensure that Contacts appears in the Project field. Select the Include system libraries when searching for a main class checkbox and click the Search button. In the Select Main Type dialog box, enter DefaultCacheServer and select the DefaultCacheServer - com.tangosol.net class. Click OK in the Select Main Type dialog box, then Apply in the Run Configurations dialog box.

    3. In the General tab of the Coherence tab, select Absolute File Path in the Topology field and browse to the contacts-cache-config.xml file in the C:\home\oracle\workspace\Contacts\appClientModule directory. Ensure that Enabled (cache server) is selected. Enter a unique value for the Cluster port. Click Apply. The General tab should look similar to Figure 4-3.

      Figure 4-3 Coherence Tab for the Contacts Cache Server Executable

      Coherence Tab for Contacts Cache Server Executable
      Description of "Figure 4-3 Coherence Tab for the Contacts Cache Server Executable"

    4. In the Other tab of the Coherence tab, scroll down to the tangosol.pof.config item. Enter the path to the POF configuration file: C:\home\oracle\workspace\Contacts\appClientModule\contacts-pof-config.xml.

    5. In the Arguments tab, enter -showversion under VM Arguments.

    6. In the Classpath tab, ensure that the Contacts project appears under User Entries. If it does not, click the Add Projects button to add the Contacts project.

    7. In the Common tab, select Shared file and browse for the \Contacts project.

    8. Click Run to start the Contacts cache server. The output should be similar to Example 4-7.

      Example 4-7 Output from the Contacts Cache Server

      java version "1.7.0"
      Java(TM) SE Runtime Environment (build 1.7.0-b147)
      Java HotSpot(TM) Client VM (build 21.0-b17, mixed mode, sharing)
       
      2012-08-16 15:09:26.380/1.718 Oracle Coherence 12.1.2.0 <Info> (thread=main, member=n/a): Loaded operational configuration from "jar:file:/C:/oracle/Middleware/Oracle_Home/coherence/lib/coherence.jar!/tangosol-coherence.xml"
      2012-08-16 15:09:26.427/1.765 Oracle Coherence 12.1.2.0 <Info> (thread=main, member=n/a): Loaded operational overrides from "jar:file:/C:/oracle/Middleware/Oracle_Home/coherence/lib/coherence.jar!/tangosol-coherence-override-dev.xml"
      2012-08-16 15:09:26.474/1.812 Oracle Coherence 12.1.2.0 <Info> (thread=main, member=n/a): Loaded operational overrides from "file:/C:/home/oracle/workspace/Contacts/build/classes/tangosol-coherence-override.xml"
      2012-08-16 15:09:26.474/1.812 Oracle Coherence 12.1.2.0 <D5> (thread=main, member=n/a): Optional configuration override "cache-factory-config.xml" is not specified
      2012-08-16 15:09:26.474/1.812 Oracle Coherence 12.1.2.0 <D5> (thread=main, member=n/a): Optional configuration override "cache-factory-builder-config.xml" is not specified
      2012-08-16 15:09:26.474/1.812 Oracle Coherence 12.1.2.0 <D5> (thread=main, member=n/a): Optional configuration override "/custom-mbeans.xml" is not specified
       
      Oracle Coherence Version 12.1.2.0 Build 36845
       Grid Edition: Development mode
      Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
       
      2012-08-16 15:09:26.896/2.234 Oracle Coherence GE 12.1.2.0 <Info> (thread=main, member=n/a): Loaded cache configuration from "file:/C:/home/oracle/workspace/Contacts/appClientModule/contacts-cache-config.xml"
      2012-08-16 15:09:27.427/2.765 Oracle Coherence GE 12.1.2.0 <Info> (thread=main, member=n/a): Created cache factory com.tangosol.net.ExtensibleConfigurableCacheFactory
      2012-08-16 15:09:29.240/4.578 Oracle Coherence GE 12.1.2.0 <D4> (thread=main, member=n/a): TCMP bound to /130.35.99.202:8088 using SystemDatagramSocketProvider
      2012-08-16 15:09:32.693/8.031 Oracle Coherence GE 12.1.2.0 <Info> (thread=Cluster, member=n/a): Created a new cluster "cluster:0x47DB" with Member(Id=1, Timestamp=2012-08-16 15:09:29.365, Address=130.35.99.202:8088, MachineId=18578, Location=site:,machine:tpfaeffl-lap7,process:5792, Role=CoherenceServer, Edition=Grid Edition, Mode=Development, CpuCount=2, SocketCount=1)
      2012-08-16 15:09:32.693/8.031 Oracle Coherence GE 12.1.2.0 <Info> (thread=main, member=n/a): Started cluster Name=cluster:0x47DB
       
      Group{Address=224.12.1.0, Port=3155, TTL=4}
       
      MasterMemberSet(
        ThisMember=Member(Id=1, Timestamp=2012-08-16 15:09:29.365, Address=130.35.99.202:8088, MachineId=18578, Location=site:,machine:tpfaeffl-lap7,process:5792, Role=CoherenceServer)
        OldestMember=Member(Id=1, Timestamp=2012-08-16 15:09:29.365, Address=130.35.99.202:8088, MachineId=18578, Location=site:,machine:tpfaeffl-lap7,process:5792, Role=CoherenceServer)
        ActualMemberSet=MemberSet(Size=1
          Member(Id=1, Timestamp=2012-08-16 15:09:29.365, Address=130.35.99.202:8088, MachineId=18578, Location=site:,machine:tpfaeffl-lap7,process:5792, Role=CoherenceServer)
          )
        MemberId|ServiceVersion|ServiceJoined|MemberState
          1|12.1.2|2012-08-16 15:09:29.365|JOINED
        RecycleMillis=1200000
        RecycleSet=MemberSet(Size=0
          )
        )
       
      TcpRing{Connections=[]}
      IpMonitor{Addresses=0}
       
      2012-08-16 15:09:32.880/8.218 Oracle Coherence GE 12.1.2.0 <D5> (thread=Invocation:Management, member=1): Service Management joined the cluster with senior service member 1
      2012-08-16 15:09:32.959/8.297 Oracle Coherence GE 12.1.2.0 <Info> (thread=main, member=1): Loaded Reporter configuration from "jar:file:/C:/oracle/Middleware/Oracle_Home/coherence/lib/coherence.jar!/reports/report-group.xml"
      2012-08-16 15:09:33.459/8.797 Oracle Coherence GE 12.1.2.0 <Info> (thread=DistributedCache:PartitionedPofCache, member=1): Loaded POF configuration from "file:/C:/home/oracle/workspace/Contacts/build/classes/contacts-pof-config.xml"
      2012-08-16 15:09:33.537/8.875 Oracle Coherence GE 12.1.2.0 <Info> (thread=DistributedCache:PartitionedPofCache, member=1): Loaded included POF configuration from "jar:file:/C:/oracle/Middleware/Oracle_Home/coherence/lib/coherence.jar!/coherence-pof-config.xml"
      2012-08-16 15:09:33.740/9.078 Oracle Coherence GE 12.1.2.0 <D5> (thread=DistributedCache:PartitionedPofCache, member=1): Service PartitionedPofCache joined the cluster with senior service member 1
      2012-08-16 15:09:33.802/9.140 Oracle Coherence GE 12.1.2.0 <Info> (thread=main, member=1): 
      Services
        (
        ClusterService{Name=Cluster, State=(SERVICE_STARTED, STATE_JOINED), Id=0, Version=12.1.2, OldestMemberId=1}
        InvocationService{Name=Management, State=(SERVICE_STARTED), Id=1, Version=12.1.2, OldestMemberId=1}
        PartitionedCache{Name=PartitionedPofCache, State=(SERVICE_STARTED), LocalStorage=enabled, PartitionCount=257, BackupCount=1, AssignedPartitions=257, BackupPartitions=0}
        )
       
      Started DefaultCacheServer...
      
  3. Create a run configuration for the ContactDriver executable.

    • Right click the ContactDriver.java file in the Project Explorer and select Run As then Run Configurations.

    • In the Run Configurations dialog box, double-click the Oracle Coherence node. Enter ContactsDriver in the Name field. In the Main tab, browse for Contacts in the Project field, and com.oracle.handson.ContactDriver in the Main class field. Click Apply.

    • In the General tab of the Coherence tab, browse for the absolute path to the contacts-cache-config.xml file in the Cache configuration descriptor field. Select the Disable (cache client) radio button. In the Cluster port field, enter 3155

    • In the Other tab of the Coherence tab, scroll down to the tangosol.pof.config field and replace the value with the absolute path to the contacts-pof-config.xml POF configuration file.

    • In the Classpath tab, ensure that Contacts (default classpath) appears under User Entries.

  4. Click Run to run the ContactDriver configuration. The output of the Contacts example should look similar to Example 4-8.

    In this example, the Contact object is converted to POF at run time. Using POF provides significant performance improvements in CPU time and the size of the generated binary file.

    Example 4-8 Output of the Contacts Example in the Eclipse IDE

    java version "1.7.0"
    Java(TM) SE Runtime Environment (build 1.7.0-b147)
    Java HotSpot(TM) Client VM (build 21.0-b17, mixed mode, sharing)
     
    2012-08-16 15:15:22.568/0.500 Oracle Coherence 12.1.2.0 <Info> (thread=main, member=n/a): Loaded operational configuration from "jar:file:/C:/oracle/Middleware/Oracle_Home/coherence/lib/coherence.jar!/tangosol-coherence.xml"
    2012-08-16 15:15:22.615/0.547 Oracle Coherence 12.1.2.0 <Info> (thread=main, member=n/a): Loaded operational overrides from "jar:file:/C:/oracle/Middleware/Oracle_Home/coherence/lib/coherence.jar!/tangosol-coherence-override-dev.xml"
    2012-08-16 15:15:22.662/0.594 Oracle Coherence 12.1.2.0 <Info> (thread=main, member=n/a): Loaded operational overrides from "file:/C:/home/oracle/workspace/Contacts/build/classes/tangosol-coherence-override.xml"
    2012-08-16 15:15:22.662/0.594 Oracle Coherence 12.1.2.0 <D5> (thread=main, member=n/a): Optional configuration override "cache-factory-config.xml" is not specified
    2012-08-16 15:15:22.662/0.594 Oracle Coherence 12.1.2.0 <D5> (thread=main, member=n/a): Optional configuration override "cache-factory-builder-config.xml" is not specified
    2012-08-16 15:15:22.662/0.594 Oracle Coherence 12.1.2.0 <D5> (thread=main, member=n/a): Optional configuration override "/custom-mbeans.xml" is not specified
     
    Oracle Coherence Version 12.1.2.0 Build 36845
     Grid Edition: Development mode
    Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
     
    2012-08-16 15:15:23.099/1.031 Oracle Coherence GE 12.1.2.0 <Info> (thread=main, member=n/a): Loaded cache configuration from "file:/C:/home/oracle/workspace/Contacts/appClientModule/contacts-cache-config.xml"
    2012-08-16 15:15:23.490/1.422 Oracle Coherence GE 12.1.2.0 <Info> (thread=main, member=n/a): Created cache factory com.tangosol.net.ExtensibleConfigurableCacheFactory
    2012-08-16 15:15:24.693/2.625 Oracle Coherence GE 12.1.2.0 <D4> (thread=main, member=n/a): TCMP bound to /130.35.99.202:8090 using SystemDatagramSocketProvider
    2012-08-16 15:15:25.068/3.000 Oracle Coherence GE 12.1.2.0 <Info> (thread=Cluster, member=n/a): Failed to satisfy the variance: allowed=16, actual=31
    2012-08-16 15:15:25.068/3.000 Oracle Coherence GE 12.1.2.0 <Info> (thread=Cluster, member=n/a): Increasing allowable variance to 17
    2012-08-16 15:15:25.396/3.328 Oracle Coherence GE 12.1.2.0 <Info> (thread=Cluster, member=n/a): This Member(Id=2, Timestamp=2012-08-16 15:15:25.193, Address=130.35.99.202:8090, MachineId=18578, Location=site:,machine:tpfaeffl-lap7,process:4572, Role=OracleHandsonContactDriver, Edition=Grid Edition, Mode=Development, CpuCount=2, SocketCount=1) joined cluster "cluster:0x47DB" with senior Member(Id=1, Timestamp=2012-08-16 15:09:29.365, Address=130.35.99.202:8088, MachineId=18578, Location=site:,machine:tpfaeffl-lap7,process:5792, Role=CoherenceServer, Edition=Grid Edition, Mode=Development, CpuCount=2, SocketCount=1)
    2012-08-16 15:15:25.630/3.562 Oracle Coherence GE 12.1.2.0 <D5> (thread=Cluster, member=n/a): Member 1 joined Service Management with senior member 1
    2012-08-16 15:15:25.630/3.562 Oracle Coherence GE 12.1.2.0 <D5> (thread=Cluster, member=n/a): Member 1 joined Service PartitionedPofCache with senior member 1
    2012-08-16 15:15:25.630/3.562 Oracle Coherence GE 12.1.2.0 <Info> (thread=main, member=n/a): Started cluster Name=cluster:0x47DB
     
    Group{Address=224.12.1.0, Port=3155, TTL=4}
     
    MasterMemberSet(
      ThisMember=Member(Id=2, Timestamp=2012-08-16 15:15:25.193, Address=130.35.99.202:8090, MachineId=18578, Location=site:,machine:tpfaeffl-lap7,process:4572, Role=OracleHandsonContactDriver)
      OldestMember=Member(Id=1, Timestamp=2012-08-16 15:09:29.365, Address=130.35.99.202:8088, MachineId=18578, Location=site:,machine:tpfaeffl-lap7,process:5792, Role=CoherenceServer)
      ActualMemberSet=MemberSet(Size=2
        Member(Id=1, Timestamp=2012-08-16 15:09:29.365, Address=130.35.99.202:8088, MachineId=18578, Location=site:,machine:tpfaeffl-lap7,process:5792, Role=CoherenceServer)
        Member(Id=2, Timestamp=2012-08-16 15:15:25.193, Address=130.35.99.202:8090, MachineId=18578, Location=site:,machine:tpfaeffl-lap7,process:4572, Role=OracleHandsonContactDriver)
        )
      MemberId|ServiceVersion|ServiceJoined|MemberState
        1|12.1.2|2012-08-16 15:09:29.365|JOINED,
        2|12.1.2|2012-08-16 15:15:25.193|JOINED
      RecycleMillis=1200000
      RecycleSet=MemberSet(Size=0
        )
      )
     
    TcpRing{Connections=[1]}
    IpMonitor{Addresses=0}
     
    2012-08-16 15:15:25.755/3.687 Oracle Coherence GE 12.1.2.0 <D5> (thread=Invocation:Management, member=2): Service Management joined the cluster with senior service member 1
    2012-08-16 15:15:25.849/3.781 Oracle Coherence GE 12.1.2.0 <Info> (thread=main, member=2): Loaded Reporter configuration from "jar:file:/C:/oracle/Middleware/Oracle_Home/coherence/lib/coherence.jar!/reports/report-group.xml"
    2012-08-16 15:15:26.224/4.156 Oracle Coherence GE 12.1.2.0 <Info> (thread=DistributedCache:PartitionedPofCache, member=2): Loaded POF configuration from "file:/C:/home/oracle/workspace/Contacts/build/classes/contacts-pof-config.xml"
    2012-08-16 15:15:26.302/4.234 Oracle Coherence GE 12.1.2.0 <Info> (thread=DistributedCache:PartitionedPofCache, member=2): Loaded included POF configuration from "jar:file:/C:/oracle/Middleware/Oracle_Home/coherence/lib/coherence.jar!/coherence-pof-config.xml"
    2012-08-16 15:15:26.427/4.359 Oracle Coherence GE 12.1.2.0 <D5> (thread=DistributedCache:PartitionedPofCache, member=2): Service PartitionedPofCache joined the cluster with senior service member 1
    They are the same!!
    
  5. If you go back and look at the cache server output, you can see messages reporting the Contacts cache client joining the cluster, completing its work, then leaving the cluster. See Example 4-9.

    Example 4-9 Contacts Cache Server Displaying the Arrival and Departure of the Contacts Client

    ...
    Started DefaultCacheServer...
    
    2012-08-16 15:15:25.396/360.734 Oracle Coherence GE 12.1.2.0 <D5> (thread=Cluster, member=1): Member(Id=2, Timestamp=2012-08-16 15:15:25.193, Address=130.35.99.202:8090, MachineId=18578, Location=site:,machine:tpfaeffl-lap7,process:4572, Role=OracleHandsonContactDriver) joined Cluster with senior member 1
    2012-08-16 15:15:25.755/361.093 Oracle Coherence GE 12.1.2.0 <D5> (thread=Cluster, member=1): Member 2 joined Service Management with senior member 1
    2012-08-16 15:15:26.490/361.828 Oracle Coherence GE 12.1.2.0 <D5> (thread=Cluster, member=1): Member 2 joined Service PartitionedPofCache with senior member 1
    2012-08-16 15:15:26.646/361.984 Oracle Coherence GE 12.1.2.0 <D5> (thread=Cluster, member=1): TcpRing disconnected from Member(Id=2, Timestamp=2012-08-16 15:15:25.193, Address=130.35.99.202:8090, MachineId=18578, Location=site:,machine:tpfaeffl-lap7,process:4572, Role=OracleHandsonContactDriver) due to a peer departure; removing the member.
    2012-08-16 15:15:26.646/361.984 Oracle Coherence GE 12.1.2.0 <D5> (thread=Cluster, member=1): Member 2 left service Management with senior member 1
    2012-08-16 15:15:26.646/361.984 Oracle Coherence GE 12.1.2.0 <D5> (thread=Cluster, member=1): Member 2 left service PartitionedPofCache with senior member 1
    2012-08-16 15:15:26.646/361.984 Oracle Coherence GE 12.1.2.0 <D5> (thread=Cluster, member=1): Member(Id=2, Timestamp=2012-08-16 15:15:26.646, Address=130.35.99.202:8090, MachineId=18578, Location=site:,machine:tpfaeffl-lap7,process:4572, Role=OracleHandsonContactDriver) left Cluster with senior member 1