Developer: Java

Creating Oracle Coherence Caches in Oracle JDeveloper

By Deepak Vohra

Published April 2008

Learn how to create and configure an Oracle Coherence cache in Oracle JDeveloper, step by step.

DOWNLOAD

Oracle Coherence has revolutionized the way clustered application data is cached. Oracle Coherence manages data in clustered applications and application servers as if it were a single application server. Database applications no longer need to query the database directly each time data is required to be retrieved, updated, or deleted.

A Coherence cache is a collection of data objects that serves as an intermediary between the database and the client applications. Database data may be loaded into a cache and made available to different applications. Thus, Coherence caches reduce load on the database and provide faster access to database data.

Coherence caches provide higher availability through database isolation and data replication. Modifications made to a cache may be synchronized with the database whenever the database is available. Even if the database or an application server node is not available, database updates are still reliable due to the lazy load and lazy write mechanism used by a Coherence cache and due to the failover and failback provided by Oracle Coherence.

Coherence caches provide distributed processing not only across a cluster of application server nodes but also across the data objects in the cache, because data modification operations may be performed on the data objects.

Oracle Coherence also provides event-based processing. The state of data objects in a cache may be monitored and actions invoked on other processes such as the start of a BPEL process.

Oracle Coherence supports different types of caches. In a replicated cache, data is replicated to each of the application server nodes in the cluster. This is suitable if faster read access is required but not suitable for writes, because data has to be written to each of the nodes. In a distributed (partitioned) cache , data is distributed (load-balanced) across different nodes. Failover is implemented in a distributed cache using backups, which are also distributed across the cluster nodes.

Oracle Coherence is implemented via services such as the Cluster service, the Distributed Cache service, and the Replicated Cache service. Whichever type of cache is used, an application uses the same API to access and store data.

The cache configuration deployment descriptor is used to configure a cache. The root element of the cache configuration file is cache-config. Cache names and name patterns are mapped to cache types in the caching-scheme-mapping element using subelement cache-mapping. Cache types are defined in the caching-schemes element. Some of the commonly used cache types are discussed in the following table.

Table 1. Cache Types

Cache Type

Description

distributed scheme

Defines a distributed cache in which data is stored across a cluster of nodes

replicated scheme

Defines a cache in which cache entries are replicated across all the cluster nodes

read-write-backing-map scheme

Defines a map, which provides a cache of a persistent store such as a relational database

external scheme

Defines an external cache such as a disk

class scheme

Defines a custom cache implementation, which is required to implement the java.util.Map interface

Now, let’s go through the exercise of creating and configuring a Coherence cache using Oracle JDeveloper.

Prerequisites

Project Configuration

Download the Oracle Coherence Version 3.3.1 - Pure Java and extract the zip file to a directory. 

After downloading Coherecne and extracting the zip file to a directory, create an application and a project in Oracle JDeveloper. Add a Java class, CoherenceCache.java, to the project. A coherence cache will be created in the Java class. Add an XML document, cache-config.xml, as the cache configuration deployment descriptor.

Add the Coherence JAR files coherence.jar and tangosol.jar to the project libraries. The Coherence JAR files are in the \coherence\lib directory of the Oracle Coherence installation. Also add the Oracle JDBC library, which is required for database access to the project libraries.

Run Configuration

Next, modify the Run configuration for the application to add the cache configuration file as a runtime Java option. Select the project node and select Tools -> Project Properties. In the Project Properties window, select Run/Debug/Profile. The Run Configuration Default is selected by default. Click Edit for the Default configuration.

In the Edit Run Configuration window, select Launch Settings. In the Java Options field, specify the cache configuration file (cache-config.xml) with

-Dtangosol.coherence.cacheconfig=[path-to-cache-config-file]/cache-config.xml

For the Oracle Coherence application we created, specify the following (the path to cache-config.xml may vary) in the Java Options field and click OK.

-Dtangosol.coherence.cacheconfig=C:/Users/dvohra09/Documents/Jdeveloper/mywork/CoherenceCache/Coherence/cache-config.xml

Click OK in the Project Properties -> Run/Debug/Profile window. The cache configuration file gets added as a runtime Java option to the coherence Java application.

Cache Configuration

In the cache configuration file, define mapping for cache names and naming patterns with the cache-mapping elements in the caching-scheme-mapping element. Specify the default mapping to cache type default-replicated and map cache name VirtualCache to cache type default-distributed. Define the distributed caching scheme with the distributed-scheme element using the DistributedCache service. The cache configuration file, listed below, is to be copied to the cache-config.xml file in Oracle JDeveloper.

<?xml version="1.0"?>

<!DOCTYPE cache-config SYSTEM "cache-config.dtd">
<cache-config>
    <caching-scheme-mapping>

        <cache-mapping>
            <cache-name>VirtualCache</cache-name>
            <scheme-name>default-distributed</scheme-name>
        </cache-mapping>
    </caching-scheme-mapping>
    <caching-schemes>
        <!--
        Default Distributed caching scheme.
        -->
        <distributed-scheme>
            <scheme-name>default-distributed</scheme-name>
            <service-name>DistributedCache</service-name>
            <backing-map-scheme>
                <class-scheme>
                    <scheme-ref>default-backing-map</scheme-ref>
                </class-scheme>
            </backing-map-scheme>
        </distributed-scheme>
  <class-scheme>
            <scheme-name>default-backing-map</scheme-name>
            <class-name>com.tangosol.util.SafeHashMap</class-name>
            </class-scheme>
 </caching-schemes>
</cache-config>

Cache Application

Next, create a cache in the Java class. Import the CacheFactory class and the NamedCache interface.

import com.tangosol.net.CacheFactory;

                                
 import com.tangosol.net.NamedCache;

An instance of a cache is created from the CacheFactory class. Create a NamedCache using the getCache() method of the CacheFactory class. Use cache name VirtualCache, which is mapped to a distributed caching scheme.

NamedCache cache = CacheFactory.getCache (

"VirtualCache");

A NamedCache is a java.util.Map that holds resources shared across nodes in a cluster. Add a cache entry using the put() method.

cache.put (key, "Hello Cache");

A cache entry may be retrieved using the get() method.

System.out.println((String)cache.get("hello"));

Copy the Java class listed below to the CoherenceCache application in Oracle JDeveloper.

package coherence;

import com.tangosol.net.CacheFactory; 
import com.tangosol.net.NamedCache;

public class CoherenceCache {
    NamedCache cache;
    public CoherenceCache() {
    }

   public void putCache(){
        cache = CacheFactory.getCache ( "VirtualCache"); 
       String key = "hello"; 
       cache.put (key, "Hello Cache"); 

   }

   public void retrieveCache(){

       System.out.println((String)cache.get("hello"));

   }

    public static void main (String [] args) {
    CoherenceCache cache = new CoherenceCache(); 
    cache.putCache();
    cache.retrieveCache();
    }
}

Right-click the Oracle Coherence application and select Run.

The Oracle Coherence application runs, and the output is displayed in the Log window. The output shows that the operational configuration gets loaded from tangosol-coherence.xml, the cache configuration gets loaded from cache-config.xml, a new cluster gets created, and the DistributedCache service joins the cluster. The operational deployment descriptor tangosol-coherence.xml specifies the operational and runtime settings used by Coherence for its clustering, communication, and data management services.

Creating an Oracle Database Cache

In this section, you will create an Oracle Database cache, which is a cache backed by Oracle Database. First create an Oracle Database table with the following SQL script.

CREATE TABLE OE.CATALOG(id VARCHAR(25)

                                
 PRIMARY KEY, value VARCHAR(96));
                                
INSERT INTO OE.CATALOG VALUES('catalog1', 'Tuning Undo Tablespace');
                                
INSERT INTO OE.CATALOG VALUES('catalog2', 'Tuning Your View Objects');

Create a Java class DatabaseCache.java for the database cache in Oracle JDeveloper and add methods createCache(), addEntry(), retrieveEntry(), eraseEntry(), and queryCache().

Custom CacheStore

To connect a cache to a back-end database, a cache configuration file (cache-config.xml) element cachestore-scheme is required. The cachestore-scheme element must be configured with a custom class that implements either the com.tangosol.net.cache.CacheLoader or com.tangosol.net.cache.CacheStore interface. A custom class that implements the CacheStore interface is listed below.

package coherence;


import com.tangosol.net.cache.CacheStore;
import com.tangosol.util.Base;

import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

public class DBCacheStore
        extends Base
        implements CacheStore {

    public DBCacheStore(String sTableName)
        {
        m_sTableName = sTableName;
        configureConnection();
        }
        protected void configureConnection()
                {
                try
                        {
                        Class.forName("oracle.jdbc.OracleDriver");
                        m_con = DriverManager.getConnection(DB_URL, DB_USERNAME, DB_PASSWORD);
                        m_con.setAutoCommit(true);
                        }
                catch (Exception e)
                        {
                        throw ensureRuntimeException(e, "Connection failed");
                        }
                }

    public String getTableName()
        {
        return m_sTableName;
        }

    public Connection getConnection()
        {
        return m_con;
        }

    public Object load(Object oKey)
        {
        Object     oValue = null;
        Connection con    = getConnection();
        String     sSQL   = "SELECT id, value FROM " + getTableName()
                    + " WHERE id = ?";
        try
            {
            PreparedStatement stmt = con.prepareStatement(sSQL);

            stmt.setString(1, String.valueOf(oKey));

            ResultSet rslt = stmt.executeQuery();
            if (rslt.next())
                {
                oValue = rslt.getString(2);
                if (rslt.next())
                    {
                    throw new SQLException("Not a unique key: " + oKey);
                    }
                }
            stmt.close();
            }
        catch (SQLException e)
            {
            throw ensureRuntimeException(e, "Load failed: key=" + oKey);
            }
        return oValue;
        }

      public void store(Object oKey, Object oValue)
        {
        Connection con     = getConnection();
        String     sTable  = getTableName();
                String     sSQL;

                if (load(oKey) != null)
                        {

                        sSQL = "UPDATE " + sTable + " SET value = ? where id = ?";
                        }
                else
                        {

                        sSQL = "INSERT INTO " + sTable + " (value, id) VALUES (?,?)";
                        }
                try
                        {
                        PreparedStatement stmt = con.prepareStatement(sSQL);
                        int i = 0;
                        stmt.setString(++i, String.valueOf(oValue));
                        stmt.setString(++i, String.valueOf(oKey));
                        stmt.executeUpdate();
                        stmt.close();
                        }
                catch (SQLException e)
                        {
                        throw ensureRuntimeException(e, "Store failed: key=" + oKey);
                        }
        }
    public void erase(Object oKey)
        {
        Connection con  = getConnection();
        String     sSQL = "DELETE FROM " + getTableName() + " WHERE id=?";
        try
            {
            PreparedStatement stmt = con.prepareStatement(sSQL);

            stmt.setString(1, String.valueOf(oKey));
            stmt.executeUpdate();
            stmt.close();
            }
        catch (SQLException e)
            {
            throw ensureRuntimeException(e, "Erase failed: key=" + oKey);
            }
        }
        public void eraseAll(Collection colKeys)
                {
                throw new UnsupportedOperationException();
                }

        public Map loadAll(Collection colKeys)
                {
                throw new UnsupportedOperationException();
                }

        public void storeAll(Map mapEntries)
                {
                throw new UnsupportedOperationException();
                }

    public Iterator keys()
        {
        Connection con  = getConnection();
        String     sSQL = "SELECT id FROM " + getTableName();
        List       list = new LinkedList();

        try
            {
            PreparedStatement stmt = con.prepareStatement(sSQL);
            ResultSet         rslt = stmt.executeQuery();
            while (rslt.next())
                {
                Object oKey = rslt.getString(1);
                list.add(oKey);
                }
            stmt.close();
            }
        catch (SQLException e)
            {
            throw ensureRuntimeException(e, "Iterator failed");
            }

        return list.iterator();
        }

    protected Connection m_con;
    protected String m_sTableName;
    private static final String DB_DRIVER   = "oracle.jdbc.OracleDriver";

        private static final String DB_URL      = "jdbc:oracle:thin:@localhost:1521:ORCL";
        private static final String DB_USERNAME = "OE";
        private static final String DB_PASSWORD = "pw";
    }

Create a Java class DBCacheStore in Oracle JDeveloper and copy the DBCacheStore.java listing to Oracle JDeveloper. The DBCacheStore application uses JDBC to access Oracle Database, but another mechanism such as Hibernate or JDO may also be used.

Now create a cache configuration file for the database cache. Define a cache name pattern DBBacked*, which is mapped to a distributed caching scheme distributed-db-backed. Specify the cachestore scheme in the distributed scheme using the class coherence.DBCacheStore, which implements the CacheStore interface. An init parameter for the database table that is on the back end of the cache be specified for the DBCacheStore class. The table name is specified in the init-param element. The DBCacheStore class performs the database operations such as reading and writing of cache entries. Coherence supports read-write caching of a datasource for which the read-write-backing-map scheme is used. The read-write-backing-map scheme defines a backing map, which provides a size-limited cache of a persistent store. Oracle Coherence supports the following four types of read-write caching:

  • Read-Through - a cache entry is read into a cache from the database when required and made available to an application
  • Write-Through - updates to  cache entries are synchronized with the database without  delay
  • Refresh-Ahead - cache entries are refreshed periodically
  • Write-Behind - updates to cache entries are asynchronously written to a database after a delay specified in the write-delay-seconds element in the cache configuration file

Here you will use the Write-Through mechanism. Copy the cache configuration file for the database cache listed below to the cache-config.xml file in Oracle JDeveloper.

<cache-config>

 <caching-scheme-mapping>
  <!-- 
    Caches with names that start with 'DBBacked' will be created 
    as distributed-db-backed.   
    -->
  <cache-mapping>
   <cache-name>DBBacked*</cache-name>
   <scheme-name>distributed-db-backed</scheme-name>
  </cache-mapping>
 </caching-scheme-mapping>
 <caching-schemes>
  <!-- 
    DB Backed Distributed caching scheme.
    -->
  <distributed-scheme>
   <scheme-name>distributed-db-backed</scheme-name>
   <service-name>DistributedCache</service-name>
   <backing-map-scheme>
    <read-write-backing-map-scheme>
     <internal-cache-scheme>
      <class-scheme>
       <class-name>com.tangosol.util.ObservableHashMap</class-name>
      </class-scheme>
     </internal-cache-scheme>
     <cachestore-scheme>
      <class-scheme>
       <class-name>coherence.DBCacheStore</class-name>
       <init-params>
        <init-param>
         <param-type>java.lang.String</param-type>
         <param-value>CATALOG</param-value>
        </init-param>
       </init-params>
      </class-scheme>
     </cachestore-scheme>
     <read-only>false</read-only>
     <!--
        To make this a write-through cache just change the value below to 0 (zero)
        -->
     <write-delay-seconds>0</write-delay-seconds>
    </read-write-backing-map-scheme>
   </backing-map-scheme>
   <listener/>
   <autostart>true</autostart>
  </distributed-scheme>
 </caching-schemes>
</cache-config>

Adding a Cache Entry

In the DatabaseCache.java application addEntry() method, create a NamedCache object using the getCache() method of the CacheFactory class.

NamedCache cache = CacheFactory.getCache("DBBackedCache");

The DBBackedCache matches the cache pattern DBBacked* and therefore is mapped to a distributed caching scheme distributed-db-backed in the cache-config.xml file. Add a cache entry using the put() method of the NamedCache object.

cache.put(new String("catalog3"), new String("Evolving Grid Management"));

As the Write-Through mechanism is being used, the new cache entry is also getting synchronized with the database; a new row is added to the CATALOG table. Comment out all the methods except the createCache() and addEntry() methods. Right-click the DatabaseCache.java application in Oracle JDeveloper and select Run to add the new cache entry.  

When the put() method is invoked, the store() method, which maps the new cache entry to database table CATALOG using JDBC, gets invoked in the DBCacheStore class. The output from the Oracle Coherence application gets displayed in the Log window, and a new cache entry gets added. The output shows that the operational configuration deployment descriptor gets loaded, the cache configuration gets loaded, a new cluster gets created, and the DistributedCache service joins the cluster.

Because we are using a Write-Through cache, the database table also gets updated.

The new cache entry may be removed with the remove() method of the NamedCache object.

cache.remove(new String("catalog3"));

Bulk uploading of cache entries may be performed using the putAll() method.

Retrieving a Cache Entry

A cache entry may be retrieved using the get() method of the NamedCache object. For example, retrieve the cache entry for id catalog1.

System.out.println((String) cache.get("catalog1"));

When the get() method is invoked, the load() method, which retrieves database table data using JDBC, gets invoked in the DBCacheStore class. The output from running the Oracle Coherence application with the createCache() and retrieveEntry() methods uncommented to retrieve a cache entry in Oracle JDeveloper is shown below.

Bulk retrieval may be performed using the getAll() method of the NamedCache object.

Querying a Database Cache

Oracle Coherence supports searching for cache entries based on search criteria using filters. Coherence filters are available in the com.tangosol.util.filter package. In Oracle Coherence Enterprise Edition and Grid Edition, indexes may be added to the Coherence cache to improve performance. We will query the database cache using a LikeFilter filter, which matches cache entries with a specified pattern.

To query a database cache, the cache entries are required to be created prior to querying; the cache entries must be retrieved into the cache using the get() or getAll() method before a query using a filter may be performed. Therefore, retrieve database data and create a collection of cache entries using thegetAll() method.

HashSet hashSet=new HashSet();

                                
hashSet.add(new String("catalog1"));
                                
hashSet.add(new String("catalog2"));
                                
hashSet.add(new String("catalog3"));
                                
Map map=cache.getAll(hashSet);

Create a LikeFilter filter that searches for cache entries starting with “Tuning”.

Filter filter = new LikeFilter(IdentityExtractor.INSTANCE, "Tuning%", '\\', true);

Query the database cache using the entrySet() method with the LikeFilter filter.

Set results = cache.entrySet(filter);

Iterate over the results of the query to output the cache entries retrieved.

for (Iterator i = results.iterator(); i.hasNext();)

                              

                                {
                              

                                Map.Entry e = (Map.Entry) i.next();
                              

                                System.out.println("Catalog ID: "+e.getKey() + ", Title:  "+e.getValue());
                              

                                }

To run the database cache query, right-click the DatabaseCache.java application, which has only the createCache() and queryCache() methods uncommented, and select Run. The cache entries that match the pattern “Tuning%” get retrieved and output in the Log window. 

Oracle Coherence supports continuous query using the ContinuousQueryCache class. A continuous query is a query that is kept up-to-date using a continuous query cache. In a continuous query cache, the results of a query are updated using event listeners on events that could change the results of the query.

Create a ContinuousQueryCache object using the NamedCache object and the LikeFilter object.

ContinuousQueryCache queryCache = new ContinuousQueryCache(cache, filter );

Now create a result set using the entrySet() method.

Set results = queryCache.entrySet(filter);

Run the database cache coherence application using continuous query cache. The output is the same as with a cache that is not continuous. But if the cache entries are updated, the continuous query results also get updated.

The database cache application DatabaseCache.java is shown below.

package coherence;


import com.tangosol.net.CacheFactory;
import com.tangosol.net.NamedCache;
import com.tangosol.net.cache.ContinuousQueryCache;
import com.tangosol.util.Filter;
import com.tangosol.util.extractor.IdentityExtractor;
import com.tangosol.util.filter.LikeFilter;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class DatabaseCache {
    NamedCache cache;
    public DatabaseCache() {
    }

    public void createCache() {
         cache = CacheFactory.getCache("DBBackedCache");

    }

    public void addEntry() {

    cache.put(new String("catalog3"), new String("Evolving Grid Management"));

    }

    public void retrieveEntry() {
    System.out.println((String) cache.get( "catalog1"));
    }

    public void eraseEntry() {
    cache.remove(new String("catalog3"));
    }

    public void queryCache() {
        Filter filter = new LikeFilter(IdentityExtractor.INSTANCE, "Tuning%", '\\', true);
        HashSet hashSet=new HashSet();
        hashSet.add(new String("catalog1"));
        hashSet.add(new String("catalog2"));
        hashSet.add(new String("catalog3"));

        Map map=cache.getAll(hashSet);
        ContinuousQueryCache queryCache = new ContinuousQueryCache(cache, filter);
        Set results = queryCache.entrySet(filter);
       /* Set results = cache.entrySet(filter);*/

        if(results.isEmpty())
        System.out.println("Result Set Empty");
            for (Iterator i = results.iterator(); i.hasNext();)
                {
                Map.Entry e = (Map.Entry) i.next();
                System.out.println("Catalog ID: "+e.getKey() + ", Title: "+e.getValue());
                }
    }

    public static void main(String[] args) {
        DatabaseCache databaseCache = new DatabaseCache();
        databaseCache.createCache();
       /*databaseCache.addEntry();
        databaseCache.retrieveEntry();
        databaseCache.eraseEntry();*/
        databaseCache.queryCache();
    }
}

Congratulations, you have just created a configured an Oracle Coherence cache!

Deepak Vohra is an Oracle Certified Associate (Oracle Database 10g) and Sun Certified Java Programmer.

Oracle Chatbot
Disconnected