Skip Headers
Oracle® Coherence Tutorial for Oracle Coherence
Release 3.6

Part Number E15831-01
Go to Documentation Home
Home
Go to Book List
Book List
Go to Table of Contents
Contents
Go to Index
Index
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
View PDF

5 Loading Data Into a Cache

In this chapter, you will learn how to populate a Coherence cache with domain objects that are read from text files.

This chapter contains the following sections:

5.1 Introduction

Until now, you have been putting and retrieving objects individually. Each put can result in increased network traffic, especially for partitioned and replicated caches. Additionally, each call to put returns the object it just replaced in the cache, which adds more unnecessary overhead. Loading the cache can be made much more efficient by using the putAll method instead.

This chapter assumes that you have completed "Creating and Caching Complex Objects". It also assumes that you are familiar with using java.io.BufferedReader to read text files, java.lang.String.split method to parse text files, and java.text.SimpleDateFormat to parse dates.

5.2 Populating a Cache with Domain Objects

This exercise demonstrates how to create a console application that populates a Coherence cache with domain objects. The application can use the Coherence com.tangosol.io.pof.PortableObject implementation.

You will create a key that will help obtain the Contact object, a generator to provide data for the cache, and a loader to load the cache.

  1. Create a Class with the Key for the Domain Objects

  2. Edit the POF Configuration File

  3. Create the Data Generator

  4. Create a Console Application to Load the Cache

  5. Edit the Cache Server Start-Up File

  6. Run the Cache Loading Example

5.2.1 Create a Class with the Key for the Domain Objects

Follow these steps to create a class that contains the key for the a domain object:

  1. Create a new project called Loading.

    See "Creating and Caching Complex Objects" for information on creating a new project.

  2. Add coherence.jar to the classpath. Also add the classes and files related to the Address, PhoneNumber, and Contact classes that you created in an earlier exercise (Contacts). These files can be found in c:\home\oracle\labs and c:\home\oracle\labs\Contacts\classes. (Hint: right-click the project and choose Project Properties then Libraries and Classpath. See "Adding JARS and Libraries to the Project Classpath".)

    Figure 5-1 Adding Contacts Directory to the Classpath

    Adding to classpath.
  3. Create a contact ID class that provides a key to the employee for whom information is tracked. See "Creating a Java Class" if you need detailed information on creating a Java class.

    Create the contact ID based on the employee's first name and last name. This object acts as the key to obtain the Contact object.

    Since this class will use POF serialization, it must implement PortableObject and provide implementations for the writeExternal and readExternal PortableObject methods and 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 non-transient 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 equals() returns true must serialize identically; most built-in Java types meet this requirement as well.

    Example 5-1 illustrates a possible solution.

    Example 5-1 Simple Contact ID 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.Base;
    import com.tangosol.util.HashHelper;
     
    import java.io.IOException;
     
    /**
    * ContactId is a key to the person for whom information is
    * tracked.
    */
    public class ContactId
            implements PortableObject
        {
        // ----- constructors ---------------------------------------------------
     
        /**
        * Default constructor (necessary for PortableObject implementation).
        */
        public ContactId() {
            }
     
        /**
        * Construct a contact person.
        *
        */
        public ContactId(String FirstName, String LastName)
            {
            super();
            this.FirstName = FirstName;
            this.LastName  = LastName;
            }
     
        // ----- accessors ------------------------------------------------------
     
        /**
        * Return the first name.
        *
        */
        public String getFirstName()
            {
            return FirstName;
            }
     
        /**
        * Return the last name.
        *
        */
        public String getLastName()
            {
            return LastName;
            }
     
        // ----- PortableObject interface ---------------------------------------
     
        public void readExternal(PofReader reader)
                throws IOException
            {
            FirstName = reader.readString(0);
            LastName = reader.readString(1);
            }
     
        public void writeExternal(PofWriter writer)
                throws IOException
            {
            writer.writeString(0, FirstName);
            writer.writeString(1, LastName);
            }
     
        // ----- Object methods -------------------------------------------------
     
        public boolean equals(Object oThat)
            {
            if (this == oThat)
                {
                return true;
                }
            if (oThat == null)
                {
                return false;
                }
     
            ContactId that = (ContactId) oThat;
            return Base.equals(getFirstName(), that.getFirstName()) &&
                   Base.equals(getLastName(),  that.getLastName());
            }
     
        public int hashCode()
            {
            return HashHelper.hash(getFirstName(),
                   HashHelper.hash(getLastName(), 0));
            }
     
        public String toString()
            {
            return getFirstName() + " " + getLastName();
            }
     
        // ----- data members ---------------------------------------------------
     
        /**
        * First name.
        */
        private String FirstName;
     
        /**
        * Last name.
        */
        private String LastName;
        }
    

5.2.2 Edit the POF Configuration File

Edit the POF configuration file. Add a <user-type> entry for ContactID to contacts-pof-config.xml. The file should look similar to Example 5-2.

Example 5-2 POF Configuration File with the ContactId Entry

<?xml version="1.0"?>
 
<!DOCTYPE pof-config SYSTEM "pof-config.dtd">
 
<pof-config>
  <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> 
     <type-id>1004</type-id>
      <class-name>com.oracle.handson.ContactId</class-name>
    </user-type>  
   </user-type-list>
  <allow-interfaces>true</allow-interfaces>
  <allow-subclasses>true</allow-subclasses>
</pof-config>

5.2.3 Create the Data Generator

Create a Java class named DataGenerator to generate random employee contact names and addresses. See "Creating a Java Class" if you need detailed information.

Use the Address, PhoneNumber, and Contact classes that you created in an earlier exercise. Use java.util.Random to generate some random names, addresses, telephone numbers, and ages.

Example 5-3 illustrates a possible solution. This solution creates a text file, contacts.cvs, that contains the employee contact information. Store the file in the root of the project directory, in this case, C:\home\oracle\labs\Loading.

Example 5-3 Sample Data Generation Class

package com.oracle.handson;
 
import com.oracle.handson.Address;
import com.oracle.handson.Contact;
import com.oracle.handson.PhoneNumber;
import com.tangosol.util.Base;
 
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
 
import java.sql.Date;
 
import java.util.Collections;
import java.util.Random;
 
 
/**
* DataGenerator is a generator of sample contacts.
*/
public class DataGenerator
    {
    // ----- static methods -------------------------------------------------
 
    /**
    * Generate contacts.
    */
    public static void main(String[] asArg)
            throws IOException
        {
        String       sFile = asArg.length > 0 ? asArg[0] : FILENAME;
        int          cCon  = asArg.length > 1 ? Integer.parseInt(asArg[1]) : 1000;
        OutputStream out   = new FileOutputStream(sFile);
 
        generate(out, cCon);
        out.close();
        }
 
    /**
    * Generate the contacts and write them to a file.
    */
    public static void generate(OutputStream out, int cContacts)
            throws IOException
        {
        PrintWriter writer = new PrintWriter(new BufferedWriter(
                new OutputStreamWriter(out)));
 
        for (int i = 0; i < cContacts; ++i)
            {
            StringBuffer sb = new StringBuffer(256);
 
            //contact person
            sb.append("John,")
              .append(getRandomName())
              .append(',');
 
            // home and work addresses
            sb.append(Integer.toString(Base.getRandom().nextInt(999)))
              .append(" Beacon St.,,") /*street1,empty street2*/
              .append(getRandomName()) /*random city name*/
              .append(',')
              .append(getRandomState())
              .append(',')
              .append(getRandomZip())
              .append(",US,Yoyodyne Propulsion Systems,")
              .append("330 Lectroid Rd.,Grover's Mill,")
              .append(getRandomState())
              .append(',')
              .append(getRandomZip())
              .append(",US,");
 
            // home and work telephone numbers
            sb.append("home,")
              .append(Base.toDelimitedString(getRandomPhoneDigits(), ","))
              .append(",work,")
              .append(Base.toDelimitedString(getRandomPhoneDigits(), ","))
              .append(',');
 
            // random birth date in millis before or after the epoch
            sb.append(getRandomDateInMillis());
 
            writer.println(sb);
            }
        writer.flush();
        }
 
    /**
    * Return a random name.
    *
    */
    private static String getRandomName()
        {
        Random rand = Base.getRandom();
        int    cCh  = 4 + rand.nextInt(7);
        char[] ach  = new char[cCh];
 
        ach[0] = (char) ('A' + rand.nextInt(26));
        for (int of = 1; of < cCh; ++of)
            {
            ach[of] = (char) ('a' + rand.nextInt(26));
            }
        return new String(ach);
        }
 
    /**
    * Return a random phone muber.
    * The phone number includes access, country, area code, and local
    * number.
    *
    */
    private static int[] getRandomPhoneDigits()
        {
        Random rand = Base.getRandom();
        return new int[] {
            11,                   // access code
            rand.nextInt(99),     // country code
            rand.nextInt(999),    // area code
            rand.nextInt(9999999) // local number
            };
        }
 
    /**
    * Return a random Phone.
    *
    */
    private static PhoneNumber getRandomPhone()
        {
        int[] anPhone = getRandomPhoneDigits();
 
        return new PhoneNumber((short)anPhone[0], (short)anPhone[1],
                (short)anPhone[2], anPhone[3]);
 
        }
    /**
    * Return a random Zip code.
    *
    */
    private static String getRandomZip()
        {
        return Base.toDecString(Base.getRandom().nextInt(99999), 5);
        }
 
    /**
    * Return a random state.
    *
    */
    private static String getRandomState()
        {
        return STATE_CODES[Base.getRandom().nextInt(STATE_CODES.length)];
        }
 
    /**
    * Return a random date in millis before or after the epoch.
    *
    */
    private static long getRandomDateInMillis()
        {
        return (Base.getRandom().nextInt(40) - 20) * Contact.MILLIS_IN_YEAR;
        }
 
    /**
    * Generate a Contact with random information.
    *
    */
    public static Contact getRandomContact()
        {
        return new Contact("John",
                getRandomName(),
                new Address("1500 Boylston St.", null, getRandomName(),
                    getRandomState(), getRandomZip(), "US"),
                new Address("8 Yawkey Way", null, getRandomName(),
                    getRandomState(), getRandomZip(), "US"),
                Collections.singletonMap("work", getRandomPhone()),
                new Date(getRandomDateInMillis()));
        }
 
    // ----- constants ------------------------------------------------------
 
    /**
    * US Postal Service two letter postal codes.
    */
    private static final String[] STATE_CODES = {
            "AL", "AK", "AS", "AZ", "AR", "CA", "CO", "CT", "DE", "OF", "DC",
            "FM", "FL", "GA", "GU", "HI", "ID", "IL", "IN", "IA", "KS", "KY",
            "LA", "ME", "MH", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE",
            "NV", "NH", "NJ", "NM", "NY", "NC", "ND", "MP", "OH", "OK", "OR",
            "PW", "PA", "PR", "RI", "SC", "SD", "TN", "TX", "UT", "VT", "VI",
            "VA", "WA", "WV", "WI", "WY"
        };
    /**
    * Default contacts file name.
    */
    public static final String FILENAME = "contacts.csv";
    }

5.2.4 Create a Console Application to Load the Cache

Create a Java class called LoaderExample. See "Creating a Java Class" if you need detailed information.

The class will load the cache with employee data generated by the program in the previous section. Use input streams and buffered readers to load the entire employee information contained in the contacts.csv file into a single Coherence cache.

The application should contain the code to parse the employee information in the data file. Once you have this information, construct the individual contacts to put into the cache. To conserve processing effort and minimize network traffic, use the putAll method to load the cache.

Example 5-4 illustrates a possible solution.

Example 5-4 Sample Cache Loading Program

package com.oracle.handson;
 
import com.tangosol.net.CacheFactory;
import com.tangosol.net.NamedCache;
 
import com.oracle.handson.ContactId;
import com.oracle.handson.Address;
import com.oracle.handson.PhoneNumber;
import com.oracle.handson.Contact;
 
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
 
import java.sql.Date;
 
import java.util.HashMap;
import java.util.Map;
 
 
/**
* LoaderExample loads contacts into the cache from a files.
*
*/
public class LoaderExample
    {
    // ----- static methods -------------------------------------------------
 
    /**
    * Load contacts.
    */
    public static void main (String[] asArg)
            throws IOException
        {
        String sFile  = asArg.length > 0 ? asArg[0] : DataGenerator.FILENAME;
        String sCache = asArg.length > 1 ? asArg[1] : CACHENAME;
 
        System.out.println("input file: " + sFile);
        System.out.println("cache name: " + sCache);
 
        new LoaderExample().load(CacheFactory.getCache(sCache),
                new FileInputStream(sFile));
        CacheFactory.shutdown();
        }
 
    /**
    * Load cache from stream.
    *
    */
    public void load(NamedCache cache, InputStream in)
            throws IOException
        {
        BufferedReader reader   = new BufferedReader(new InputStreamReader(in));
        Map            mapBatch = new HashMap(1024);
        String         sRecord;
        int            cRecord  = 0;
 
        while ((sRecord = reader.readLine()) != null)
            {
            // parse record
            String[]      asPart     = sRecord.split(",");
            int           ofPart     = 0;
            String        sFirstName =  asPart[ofPart++];
            String        sLastName  =  asPart[ofPart++];
            ContactId          id    = new ContactId(sFirstName, sLastName);
            Address       addrHome   = new Address(
                                         /*streetline1*/ asPart[ofPart++],
                                         /*streetline2*/ asPart[ofPart++],
                                         /*city*/        asPart[ofPart++],
                                         /*state*/       asPart[ofPart++],
                                         /*zip*/         asPart[ofPart++],
                                         /*country*/     asPart[ofPart++]);
            Address       addrWork   = new Address(
                                         /*streetline1*/ asPart[ofPart++],
                                         /*streetline2*/ asPart[ofPart++],
                                         /*city*/        asPart[ofPart++],
                                         /*state*/       asPart[ofPart++],
                                         /*zip*/         asPart[ofPart++],
                                         /*country*/     asPart[ofPart++]);
            Map           mapTelNum  = new HashMap();
 
            for (int c = asPart.length - 1; ofPart < c; )
                {
                mapTelNum.put(/*type*/ asPart[ofPart++], new PhoneNumber(
                    /*access code*/  Short.parseShort(asPart[ofPart++]),
                    /*country code*/ Short.parseShort(asPart[ofPart++]),
                    /*area code*/    Short.parseShort(asPart[ofPart++]),
                    /*local num*/    Integer.parseInt(asPart[ofPart++])));
                }
            Date dtBirth = new Date(Long.parseLong(asPart[ofPart]));
 
            // Construct Contact and add to batch
            Contact con1 = new Contact(sFirstName, sLastName, addrHome,                                        addrWork, mapTelNum, dtBirth);            System.out.println(con1);            mapBatch.put(id, con1);            
 
            ++cRecord;
            if  (cRecord % 1024 == 0)
                {
                // load batch
                cache.putAll(mapBatch);
                mapBatch.clear();
                System.out.print('.');
                System.out.flush();
                }
            }
 
            if (!mapBatch.isEmpty())
               {
               // load final batch
               cache.putAll(mapBatch);
               }
 
        System.out.println("Added " + cRecord + " entries to cache");
        }
 
 
    // ----- constants ------------------------------------------------------
 
    /**
    * Default cache name.
    */
    public static final String CACHENAME = "ContactsCache";
 
    }

5.2.5 Edit the Cache Server Start-Up File

Edit the contacts-cache-server.cmd file to add the compiled classes for the Loading project (in this case, C:\home\oracle\labs\Loading\classes). Ensure that the path to the directory where the cache configuration file is stored (in this case, C:\home\oracle\labs) is listed.

The contacts-cache-server.cmd file should look similar to Example 5-5:

Example 5-5 Sample contacts-cache-server.cmd File

@echo off
setlocal
 
if (%COHERENCE_HOME%)==() (
    set COHERENCE_HOME=c:\oracle\product\coherence
) 
 
 
set COH_OPTS=%COH_OPTS% -server -cp %COHERENCE_HOME%\lib\coherence.jar;C:\home\oracle\labs\Contacts\classes;C:\home\oracle\labs\Loading\classes;C:\home\oracle\labs;
set COH_OPTS=%COH_OPTS% -Dtangosol.coherence.cacheconfig=\home\oracle\labs\contacts-cache-config.xml
 
java %COH_OPTS% -Xms1g -Xmx1g -Xloggc: com.tangosol.net.DefaultCacheServer %2 %3 %4 %5 %6 %7
 
:exit

5.2.6 Run the Cache Loading Example

Follow these steps to run the cache loading example.

  1. Add the path to the cache configuration to the Java Options field of the Project Properties then Run/Debug/Profile.

    -Dtangosol.coherence.cacheconfig=\home\oracle\labs\contacts-cache-config.xml 
    
  2. If you have not done so already, compile the Java files in the Loading project. Right-click the project in JDeveloper and select Make Loading.jpr.

  3. Stop any running cache servers. Start a cache server with the contacts-cache-server.cmd file.

  4. Run DataGenerator from JDeveloper, then run LoaderExample.

    A stream of employee contact information should appear in the JDeveloper output window. Figure 5-2 illustrates an example.

    Figure 5-2 Output from the Sample Cache Loading Program

    Output from the Sample Cache Loading Program

5.3 Querying and Aggregating Data in the Cache

This exercise introduces the concept of querying and aggregating data in a cache. This exercise demonstrates how to:

After putting complex objects in the named caches, you can query and aggregate information within the grid. The com.tangosol.util.QueryMap interface provides methods for managing the values or keys within a cache. You can use filters to restrict your results. You can also define indexes to optimize your queries.

Because Coherence serializes information when storing, you also have the overhead of deserializing when querying. When indexes are added, the values kept in the index itself are deserialized and therefore, offer quicker access time. The objects that are indexed are always kept serialized.

Some of the often-used methods in the QueryMap interface are:

It is important to note that filtering occurs at Cache Entry Owner level. In a partitioned topology, filtering can be performed in parallel because it is the primary partitions that do the filtering. The QueryMap interface uses the Filter classes. You can find more information on these classes in the API for the com.tangosol.util.filter package.

All Coherence NamedCaches implement the com.tangosol.util.QueryMap interface. This allows NamedCaches to support the searching for keys or entries in a cache that satisfy some condition. The condition can being represented as an object that implements the com.tangosol.util.Filter interface.

The com.tangosol.util.filter package contains several predefined classes that provide implementations of standard query expressions. Examples of these classes include GreaterFilter, GreaterEquals, LikeFilter, NotEqualsFilter, InFilter, and so on. You can use these filters to construct and compose object-based equivalents of most SQL WHERE clause expressions.

Note:

Coherence does not provide a SQLFilter because it is unlikely that the objects placed in a cache are modeled in a relational manner, that is, using rows and columns (as they are typically represented in a database). Additionally, it is common that objects placed in a cache are not easily modeled using relational models, for example, large blobs.

The Filter classes use standard Java method reflection to represent test conditions. For example, the following filter represents the condition where the value returned from the getHomeAddress.getState method on an object in a cache is for Massachusetts (MA):

(new EqualsFilter("getHomeAddress.getState", "MA");

If the object tested with this filter does not have a get method, then the test will fail.

A couple of examples will make things clearer:

In addition to the entrySet and keySet methods defined by the QueryMap interface, Coherence supports the definition of indexes, using the addIndex method, to improve query performance. Unlike relational database systems, where indexes are defined according to well-known and strictly enforced collections of named columns (that is, a schema), Coherence does not have a schema. Though lacking a formal schema for data allows for significant flexibility and polymorphism, within applications, it means that an approach different from that of traditional database systems is required to define indexes and therefore, increase query performance.

To define the values that are to be indexed for each object placed in a cache, Coherence introduces the concept of a ValueExtractor. A com.tangosol.util.ValueExtractor is a simple interface that defines an "extract" method. If given an object parameter, a ValueExtractor returns some value based on the parameter.

A simple example of a ValueExtractor implementation is the com.tangosol.util.extractor.ReflectionExtractor, which uses reflection to return the result of a method call on an object. For example:

new ReflectionExtractor("getCity")

ValueExtractors may be used throughout the Coherence API. Typically, however, they are used to define indexes.

An especially useful type of extractor is the ChainedExtractor. This is a composite ValueExtractor implementation based on an array of extractors. Extractors in the array are applied sequentially left-to-right, so a result of a previous extractor serves as a target object for a next one. For example:

new ChainedExtractor(new ReflectionExtractor("getHomeAddress"), new ReflectionExtractor("getState"))

This example assumes that HomeAddress and State belong to a complex Contact object. ChainedExtractor first uses reflection to call getHomeAddress on each cached Contact object, and then uses reflection to call getState on the set of returned HomeAddress.

  1. Create the Class to Query Cache Data

  2. Run the Query Example

  3. Edit the Query Example to Perform Aggregations

  4. Run the Query and Aggregation Example

5.3.1 Create the Class to Query Cache Data

Create a new Java class called QueryExample to perform your queries. Ensure that the class has a main method. See "Creating a Java Class" if you need detailed information.

Use the entrySet method to get the employee contact information for:

  • all employees who live in Massachusetts. Hint:

    cache.entrySet(new EqualsFilter("getHomeAddress.getState", "MA"));
    
  • all employees who live in Massachusetts and work elsewhere. Hint:

    cache.entrySet(new AndFilter(
             new EqualsFilter("getHomeAddress.getState", "MA"),
             new NotEqualsFilter("getWorkAddress.getState", "MA")));
    
  • all employees whose city name begins with 'S'. Hint:

    cache.entrySet(new LikeFilter("getHomeAddress.getCity", "S%"));
    
  • all employees with last name beginning with 'S' that live in Massachusetts. Use both key and value in the query. Note that the cache entries use ContactId objects as the keys. You can use the KeyExtractor to get these values. KeyExtractor is a specialized value extractor that indicates that a query should be run against the key objects, rather than the values. Hint:

    cache.entrySet(new AndFilter(
         new LikeFilter(new KeyExtractor("getLastName"), "S%", 
             (char) 0, false),
         new EqualsFilter("getHomeAddress.getState", "MA")));
    
  • all employees who are older than a specified age. Hint:

    final int nAge = 42;
        setResults = cache.entrySet(new GreaterFilter("getAge", nAge));
    

Use indexes to improve performance. Hint: Find the addIndex method in the Javadoc for the QueryMap interface.

Example 5-6 illustrates a possible solution.

Example 5-6 Sample QueryExample Class

package com.oracle.handson;
 
import com.tangosol.net.CacheFactory;
import com.tangosol.net.NamedCache;
 
import com.tangosol.util.extractor.ChainedExtractor;
import com.tangosol.util.extractor.KeyExtractor;
import com.tangosol.util.extractor.ReflectionExtractor;
 
import com.tangosol.util.filter.AlwaysFilter;
import com.tangosol.util.filter.AndFilter;
import com.tangosol.util.filter.EqualsFilter;
import com.tangosol.util.filter.GreaterFilter;
import com.tangosol.util.filter.LikeFilter;
import com.tangosol.util.filter.NotEqualsFilter;
 
import java.util.Iterator;
import java.util.Set;
 
 
/**
* QueryExample runs sample queries for contacts.
*
*/
public class QueryExample{
  
    // ----- QueryExample methods ---------------------------------------
 
    public static void main(String[] args) {
      NamedCache cache = CacheFactory.getCache("ContactsCache");
       query(cache);
    }
    /**
    * Perform the example queries
    *
    */
     public static void query(NamedCache cache)
        {
         // Add indexes to make queries more efficient
        ReflectionExtractor reflectAddrHome =
                new ReflectionExtractor("getHomeAddress");

        // Add an index for the age
        cache.addIndex(new ReflectionExtractor("getAge"), true, null);

        // Add index for state within home address
       cache.addIndex(new ChainedExtractor(reflectAddrHome,
                new ReflectionExtractor("getState")), true, null);

// Add index for state within work address
        cache.addIndex(new ChainedExtractor(
                new ReflectionExtractor("getWorkAddress"),
                new ReflectionExtractor("getState")), true, null);

        // Add index for city within home address
        cache.addIndex(new ChainedExtractor(reflectAddrHome,
                new ReflectionExtractor("getCity")), true, null);
 
        // Find all contacts who live in Massachusetts
        Set setResults = cache.entrySet(new EqualsFilter(
                "getHomeAddress.getState", "MA"));
        printResults("MA Residents", setResults);
 
        // Find all contacts who live in Massachusetts and work elsewhere
        setResults = cache.entrySet(new AndFilter(
                new EqualsFilter("getHomeAddress.getState", "MA"),
                new NotEqualsFilter("getWorkAddress.getState", "MA")));
        printResults("MA Residents, Work Elsewhere", setResults);
 
        // Find all contacts whose city name begins with 'S'
        setResults = cache.entrySet(new LikeFilter("getHomeAddress.getCity",
                "S%"));
        printResults("City Begins with S", setResults);
 
        final int nAge = 42;
        // Find all contacts who are older than nAge
        setResults = cache.entrySet(new GreaterFilter("getAge", nAge));
        printResults("Age > " + nAge, setResults);
 
        // Find all contacts with last name beginning with 'S' that live
        // in Massachusetts. Uses both key and value in the query.
        setResults = cache.entrySet(new AndFilter(
                new LikeFilter(new KeyExtractor("getLastName"), "S%",
                               (char) 0, false),
                new EqualsFilter("getHomeAddress.getState", "MA")));
        printResults("Last Name Begins with S and State Is MA", setResults);
 
        }
 
    /**
    * Print results of the query
    *
    * @param sTitle      the title that describes the results
    * @param setResults  a set of query results
    */
    private static void printResults(String sTitle, Set setResults)
        {
        System.out.println(sTitle);
        for (Iterator iter = setResults.iterator(); iter.hasNext(); )
            {
            System.out.println(iter.next());
            }
        }
    }

5.3.2 Run the Query Example

Stop all running cache servers. Restart the Contacts cache server with contacts-cache-server.cmd. Run the DataGenerator file, the LoadExample file, then the QueryExample file. After printing all of the contact information in the cache, it displays the results of the queries. The results should look similar to Figure 5-3.

Figure 5-3 Output of the QueryExample Class

Output of the QueryExample Class

5.3.3 Edit the Query Example to Perform Aggregations

Add code in QueryExample.java to perform aggregations on the data in the cache. An EntryAggregator (com.tangosol.util.InvocableMap.EntryAggregator) enables you to perform operations on all or a specific set of objects and return an aggregation. EntryAggregators are essentially agents that execute services in parallel against the data within the cluster. Aggregations are performed in parallel and can benefit from the addition of cluster members.

There are two ways of aggregating: aggregate over a collection of keys or by specifying a filter. Example 5-7 illustrates the methods that perform these aggregations.

Example 5-7 Methods to Aggregate Over Keys or by Specifying Filters

Object aggregate(Collection keys, InvocableMap.entryAggregator agg)

Object aggregate(Filter filter, InvocableMap.entryAggregator agg)

The following example uses a filter.

  1. Using aggregations, write code in the QueryExample class to calculate the following:

    • the number of employees that are older than a specified age. Hint: use the GreaterFilter and the Count class:

      cache.aggregate(new GreaterFilter("getAge", nAge), new Count())
      
    • lowest age in the set of employees. Hint: use the AlwaysFilter and the LongMin class:

      cache.aggregate(AlwaysFilter.INSTANCE, new LongMin("getAge"))
      
    • highest age in the set of employees. Hint: use the AlwaysFilter and the LongMax class:

      cache.aggregate(AlwaysFilter.INSTANCE, new LongMax("getAge"))
      
    • average age of employees. Hint: use the AlwaysFilter and the DoubleAverage class:

      cache.aggregate(AlwaysFilter.INSTANCE, new DoubleAverage("getAge")
      
  2. Import the Count, DoubleAverage, LongMax, and LongMin aggregator classes.

    import com.tangosol.util.aggregator.Count;
    import com.tangosol.util.aggregator.DoubleAverage;
    import com.tangosol.util.aggregator.LongMax;
    import com.tangosol.util.aggregator.LongMin;
    

    The QueryExample.java file should now look similar to Example 5-8:

    Example 5-8 QueryExample with Aggregation

    package com.oracle.handson;
    
    import com.tangosol.net.CacheFactory;
    import com.tangosol.net.NamedCache;
    
    import com.tangosol.util.aggregator.Count;  
    import com.tangosol.util.aggregator.DoubleAverage;
    import com.tangosol.util.aggregator.LongMax;
    import com.tangosol.util.aggregator.LongMin;
    
    import com.tangosol.util.extractor.ChainedExtractor;
    import com.tangosol.util.extractor.KeyExtractor;
    import com.tangosol.util.extractor.ReflectionExtractor;
    
    import com.tangosol.util.filter.AlwaysFilter;
    import com.tangosol.util.filter.AndFilter;
    import com.tangosol.util.filter.EqualsFilter;
    import com.tangosol.util.filter.GreaterFilter;
    import com.tangosol.util.filter.LikeFilter;
    import com.tangosol.util.filter.NotEqualsFilter;
    
    import java.util.Iterator;
    import java.util.Set;
    
    /**
    * QueryExample runs sample queries for contacts.
    */
    public class QueryExample{
      
        // ----- QueryExample methods ---------------------------------------
    
        public static void main(String[] args) {
          NamedCache cache = CacheFactory.getCache("ContactsCache");
           query(cache);
        }
        /**
        * Perform the example queries
        *
        */
         public static void query(NamedCache cache)
            {
             // Add indexes to make queries more efficient
            ReflectionExtractor reflectAddrHome =
                    new ReflectionExtractor("getHomeAddress");
    
            cache.addIndex(new ReflectionExtractor("getAge"), true, null);
            cache.addIndex(new ChainedExtractor(reflectAddrHome,
                    new ReflectionExtractor("getState")), true, null);
            cache.addIndex(new ChainedExtractor(
                    new ReflectionExtractor("getWorkAddress"),
                    new ReflectionExtractor("getState")), true, null);
            cache.addIndex(new ChainedExtractor(reflectAddrHome,
                    new ReflectionExtractor("getCity")), true, null);
    
            // Find all contacts who live in Massachusetts
            Set setResults = cache.entrySet(new EqualsFilter(
                    "getHomeAddress.getState", "MA"));
            printResults("MA Residents", setResults);
    
            // Find all contacts who live in Massachusetts and work elsewhere
            setResults = cache.entrySet(new AndFilter(
                    new EqualsFilter("getHomeAddress.getState", "MA"),
                    new NotEqualsFilter("getWorkAddress.getState", "MA")));
            printResults("MA Residents, Work Elsewhere", setResults);
    
            // Find all contacts whose city name begins with 'S'
            setResults = cache.entrySet(new LikeFilter("getHomeAddress.getCity",
                    "S%"));
            printResults("City Begins with S", setResults);
    
            final int nAge = 42;
            // Find all contacts who are older than nAge
            setResults = cache.entrySet(new GreaterFilter("getAge", nAge));
            printResults("Age > " + nAge, setResults);
    
            // Find all contacts with last name beginning with 'S' that live
            // in Massachusetts. Uses both key and value in the query.
            setResults = cache.entrySet(new AndFilter(
                    new LikeFilter(new KeyExtractor("getLastName"), "S%",
                                   (char) 0, false),
                    new EqualsFilter("getHomeAddress.getState", "MA")));
            printResults("Last Name Begins with S and State Is MA", setResults);
     
           // Count contacts who are older than nAge
            System.out.println("count > " + nAge + ": "+ cache.aggregate(
                    new GreaterFilter("getAge", nAge), new Count()));
      
            // Find minimum age
            System.out.println("min age: " + cache.aggregate(AlwaysFilter.INSTANCE,
                    new LongMin("getAge")));
      
            // Calculate average age
            System.out.println("avg age: " + cache.aggregate(AlwaysFilter.INSTANCE,
                    new DoubleAverage("getAge")));
      
            // Find maximum age
            System.out.println("max age: " + cache.aggregate(AlwaysFilter.INSTANCE,
                    new LongMax("getAge")));
            }
      
        /**
        * Print results of the query
        *
        */
        private static void printResults(String sTitle, Set setResults)
            {
            System.out.println(sTitle);
            for (Iterator iter = setResults.iterator(); iter.hasNext(); )
                {
                System.out.println(iter.next());
                }
            }
        }
    

5.3.4 Run the Query and Aggregation Example

Follow these steps to query the cache and aggregate the results.

  1. Stop the Contacts cache server and re-start it with the contacts-cache-server.cmd file.

  2. Run the DataGenerator, LoaderExample, and QueryExample applications.

    You should see output similar to Figure 5-4 in JDeveloper.

    Figure 5-4 Output from the Aggregators

    Output from the Aggregators