3 Leaving Strings Behind

In this chapter, you work with complex objects residing in the cache. Using JDeveloper, you will create a new Person class that is serializable, store and retrieve Person objects in the cache, and serialize your own objects.

This chapter contains the following sections:

3.1 Introduction

Until now you have been putting and getting String objects as the value in a NamedCache. 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 potentially be used as a value or key. This enables you to store complex objects as values in the cache.

Because Coherence may need to 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 them into a live object at some future time. For example, objects that implement the java.io.Serializable interface are serializable.

Coherence supplies its own class for high-performance serialization called com.tangosol.io.pof.PortableObject. This is up to six times faster than the standard Serializable and the serialized result set is smaller.

3.2 Creating and Caching Complex Objects

In this exercise you will create a Person object that has attributes of various data types, and a program that uses Java Serialization to put the object into the cache and retrieve it.

To create complex objects and place them in the cache:

  1. Create a Person class to store in the cache.

    1. Create a new project in JDeveloper called Lab4. Hint: Right-click the Coherence application and choose New... In the New Gallery choose Projects under Categories and Generic Project under Items. Click OK.

      Figure 3-1 Creating a New Project in the New Gallery

      Creating a New Project in the New Gallery
      Description of "Figure 3-1 Creating a New Project in the New Gallery"

    2. Name the project Lab4 and select Java from Project Technologies. Click Next.

      Figure 3-2 Naming the New Project

      Naming the New Project
      Description of "Figure 3-2 Naming the New Project"

    3. Ensure that the Default Package is com.oracle.coherence.handson, the Java Source Path is C:\home\oracle\labs\Lab4\src and the Output Directory is C:\home\oracle\labs\Lab4\classes. Click Finish.

      Figure 3-3 Configuring Java Settings for the New Project

      Configuring Java Settings for the New Project
      Description of "Figure 3-3 Configuring Java Settings for the New Project"

    4. Create a new Java class called Person. Right click the Lab4 project and select New.... In the New Gallery, choose Java under General and Java Class under Items. Click OK.

      Figure 3-4 Creating the Java Class

      Creating the Java Class in the New Gallery
      Description of "Figure 3-4 Creating the Java Class"

    5. Name the Java class Person. Do not select the Generate Main Method check box. Click OK.

      Figure 3-5 Naming the Java Class

      Naming the Java Class
      Description of "Figure 3-5 Naming the Java Class"

    6. In the JDeveloper code editor, change your class to implement java.io.Serializable. Hint: Use implements java.io.Serializable. Add an import statement for the java.io.Serializable class.

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

      int id

      String lastname

      String firstname

      String address

      int age

      String gender

      The Person class should look similar to the image in Figure 3-6.

      Figure 3-6 Person Class in the JDeveloper Code Editor

      Person Class in the JDeveloper Code Editor
      Description of "Figure 3-6 Person Class in the JDeveloper Code Editor "

    8. JDeveloper can generate the default get/set methods for your attributes. From the Source menu, select Generate Accessors. Select the check box next to the Person class. All of the attributes are now automatically selected. Click OK to continue.

      Figure 3-7 Generating Accessors for the Java Class Attributes

      Generating Accessors for the Java Class Attributes
      Description of "Figure 3-7 Generating Accessors for the Java Class Attributes"

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

      Figure 3-8 Generating Constructors for the Java Class

      Generating Constructors for the Java Class
      Description of "Figure 3-8 Generating Constructors for the Java Class"

    10. From the Source menu, select Generate equals () and hashCode(), click Select All, and then click OK.

      Figure 3-9 Generating equals() and hashCode() Methods

      Generating equals() and hashCode() Methods
      Description of "Figure 3-9 Generating equals() and hashCode() Methods"

    11. Change the Java process to be storage-disabled when you run it. right click the project and select Project Properties. In the Project Properties dialog box select Run/Debug/Profile and click Edit. Set Virtual Machine to Server and enter the following values for local storage and log level in the Java Options field of the Edit Run Configuration dialog box.

      -Dtangosol.coherence.distributed.localstorage=false  -Dtangosol.coherence.log.level=3
      

      Click OK in the Edit Run Configuration dialog box and OK in the Project Properties dialog box to return to the JDeveloper IDE.

      Figure 3-10 Setting Java Runtime Options

      Setting Java Runtime Options
      Description of "Figure 3-10 Setting Java Runtime Options"

  2. Create a driver class called PersonExample to put and get your Person class.

    1. Create a new Java class called PersonExample in the Lab4 project. Right click the Lab4 project and select New.... In the New Gallery, choose Java under General and Java Class under Items. Click OK.

    2. Name the Java class PersonExample. Select the Generate Main Method check box. Click OK.

      Figure 3-11 Creating a Java Class with a main() Method

      Creating a Java Class with a main() Method
      Description of "Figure 3-11 Creating a Java Class with a main() Method"

    3. Create a new NamedCache called person and put a new instance of the Person class in it.

    4. Get the person object from the cache and ensure that the two objects are identical. Example 3-1 illustrates a sample program.

      Example 3-1 Comparing an Object from the Cache with the Original

      public static void main(String[] args) {
         NamedCache person = CacheFactory.getCache("person");
      
         Person p1 = new Person(1,"Middleton","Tim", 
         "Level 2, 66 Kings Park Road, West Perth", 
         39,Person.MALE);
      
         person.put(p1.getId(),p1);
      
         Person p2 = (Person)person.get(p1.getId());
      
         if (p2.equals(p1)) {
         System.out.println("They are the same!!");
         } 
      }
      

      Note:

      You will observe the following error: Type or field 'MALE' not found in Person. You will resolve this in the next step.
    5. Open Person.java and add the following lines to initialize the gender variable.

      static String MALE="M"; 
      static String FEMALE="F";
      

      Add these two lines right after where you declared these attributes:

      private int id; 
      private String lastname; 
      private String firstname; 
      private Stringaddress; 
      private intage; 
      private String gender;
      
    6. Start the cache server.

      C:\oracle\product\coherence\bin>cache-server.cmd 
      
    7. Execute PersonExample and view the result.

      Figure 3-12 Results of Running PersonExample.java File

      Results of Running PersonExample.java File
      Description of "Figure 3-12 Results of Running PersonExample.java File "

  3. Implement faster serialization: change the Person class to implement the Coherence PortableObject class.

    1. Edit the Person class in JDeveloper and change import java.io.Serializable to import com.tangosol.io.pof.PortableObject. Change implements Serializable to implements PortableObject. Note that the Person class now appears with a red underline. If you place your cursor on this, JDeveloper tells you what the problem is.

      Methods not implemented: readExternal(PofReader) writeExternal(PofWriter)
      

      Using the PortableObject class requires that you implement its readExternal(PofReader) and writeExternal(writeExternal) methods.

    2. Right-click Person and choose Code Assist. Select Implement Methods. The Implement Methods dialog box should appear. This generates the required readExternal and writeExternal methods. Accept the defaults and click OK.

      Figure 3-13 Implement Methods Dialog Box

      Implement Methods Dialog Box
      Description of "Figure 3-13 Implement Methods Dialog Box"

    3. Make the following changes to the Person class. (You may need to import java.io.IOException.)

      Figure 3-14 Implementing the PortableObject.readExternal and writeExternal Methods

      Description of Figure 3-14 follows
      Description of "Figure 3-14 Implementing the PortableObject.readExternal and writeExternal Methods"

    4. Save the changes and rebuild your application.

  4. Create a POF configuration file for person.

    1. Create a copy of coherence-pof-config.xml and name it lab4-pof-config.xml.

      Note:

      If the coherence-pof-config.xml file is not already present in your file system, you can find a copy of it in the coherence.jar file.
      cp coherence-pof-config.xml lab4-pof-config.xml
      
    2. Edit lab4-pof-config.xml and add a <user-type> element for Person to the <user-type-list> section. Use 1001 as the value for the <type-id> subelement and the full class name for Person in the <class-name> subelement. This is illustrated in Example 3-2.

      Example 3-2 New <user-type> Element in POF Configuration File

       ...
       <user-type-list>
          ...
          <user-type>
            <type-id>1001</type-id>
            <class-name>com.oracle.coherence.handson.Person</class-name>
          </user-type> 
      </user-type-list>
      ...
      
    3. Save the lab4-pof-config.xml file in the \home\oracle\labs directory.

    4. Create a copy of coherence-cache-config.xml and name it lab4-cache-config.xml.

      Note:

      If the coherence-cache-config.xml file is not already present in your file system, you can find a copy of it in the coherence.jar file.
      cp coherence-cache-config.xml lab4-cache-config.xml
      
    5. Edit lab4-cache-config.xml and un-comment the serializer section for the example-distributed caching scheme. This is illustrated in Example 3-3.

      Example 3-3 Serializer Section in the Cache Configuration File

      ...
      <caching-schemes>
          <!--
          Distributed caching scheme.
          -->
          <distributed-scheme>
            <scheme-name>example-distributed</scheme-name>
            <service-name>DistributedCache</service-name>
      
            <!-- To use POF serialization for this partitioned service, 
                 uncomment the following section -->
          
            <serializer>
              <class-name>com.tangosol.io.pof.ConfigurablePofContext</class-name>
            </serializer>     
      
      ...
      
    6. Add an <init-param> element to the serializer passing the name lab4-pof-config.xml.

      Example 3-4 <init-param> Section in the Cache Configuration File

         ...
            <serializer>
              <class-name>com.tangosol.io.pof.ConfigurablePofContext</class-name>
              <init-params>
                <init-param>
                  <param-value>lab4-pof-config.xml</param-value>
                  <param-type>String</param-type>
                </init-param>
              </init-params>
            </serializer>     
      ...
      
  5. Stop all cache servers.

  6. Copy cache-server.cmd to pof-cache-server.cmd and save the file in the \oracle\product\coherence\bin directory.

    cd /oracle/product/coherence/bin 
    cp cache-server.cmd pof-cache-server.cmd
    
  7. Edit pof-cache-server.cmd to point to the lab4-cache-config.xml file and \home\oracle\labs directory to the classpath. This is to ensure that the cache can find lab4-pof-config.xml file at runtime.

    1. Add \home\oracle\labs to the classpath.

    2. Add the location of Person.class to your classpath (\home\oracle\labs\Lab4\classes)

    3. Add COHERENCE_OPTS="-Dtangosol.coherence.cacheconfig=\home\oracle\labs\lab4-cache-config.xml"

    4. Add COHERENCE_OPTS to the JAVAEXEC command line. The resulting pof-cache-server.cmd file should look similar to this:

      Example 3-5 Sample pof-cache-config.cmd File

      @echo off
      @
      @rem This will start a cache server
      @
      setlocal
      
      :config
      @rem specify the Coherence installation directory
      set coherence_home=c:\oracle\product\coherence
      
      @rem specify the JVM heap size
      set memory=512m
      
      :start
      if not exist "%coherence_home%\lib\coherence.jar" goto instructions
      
      if "%java_home%"=="" (set java_exec=java) else (set java_exec=%java_home%\bin\java)
      
      :launch
      
      set java_opts="-Xms%memory% -Xmx%memory%"
      
      set coherence_opts="-Dtangosol.coherence.log.level=9 
      
      -Dtangosol.coherence.cacheconfig=C:/home/oracle/labs/lab4-cache-config.xml"
      
      "%java_exec%" -server -showversion "%java_opts%" "%coherence_opts%" -cp 
      
      "%coherence_home%\lib\coherence.jar;C:\home\oracle\labs\Lab4\classes;C:\home\oracle\labs" 
      
      com.tangosol.net.DefaultCacheServer %1
      
      goto exit
      
      :instructions
      
      echo Usage:
      echo   ^<coherence_home^>\bin\pof-cache-server.cmd
      goto exit
      
      :exit
      endlocal
      @echo on
      
  8. Start the cache server by executing the following command:

    C:\oracle\product\coherence\bin>pof-cache-server.cmd 
    

    Example 3-6 lists sample output from running pof-cache-server.cmd.

    Example 3-6 Starting the POF Cache Server

    C:\oracle\product\coherence\bin>pof-cache-server.cmdjava version "1.6.0_05"Java(TM) SE Runtime Environment (build 1.6.0_05-b13)Java HotSpot(TM) Server VM (build 10.0-b19, mixed mode)2008-12-16 18:20:05.665/0.547 Oracle Coherence 3.4.1/407 <Info> (thread=main, member=n/a): Loaded operational configuration from resource "jar:file:/C:/oracle/product/coherence/lib/coherence.jar!/tangosol-coherence.xml"2008-12-16 18:20:05.681/0.563 Oracle Coherence 3.4.1/407 <Info> (thread=main, member=n/a): Loaded operational overrides from resource "jar:file:/C:/oracle/product/coherence/lib/coherence.jar!/tangosol-coherence-override-dev.xml"2008-12-16 18:20:05.681/0.563 Oracle Coherence 3.4.1/407 <D5> (thread=main, member=n/a): Optional configuration override "/tangosol-coherence-override.xml" is not specified2008-12-16 18:20:05.697/0.579 Oracle Coherence 3.4.1/407 <D5> (thread=main, member=n/a): Optional configuration override "/custom-mbeans.xml" is not specified2008-12-16 18:20:05.697/0.579 Oracle Coherence 3.4.1/407 <D6> (thread=main, member=n/a): Loaded edition data from "jar:file:/C:/oracle/product/coherence/lib/coherence.jar!/coherence-grid.xml"Oracle Coherence Version 3.4.1/407 Grid Edition: Development modeCopyright (c) 2000-2008 Oracle. All rights reserved.2008-12-16 18:20:06.400/1.282 Oracle Coherence GE 3.4.1/407 <Info> (thread=main, member=n/a): Loaded cache configuration from file "C:\home\oracle\labs\lab4-cache-config.xml"2008-12-16 18:20:07.322/2.204 Oracle Coherence GE 3.4.1/407 <D5> (thread=Cluster, member=n/a): Service Cluster joined the cluster with senior service member n/a2008-12-16 18:20:07.540/2.422 Oracle Coherence GE 3.4.1/407 <Info> (thread=Cluster, member=n/a): This Member(Id=3, Timestamp=2008-12-16 18:20:07.322, Address=130.35.99.248:8089, MachineId=49912, Location=site:us.oracle.com,machine:tpfaeffl-pc,process:5564, Role=CoherenceServer, Edition=Grid Edition, Mode=Development, CpuCount=1, SocketCount=1) joined cluster "cluster:0x23CB" with senior Member(Id=1, Timestamp=2008-12-16 13:01:46.572, Address=130.35.99.248:8088, MachineId=49912, Location=site:us.oracle.com,machine:tpfaeffl-pc,process:1592, Role=CoherenceConsole, Edition=Grid Edition, Mode=Development, CpuCount=1, SocketCount=1)2008-12-16 18:20:07.837/2.719 Oracle Coherence GE 3.4.1/407 <D5> (thread=DistributedCache, member=3): Service DistributedCache joined the cluster with senior service member 32008-12-16 18:20:07.868/2.750 Oracle Coherence GE 3.4.1/407 <Info> (thread=DistributedCache, member=3): Loading POF configuration from resource "file:/C:/home/o
    
    racle/labs/lab4-pof-config.xml"
    2008-12-16 18:20:07.884/2.766 Oracle Coherence GE 3.4.1/407 <Info> (thread=Distr
    ibutedCache, member=3): Loading POF configuration from resource "jar:file:/C:/or
    acle/product/coherence/lib/coherence.jar!/coherence-pof-config.xml"
    2008-12-16 18:20:08.337/3.219 Oracle Coherence GE 3.4.1/407 <D5> (thread=Replica
    tedCache, member=3): Service ReplicatedCache joined the cluster with senior serv
    ice member 3
    2008-12-16 18:20:08.353/3.235 Oracle Coherence GE 3.4.1/407 <D5> (thread=Optimis
    ticCache, member=3): Service OptimisticCache joined the cluster with senior serv
    ice member 3
    2008-12-16 18:20:08.384/3.266 Oracle Coherence GE 3.4.1/407 <D5> (thread=Invocat
    ion:InvocationService, member=3): Service InvocationService joined the cluster w
    ith senior service member 3
    2008-12-16 18:20:08.384/3.266 Oracle Coherence GE 3.4.1/407 <Info> (thread=main,
     member=3): Started DefaultCacheServer...
     
    SafeCluster: Name=cluster:0x23CB
     
    Group{Address=224.3.4.1, Port=34407, TTL=4}
     
    MasterMemberSet
      (
      ThisMember=Member(Id=3, Timestamp=2008-12-16 18:20:07.322, Address=130.35.99.2
    48:8089, MachineId=49912, Location=site:us.oracle.com,machine:tpfaeffl-pc,proces
    s:5564, Role=CoherenceServer)
      OldestMember=Member(Id=1, Timestamp=2008-12-16 13:01:46.572, Address=130.35.99
    .248:8088, MachineId=49912, Location=site:us.oracle.com,machine:tpfaeffl-pc,proc
    ess:1592, Role=CoherenceConsole)
      ActualMemberSet=MemberSet(Size=2, BitSetCount=2
        Member(Id=1, Timestamp=2008-12-16 13:01:46.572, Address=130.35.99.248:8088,
    MachineId=49912, Location=site:us.oracle.com,machine:tpfaeffl-pc,process:1592, R
    ole=CoherenceConsole)
        Member(Id=3, Timestamp=2008-12-16 18:20:07.322, Address=130.35.99.248:8089,
    MachineId=49912, Location=site:us.oracle.com,machine:tpfaeffl-pc,process:5564, R
    ole=CoherenceServer)
        )
      RecycleMillis=120000
      RecycleSet=MemberSet(Size=0, BitSetCount=0
        )
      )
    
    Services  (  TcpRing{TcpSocketAccepter{State=STATE_OPEN, ServerSocket=130.35.99.248:8089},Connections=[]}  ClusterService{Name=Cluster, State=(SERVICE_STARTED, STATE_JOINED), Id=0, Version=3.4, OldestMemberId=1}  DistributedCache{Name=DistributedCache, State=(SERVICE_STARTED), LocalStorage=enabled, PartitionCount=257, BackupCount=1, AssignedPartitions=257, BackupPartitions=0}  ReplicatedCache{Name=ReplicatedCache, State=(SERVICE_STARTED), Id=2, Version=3.0, OldestMemberId=3}  Optimistic{Name=OptimisticCache, State=(SERVICE_STARTED), Id=3, Version=3.0, OldestMemberId=3}  InvocationService{Name=InvocationService, State=(SERVICE_STARTED), Id=4, Version=3.1, OldestMemberId=3}  )2008-12-16 18:20:09.087/3.969 Oracle Coherence GE 3.4.1/407 <D5> (thread=TcpRingListener, member=3): TcpRing: connecting to member 1 using TcpSocket{State=STATE_OPEN, Socket=Socket[addr=/130.35.99.248,port=4044,localport=8089]}
    
  9. Try to run the POF cache server in JDeveloper. To do this, add additional CLASSPATH entries to the existing project properties. Navigate to Tools > Project Properties > Libraries and Classpath. Click Add JAR/Directory. In your project, add \home\oracle\labs to your CLASSPATH.

    Figure 3-15 Adding Labs and Configuration Files to the Classpath

    Adding Labs and Configuration Files to the Classpath
    Description of "Figure 3-15 Adding Labs and Configuration Files to the Classpath"

  10. Click Run/Debug/Profile to edit the runtime properties. Append the following line to the existing Java Options.

    -Dtangosol.coherence.cacheconfig=C:\home\oracle\labs\lab4-cache-config.xml 
    

    Click OK to save your changes to the runtime configuration and OK again to dismiss the Project Properties dialog box.

    Figure 3-16 Setting the Runtime Profile

    Setting the Runtime Profile
    Description of "Figure 3-16 Setting the Runtime Profile"

  11. Rerun PersonExample.java from the JDeveloper IDE to ensure that it works.

    In this example, the Person object is converted to POF at run time instead of the standard Java serialized format. Using POF should provide significant performance improvements in terms of CPU time and the size of the binary generated.

    Figure 3-17 PersonExample Output Run from JDeveloper

    PersonExample Output Run from JDeveloper
    Description of "Figure 3-17 PersonExample Output Run from JDeveloper"

3.3 Caching a Complex Object using Java Serialization

In this practice, you create a simple domain object that can be placed into a Coherence cache. This practice assumes that you have a good understanding of Java language serialization rules.

Coherence requires that any non-primitive object placed into its cache be Java-Serializable. This is because the objects may need to be transported across process boundaries, that is, between JVMs, across networks, or processes on the same physical machine. The standard way to transport an object is to serialize, transmit, and then deserialize it. Coherence requires objects to be placed in a cache for them to be serializable.

  1. Create a Java class called EndOfDayStockSummary that implements the java.io.Serializable interface.

    The purpose of this file is to capture the open, high, low, close, and adjusted close prices (in dollars) of a stock (called a symbol) for a specific date, with the trading volume. The variables in Example 3-7 can be the attribute definitions for the class:

    Example 3-7 Attributes for a Sample Serializable Class

    private String symbol 
    private long date 
    private double openPrice 
    private double highPrice 
    private double lowPrice 
    private double closePrice 
    private double adjustedClosePrice 
    private long volume
    
    1. Define a method called getKey() to return a String that is a combination of the symbol and date attributes.

    2. Declare serialVersionUID.

    3. Define a public default constructor EndOfDayStockSummary().

    4. Define a public constructor that accepts values for all the attributes specified.

      Example 3-8 illustrates a sample solution.

      Example 3-8 Sample Domain Object for the Coherence Cache

      package com.oracle.coherence.handson;
      
      
      import java.io.Serializable;
      import java.util.Date;
      
      public class EndOfDayStockSummary implements Serializable {
      
          private static final long serialVersionUID = -200684451000777011L;
          
          private String symbol;
          private long date;
          private double openPrice;
          private double highPrice;
          private double lowPrice;
          private double closePrice;
          private double adjustedClosePrice;
          private long volume;
          
          public EndOfDayStockSummary() {
              //for Java Serialization.  not to be called directly by application code
          }
          
          
          public EndOfDayStockSummary(String symbol, 
                                       long date, 
                                       double openPrice,
                                       double highPrice, 
                                       double lowPrice, 
                                       double closePrice,
                                       double adjustedClosePrice, 
                                       long volume) {
              this.symbol = symbol;
              this.date = date;
              this.openPrice = openPrice;
              this.highPrice = highPrice;
              this.lowPrice = lowPrice;
              this.closePrice = closePrice;
              this.adjustedClosePrice = adjustedClosePrice;
              this.volume = volume;
          }
              
          
          public String getKey() {
              return symbol + date;
          }
          
          
          public String getSymbol() {
              return symbol;
          }
      
          public long getDate() {
              return date;
          }
          
      
          public double getOpenPrice() {
              return openPrice;
          }
      
          public double getHighPrice() {
              return highPrice;
          }
      
          public double getLowPrice() {
              return lowPrice;
          }
      
          public double getClosePrice() {
              return closePrice;
          }
          
          
          public double getAdjustedClosePrice() {
              return adjustedClosePrice;
          }
      
          public long getVolume() {
              return volume;
          }
      
          public String toString() {
              return String.format("EndOfDayPrice{symbol=%s, date=%s, open=%f, high=%f, low=%f, close=%f, adj-close=%f, volume=%d}", 
                                    symbol,
                                    new Date(date),
                                    openPrice,
                                    highPrice,
                                    lowPrice, 
                                    closePrice,
                                    adjustedClosePrice,
                                    volume);
          }
      }
      
  2. Create a console application called CacheAnObject which contains a main method.

    In the application, create an instance of the EndOfDayStockSummary class and use the NamedCache put method to place it into a Coherence cache called dist-eodStockSummaries. The application should then retrieve the summery and display it in the console.

    Example 3-9 illustrates a sample solution.

    Example 3-9 Sample Console Application

    import java.util.Date;
    
    import com.tangosol.net.CacheFactory;
    import com.tangosol.net.NamedCache;
    
    public class CacheAnObject {
    
        public static void main(String[] args) {
            CacheFactory.ensureCluster();
            
            NamedCache namedCache = CacheFactory.getCache("dist-eodStockSummaries");
            
            EndOfDayStockSummary eodStockSummary = new EndOfDayStockSummary("orcl", 
                                                            new Date().getTime(), 
                                                            25.00,
                                                            27.00,
                                                            24.00,
                                                            26.00,
                                                            26.00,
                                                            1000); 
            
            namedCache.put(eodStockSummary.getKey(), eodStockSummary);
            
            System.out.println(namedCache.get(eodStockSummary.getKey()));
            
            CacheFactory.shutdown();
        }
    
    }
    
  3. Edit the Lab4 Project Properties and modify the Run/Debug/Profile configuration. Remove the following line from the existing Java Options if it is present.

    -Dtangosol.coherence.cacheconfig=\home\oracle\labs\lab4-cache-config.xml 
    

    This field should still contain the following lines:

    -Dtangosol.coherence.distributed.localstorage=false -Dtangosol.coherence.log.level=3
    

    Figure 3-18 Project Properties for the EndOfDayStockSummary Application

    Project Properties for EndOfDayStockOptions Application
    Description of "Figure 3-18 Project Properties for the EndOfDayStockSummary Application"

  4. Edit cache-server.cmd and add the following:

    1. Add \home\oracle\labs to the classpath.

    2. Add the location of Person.class to your classpath (\home\oracle\labs\Lab4\classes)

  5. Ensure that all other cache servers are shut down and execute cache-server.cmd. Ensure that the cache server starts successfully, then shut it down.

  6. Execute CacheAnObject from the JDeveloper IDE and observe the results.

    Figure 3-19 Output from the CacheAnObject Console Application

    Output from the Console Application
    Description of "Figure 3-19 Output from the CacheAnObject Console Application "

3.4 Caching a Complex Object using Coherence PortableObject

In this exercise, you improve the serialization performance of domain objects placed in the Coherence cache using the com.tangosol.io.pof.PortableObject interface.

This exercise assumes that you have successfully completed the previous section, "Caching a Complex Object using Java Serialization".

Standard Java serialization performance is often a significant bottleneck for applications that communicate across process boundaries, especially those that depend on networks. Java serialization also produces serialization streams that are often very verbose in their binary format, typically containing much more information than required by an application. It is also possible that the streams are encoded in such a manner that it is inefficient to send across a network, or to construct and destruct in a Java heap (memory). This consequently leads to increased garbage collection requirements.

To resolve some of these issues, including dramatically improving serialization performance, reducing the binary format size, and reducing the impact on garbage collection, Coherence provides its own proprietary serialization known as the PortableObject format. The PortableObject interface introduces two simple methods, readExternal and writeExternal, that permit a developer 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 dramatically improve the performance of the process. Using POF will dramatically reduce the size of the resulting binary. The size of the binary is often 5 to 10x smaller, and the conversion to-or-from the binary can be between 5 and 20 times faster, depending on the size of the object.

Note:

Properties in POF are indexed and they should be read from the same index they were written to.

To implement PortableObject serialization format:

  1. Modify the EndOfDayStockSummary class from the previous exercise to implement the com.tangosol.io.pof.PortableObject interface.

  2. Using the methods defined in com.tangosol.io.pof.PofReader class and com.tangosol.io.pof.PofWriter the non-static methods defined in the DataInput and DataOutput streams, implement the readExternal and writeExternal methods.

    Example 3-10 Sample PortableObject Implementation

    package com.oracle.coherence.handson;
    
    import java.io.IOException;
    
    import com.tangosol.io.pof.PofReader;
    import com.tangosol.io.pof.PofWriter;
    import com.tangosol.io.pof.PortableObject;
    import java.util.Date;
    
    public class EndOfDayStockSummary implements PortableObject {
    
        private static final long serialVersionUID = -200684451000777011L;
        
        private String symbol;
        private long date;
        private double openPrice;
        private double highPrice;
        private double lowPrice;
        private double closePrice;
        private double adjustedClosePrice;
        private long volume;
    
        
        public EndOfDayStockSummary() {
            //for Java Serialization.  not to be called directly by application code
        }
        
        
        public EndOfDayStockSummary(String symbol, 
                                     long date, 
                                     double openPrice,
                                     double highPrice, 
                                     double lowPrice, 
                                     double closePrice,
                                     double adjustedClosePrice, 
                                     long volume) {
            this.symbol = symbol;
            this.date = date;
            this.openPrice = openPrice;
            this.highPrice = highPrice;
            this.lowPrice = lowPrice;
            this.closePrice = closePrice;
            this.adjustedClosePrice = adjustedClosePrice;
            this.volume = volume;
        }
            
        
        public String getKey() {
            return symbol + date;
        }
        
        
        public String getSymbol() {
            return symbol;
        }
    
        public long getDate() {
            return date;
        }
        
        public double getOpenPrice() {
            return openPrice;
        }
    
        public double getHighPrice() {
            return highPrice;
        }
    
        public double getLowPrice() {
            return lowPrice;
        }
    
        public double getClosePrice() {
            return closePrice;
        }
        
        
        public double getAdjustedClosePrice() {
            return adjustedClosePrice;
        }
    
        public long getVolume() {
            return volume;
        }
    
        public String toString() {
            return String.format("EndOfDayPrice{symbol=%s, date=%s, open=%f, high=%f, low=%f, close=%f, adj-close=%f, volume=%d}", 
                                  symbol,
                                  new Date(date),
                                  openPrice,
                                  highPrice,
                                  lowPrice, 
                                  closePrice,
                                  adjustedClosePrice,
                                  volume);
        }
    
        public void readExternal(PofReader pofReader) throws IOException {
            symbol=pofReader.readString(0);
            date=pofReader.readLong(1);
            openPrice=pofReader.readDouble(2);
            lowPrice=pofReader.readDouble(3);
            highPrice=pofReader.readDouble(4);
            closePrice=pofReader.readDouble(5);
            adjustedClosePrice=pofReader.readDouble(6);
            volume=pofReader.readLong(7);
            
        }
    
        public void writeExternal(PofWriter pofWriter) throws IOException {
            pofWriter.writeString(0, symbol);
            pofWriter.writeLong(1, date);
            pofWriter.writeDouble(2, openPrice);
            pofWriter.writeDouble(3, highPrice);
            pofWriter.writeDouble(4, lowPrice);
            pofWriter.writeDouble(5, closePrice);
            pofWriter.writeDouble(6, adjustedClosePrice);
            pofWriter.writeLong(7, volume);
            
        }
    }
    
  3. Modify lab4-pof-config.xml in \home\oracle\labs to have an entry for EndOfDayStockSummary. Hint: You can change <type-id> to 1002 and <class-name> to com.oracle.coherence.handson.EndOfDayStockSummary.

  4. Edit the Lab4 Project Properties and modify the Run/Debug/Profile configuration. Append the following line to the existing Java Options:

    -Dtangosol.coherence.cacheconfig=\home\oracle\labs\lab4-cache-config.xml
    

    Figure 3-20 Setting Java Options for the PortableObject Implementation

    Setting Java Options for PortableObject Implementation
    Description of "Figure 3-20 Setting Java Options for the PortableObject Implementation"

  5. Save the changes and rebuild your application.

  6. Ensure that all other cache servers are shut down and execute pof-cache-server.cmd on the command line

    C:\oracle\product\coherence\bin>pof-cache-server.cmd
    

    In the output from the server startup, you should see lines similar to those in Example 3-11 that indicate that a POF configuration is being employed.

    Example 3-11 Output from Starting the Server with a POF Configuration

    ...
    2008-12-17 18:47:12.900/3.407 Oracle Coherence GE 3.4.1/407 <Info> (thread=Distr
    ibutedCache, member=2): Loading POF configuration from resource "file:/C:/home/o
    racle/labs/lab4-pof-config.xml"
    2008-12-17 18:47:12.931/3.438 Oracle Coherence GE 3.4.1/407 <Info> (thread=Distr
    ibutedCache, member=2): Loading POF configuration from resource "jar:file:/C:/or
    acle/product/coherence/lib/coherence.jar!/coherence-pof-config.xml"
    2008-12-17 18:47:13.431/3.938 Oracle Coherence GE 3.4.1/407 <D5> (thread=Replica
    tedCache, member=2): Service ReplicatedCache joined the cluster with senior serv
    ice member 2
    2008-12-17 18:47:13.462/3.969 Oracle Coherence GE 3.4.1/407 <D5> (thread=Optimis
    ticCache, member=2): Service OptimisticCache joined the cluster with senior serv
    ice member 2
    2008-12-17 18:47:13.478/3.985 Oracle Coherence GE 3.4.1/407 <D5> (thread=Invocat
    ion:InvocationService, member=2): Service InvocationService joined the cluster w
    ith senior service member 2
    2008-12-17 18:47:13.478/3.985 Oracle Coherence GE 3.4.1/407 <Info> (thread=main,
     member=2): Started DefaultCacheServer...
    ...
    
  7. Verify that your CacheAnObject.java class from the previous exercise uses the EndOfDayStockSummary that implemented PortableObject. Run CacheAnObject from the JDeveloper IDE.

    Figure 3-21 Running EndOfDayStockSummary with a PortableObject Implementation

    Running application with PortableObject Implementation
    Description of "Figure 3-21 Running EndOfDayStockSummary with a PortableObject Implementation"

It is important to keep the order of the attributes and their indexes consistent between the readExternal and writeExternal methods. As an experiment, try changing the index of the object attributes written in the writeExternal method to be different from the index of the reading object attributes in the readExternal method.

For example, change the order of the openPrice and lowPrice attributes and indexes as they currently appear in the writeExternal method:

...
pofWriter.writeDouble(2, openPrice);
pofWriter.writeDouble(3, highPrice);
pofWriter.writeDouble(4, lowPrice);
...

to this order:

...
pofWriter.writeDouble(2, lowPrice);
pofWriter.writeDouble(3, highPrice);
pofWriter.writeDouble(4, openPrice);
...

Run CacheAnObject.java again. Notice that the assigned values in the output are transposed.