How to Access Oracle Coherence Caches from a JRuby App

by Pas Apicella

Accessing the Coherence Grid as a Coherence Extend Client has some clear performance advantages.

Published February 2011

The Oracle Coherence In-Memory Data Grid is a data management system for application objects that are shared across multiple servers, require low response time, very high throughput, predictable scalability, continuous availability and information reliability. As a result of these capabilities, Oracle Coherence is ideally suited for use in computational intensive, stateful middle-tier applications. Coherence is intended to run in the application tier and is often run in-process with the application itself - for example, in an application server cluster.

The following how-to explains how to access a Coherence cache from JRuby as a Coherence Extend Client using Portable Object Format (POF). We use an extend client and POF for the following reasons:

  • The sample app is a console application that will connect/disconnect to the cache many times a day. An extend client will avoid the overhead of cluster membership information maintenance by connecting to the proxy server itself. (See diagram below; more information on this architecture can be found here.)
  • With an extend client storage is disabled by default, thus we avoid any overhead required to store data locally.
  • Using POF has many advantages ranging from performance benefits to language independence, although in this example we are using it from a Java-enabled client. Serialization (the process of encoding an object into a binary format) is a critical component in Coherence as data needs to be moved around the network. POF is designed to be incredibly efficient in both space and time and has become a cornerstone element in working with Coherence. (More information on POF can be found here.)

jruby-coherence-f1

The demo will use the familiar HR schema data which is loaded from an Oracle database into a Coherence Cluster. Then from a JRuby script we will access the cluster data as an extend client to avoid the overhead of joining the cluster as a member.

Software Requirements

The following software can be used:

Oracle Database 11g Release 2 Oracle Database Software Downloads
Oracle Coherence for Java Version 3.6.1 Oracle Coherence Software Downloads
JDK 1.6 Java SE Downloads
JRuby 1.5.6 jruby.org
Oracle Database 11g Release 2 JDBC Driver Download ojdbc6.jar from Oracle Database 11g Release 2 JDBC Drivers. Drivers for other versions of Oracle or the JDK can be downloaded from the JDBC Driver Downloads page.
Apache Ant apache.org or via your operating system package manager
Sample Code The example code can be downloaded here. Unzip to your home directory. It will create two directories: jruby_coherence and hr_demo


The examples will also work with other versions.

Install each component using its recommended install instructions.

Demo Setup

In a terminal window set the Oracle environment and verify Oracle's standard demonstration Human Resources (HR) schema is installed:

$ sqlplus hr/welcome

[. . .]
SQL> select table_name from user_tables;

TABLE_NAME
------------------------------
COUNTRIES
JOBS
EMPLOYEES
LOCATIONS
DEPARTMENTS
JOB_HISTORY
REGIONS
      

If the schema is not available, see Installing the HR Schema in the Oracle Database Sample Schemas 11g Release 2 (11.2) guide.

Verify you have Oracle Coherence for Java 3.6.1 unzipped on your file system:

$ ls -l $HOME/coherence
total 16
drwxr-xr-x 2 pas     usergrp 4096 Feb 14 13:07 bin
drwxr-xr-x 3 pas     usergrp 4096 Feb 14 13:07 doc
drwxr-xr-x 3 pas     usergrp 4096 Feb 14 13:07 lib
-rw-r--r-- 1 pas     usergrp  100 Nov  9 14:49 product.xml
      

Verify JDK 1.6 is installed:

$ java -version
java version "1.6.0_23"
Java(TM) SE Runtime Environment (build 1.6.0_23-b05)
Java HotSpot(TM) 64-Bit Server VM (build 19.0-b09, mixed mode)
      

Set JRUBY_HOME to the directory where JRuby is installed and verify JRuby is in your path:

$ export JRUBY_HOME=$HOME/jruby-1.5.6
$ export PATH=$PATH:$JRUBY_HOME/bin
$ jruby -v

jruby 1.5.6 (ruby 1.8.7 patchlevel 249) (2010-12-03 9cf97c3) 
(Java HotSpot(TM) 64-Bit Server VM 1.6.0_23) [amd64-java]
      

You'll need to set the same environment in three terminal windows so keep track of the variable you set.

Verify you have Apache Ant installed and available in your path:

$ export PATH=$PATH:$HOME/apache-ant-1.8.2/bin
$ ant -version
Apache Ant(TM) version 1.8.2 compiled on December 20 2010
      

Running the Demo: Setting up Coherence

Edit the sample code file jruby_coherence/build.properties to define the Coherence home directory required to run the demo. You may also reduce the heap size of the JVM if you don't have enough memory on your machine.

# oracle.coherence.home
#
oracle.coherence.home=/home/pas/coherence

# jvmargs
#
# JVM args to pass into the command at runtime to set heap size etc

jvmargs=-server -showversion -Xms512m -Xmx512m
      

Edit jruby_coherence/src/db.properties to connect to your HR database schema. This is only required when we load the data from the database into the cache, so it's only used once:

# db properties
dburl=jdbc:oracle:thin:@localhost/orcl
dbuser=hr
dbpassword=welcome
      

Edit jruby_coherence/build.xml and locate the two proxy-host settings in the Run SECOND and Run THIRD comments. Set the value to specify the node host name or IP address of your machine:

  . . .
  <!-- Run SECOND
       Starts a storage disabled coherence proxy server node , enabling
       clients to access the coherence cluster. It does not store any data but
       is a member of the cluster to provide access to remote clients -->
  <target name="run-default-extend" depends="package" 
          description="Run a proxy server">
  . . .

      <sysproperty key="proxy.host" value="localhost"/>
  . . .
  <!-- Run THIRD
       This is the remote client node which loads data into the cache connecting
       to the proxy server node -->
  <target name="run-default-extend-client" depends="package" 
           description="Run the extend client to load data">
  . . .

      <sysproperty key="proxy.host" value="localhost"/>
  . . .
      

Also copy ojdbc6.jar into the unzipped demo directory jruby_coherence/lib:

$ cp ojdbc6.jar jruby_coherence/lib
$ ls -l jruby_coherence/lib
total 2128
-rw-r--r-- 1 pas usergrp 2152051 Oct 14 09:22 ojdbc6.jar
      

Change into the demonstation directory and start a cache server. Simply type in ant which will then run the default Ant task in build.xml (under the comment Run FIRST) for the demonstration. It will start a cache server node. It will run without returning the command prompt. (Note: This is the first request to start a cache server and before it does that, it must compile the required server/client classes which are then packaged into a oraclehrdemo.jar file prior to starting the cache server. This client JAR file is required as it contains the required classes and XML files to be able to query the cache data which we do at a later step.)

$ cd jruby_coherence
$ ant

Buildfile: /home/pas/jruby_coherence/jruby_coherence/build.xml

init:
    [mkdir] Created dir: /home/pas/jruby_coherence/jruby_coherence/classes

compile:

. . .

package:
      [jar] Building jar: /home/pas/jruby_coherence/jruby_coherence/lib/oraclehrdemo.jar

run-default:
     [echo] Starting cache server with jvm args : -server -showversion 
            -Xms512m -Xmx512m
     [java] java version "1.6.0_23"
     [java] Java(TM) SE Runtime Environment (build 1.6.0_23-b05)
     [java] Java HotSpot(TM) 64-Bit Server VM (build 19.0-b09, mixed mode)
     [java]

. . .

     [java]   (
     [java]   ClusterService{Name=Cluster, State=(SERVICE_STARTED, 
		STATE_JOINED), Id=0, Version=3.6, OldestMemberId=1}
     [java]   InvocationService{Name=Management, State=(SERVICE_STARTED), 
			Id=1, Version=3.1, OldestMemberId=1}
     [java]   PartitionedCache{Name=DistributedCache, State=(SERVICE_STARTED), 
		LocalStorage=enabled, PartitionCount=257, BackupCount=1, 
                AssignedPartitions=257, BackupPartitions=0}
     [java]   )
     [java]
     [java] Started DefaultCacheServer...
      

More than one cache server could be started, even on multiple machines. For this demo we only require one cache server node.

If you get a compile error with the cause "package oracle.jdbc.pool does not exist", confirm that you copied ojdbc6.jar to jruby_coherence/lib.

At this point we are going to start a proxy server. This sever will be storage disabled and is only used for Extend clients to query the cache. Start the proxy server on the same node as the cache server node was started. For more information see the section Setting Up Coherence*Extend in the Oracle Coherence Client Guide Release 3.6.1 manual.

A Coherence cluster must include an extend proxy service in order to accept extend client connections and must include a cache that is used by clients to retrieve and store data. Both the extend proxy service and caches are configured in the cluster's cache configuration deployment descriptor. Extend proxy services and caches are started as part of a cache server (DefaultCacheServer) process.

Start a new shell and set JRUBY_PATH and PATH environment variables again. Run the proxy server node as shown below using the ant target run-default-extend. Once again this won't return to the command prompt:

$ ant run-default-extend

Buildfile: /home/cjones/jruby_coherence/build.xml

init:

compile:

...

package:

run-default-extend:
     [echo] Starting cache server proxy with jvm args : 
                  -server -showversion -Xms512m -Xmx512m
     [java] java version "1.6.0_23"
     [java] Java(TM) SE Runtime Environment (build 1.6.0_23-b05)
     [java] Java HotSpot(TM) 64-Bit Server VM (build 19.0-b09, mixed mode)
     [java]
     [java] 2011-02-14 16:38:44.205/0.832 Oracle Coherence 3.6.1.0 <Info> 
(thread=main, member=n/a): Loaded operational configuration from 
"jar:file:/home/cjones/coherence/lib/coherence.jar!/tangosol-coherence.xml"
     [java] 2011-02-14 16:38:44.263/0.890 Oracle Coherence 3.6.1.0 <Info> 
(thread=main, member=n/a): Loaded operational overrides from 
"jar:file:/home/cjones/coherence/lib/coherence.jar!/tangosol-coherence-override-dev.xml"
     [java] 2011-02-14 16:38:44.274/0.901 Oracle Coherence 3.6.1.0 <D5> 
(thread=main, member=n/a): Optional configuration override 
"/tangosol-coherence-override.xml" is not specified
     [java] 2011-02-14 16:38:44.306/0.933 Oracle Coherence 3.6.1.0 <D5> 
(thread=main, member=n/a): Optional configuration override "/custom-mbeans.xml" 
is not specified
     [java]
     [java] Oracle Coherence Version 3.6.1.0 Build 19636
     [java]  Grid Edition: Development mode
     [java] Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
     [java]
     [java] 2011-02-14 16:38:45.081/1.708 Oracle Coherence GE 3.6.1.0 <Info> 
(thread=main, member=n/a): Loaded cache configuration from 
"jar:file:/home/cjones/jruby_coherence/lib/oraclehrdemo.jar!/coherence-cache-config.xml"
     [java] 2011-02-14 16:38:45.972/2.599 Oracle Coherence GE 3.6.1.0 <D4> 
(thread=main, member=n/a): TCMP bound to /130.35.70.193:8090 using SystemSocketProvider
     [java] 2011-02-14 16:38:49.439/6.066 Oracle Coherence GE 3.6.1.0 <Info> 
(thread=Cluster, member=n/a): Created a new cluster "cluster:0xC4DB" with Member(Id=1, 
Timestamp=2011-02-14 16:38:45.981, Address=130.35.70.193:8090, MachineId=59329, 
Location=site:usdhcp.oraclecorp.com,machine:dhcp-5op3-5op4-west-130-35-70-19,process:6490, 
Role=CoherenceServer, Edition=Grid Edition, Mode=Development, CpuCount=1, SocketCount=1) 
UID=0x822346C10000012E26C14DDDE7C11F9A
     [java] 2011-02-14 16:38:49.450/6.077 Oracle Coherence GE 3.6.1.0 <Info> 
(thread=main, member=n/a): Started cluster Name=cluster:0xC4DB
     [java]
     [java] Group{Address=224.3.6.0, Port=36000, TTL=4}
     [java]
     [java] MasterMemberSet
[java] (
[java] ThisMember=Member(Id=1, Timestamp=2011-02-14 16:38:45.981,
Address=130.35.70.193:8090, MachineId=59329,
Location=site:usdhcp.oraclecorp.com,machine:dhcp-5op3-5op4-west-130-35-70-19,process:6490,
Role=CoherenceServer)
[java] OldestMember=Member(Id=1, Timestamp=2011-02-14 16:38:45.981,
Address=130.35.70.193:8090, MachineId=59329, Location=site:usdhcp.oraclecorp.com,
machine:dhcp-5op3-5op4-west-130-35-70-19,process:6490, Role=CoherenceServer)
[java] ActualMemberSet=MemberSet(Size=1, BitSetCount=2
[java] Member(Id=1, Timestamp=2011-02-14 16:38:45.981, Address=130.35.70.193:8090,
MachineId=59329, Location=site:usdhcp.oraclecorp.com,
machine:dhcp-5op3-5op4-west-130-35-70-19,process:6490, Role=CoherenceServer)
[java] )
[java] RecycleMillis=1200000
[java] RecycleSet=MemberSet(Size=0, BitSetCount=0
[java] )
[java] )
[java]
[java] TcpRing{Connections=[]}
[java] IpMonitor{AddressListSize=0}
[java] [java] 2011-02-14 16:38:49.518/6.145 Oracle Coherence GE 3.6.1.0 <Info> (thread=Cluster, member=1): Loaded POF configuration from "jar:file:/home/cjones/jruby_coherence/lib/oraclehrdemo.jar!/hr-pof-config.xml" [java] 2011-02-14 16:38:49.572/6.199 Oracle Coherence GE 3.6.1.0 <Info> (thread=Cluster, member=1): Loaded included POF configuration from "jar:file:/home/cjones/coherence/lib/coherence.jar!/coherence-pof-config.xml" [java] 2011-02-14 16:38:49.922/6.549 Oracle Coherence GE 3.6.1.0 <D5> (thread=Invocation:Management, member=1): Service Management joined the cluster with senior service member 1 [java] 2011-02-14 16:38:50.397/7.024 Oracle Coherence GE 3.6.1.0 <D5> (thread=DistributedCache, member=1): Service DistributedCache joined the cluster with senior service member 1 [java] 2011-02-14 16:38:50.923/7.550 Oracle Coherence GE 3.6.1.0 <Info> (thread=Proxy:ExtendTcpProxyService:TcpAcceptor, member=1): TcpAcceptor now listening for connections on 130.35.70.193:9099 [java] 2011-02-14 16:38:50.939/7.570 Oracle Coherence GE 3.6.1.0 <D5> (thread=Proxy:ExtendTcpProxyService:TcpAcceptor, member=1): Started: TcpAcceptor{Name=Proxy:ExtendTcpProxyService:TcpAcceptor, State=(SERVICE_STARTED), ThreadCount=5, HungThreshold=0, TaskTimeout=0, Codec=Codec(Format=POF), Serializer=com.tangosol.io.pof.ConfigurablePofContext, PingInterval=0, PingTimeout=0, RequestTimeout=0, SocketProvider=SystemSocketProvider, LocalAddress=[/130.35.70.193:9099], SocketOptions{LingerTimeout=0, KeepAliveEnabled=true, TcpDelayEnabled=false}, ListenBacklog=0, BufferPoolIn=BufferPool(BufferSize=2KB, BufferType=DIRECT, Capacity=Unlimited), BufferPoolOut=BufferPool(BufferSize=2KB, BufferType=DIRECT, Capacity=Unlimited)} [java] 2011-02-14 16:38:50.954/7.581 Oracle Coherence GE 3.6.1.0 <D5> (thread=Proxy:ExtendTcpProxyService, member=1): Service ExtendTcpProxyService joined the cluster with senior service member 1 [java] 2011-02-14 16:38:50.959/7.586 Oracle Coherence GE 3.6.1.0 <Info> (thread=main, member=1): [java] Services [java] ( [java] ClusterService{Name=Cluster, State=(SERVICE_STARTED, STATE_JOINED), Id=0, Version=3.6, OldestMemberId=1} [java] InvocationService{Name=Management, State=(SERVICE_STARTED), Id=1, Version=3.1, OldestMemberId=1} [java] PartitionedCache{Name=DistributedCache, State=(SERVICE_STARTED), LocalStorage=disabled} [java] ProxyService{Name=ExtendTcpProxyService, State=(SERVICE_STARTED), Id=3, Version=3.2, OldestMemberId=1} [java] ) [java] [java] Started DefaultCacheServer... [java]

You can see from the MasterMemberSet section in the log that it joined the cluster and is now waiting for requests. At the end of the output log from your still running Cache Server Node (the first terminal window) you can also see the new node joined the cluster:

     [java] 2011-03-07 15:33:09.132/1332.883 Oracle Coherence GE 3.6.1.0 <D5> 
(thread=Cluster, member=1): Member(Id=2, Timestamp=2011-03-07 15:33:08.97, 
Address=10.0.2.15:8090, MachineId=2063, Location=process:7380, Role=CoherenceServer) 
joined Cluster with senior member 1
     [java] 2011-03-07 15:33:09.757/1333.506 Oracle Coherence GE 3.6.1.0 <D5> 
(thread=Cluster, member=1): Member 2 joined Service Management with senior member 1
     [java] 2011-03-07 15:33:10.592/1334.341 Oracle Coherence GE 3.6.1.0 <D5> 
(thread=Cluster, member=1): Member 2 joined Service DistributedCache with senior member 1
     [java] 2011-03-07 15:33:10.947/1334.696 Oracle Coherence GE 3.6.1.0 <D5> 
(thread=Cluster, member=1): Member 2 joined Service ExtendTcpProxyService with senior member 2


Open a third terminal window and set the environment again. Now run the extend client to load data into the cache. This simply loads the tables DEPARTMENTS and EMPLOYEES into the cache so we have some data to access from JRuby:

$ ant run-default-extend-client
Buildfile: /home/cjones/jruby_coherence/build.xml

init:

compile:

...   

package:

run-default-extend-client:
     [echo] Starting extend client with jvm args : -server -showversion -Xms512m -Xmx512m
     . . .

     [java] Oracle Coherence Version 3.6.1.0 Build 19636
     [java]  Grid Edition: Development mode
     [java] Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
     . . .
     [java] INFO: Connected to db with URL : jdbc:oracle:thin:@localhost/orcl
     [java] Mar 7, 2011 4:23:01 PM pas.au.coherence.hr.client.LoadDataIntoCoherence 
doLogMessage
     [java] INFO: DEPT CACHE size : 27
     [java] Mar 7, 2011 4:23:01 PM pas.au.coherence.hr.client.LoadDataIntoCoherence 
doLogMessage
     [java] INFO: EMP CACHE size : 107

BUILD SUCCESSFUL
Total time: 2 seconds
      

If you have the default schema data, the output will show that 27 Department records and 107 Employee records were loaded into the cache server.

Running the Demo: Accessing Coherence from JRuby

Now we are ready to create a JRuby extend client script which will display some data from the cache to the console.

Copy oraclehrdemo.jar that was created in step 2.5 to the hr_demo directory:

$ cp ~/jruby_coherence/lib/oraclehrdemo.jar ~/hr_demo
      

Review the sample file hr_demo/jruby_cohextendclient.rb ge the location of the Coherence jar to match your system. Also update the proxy.host if necessary: The JRuby script will coonect to the proxy server node as an extend client and query the two known cache names and output the cache data to the console.

# jruby_cohextendclient.rb

require 'java'
require File.dirname(__FILE__) + '/oraclehrdemo.jar'
# Update for your environment
require '/home/cjones/coherence/lib/coherence.jar'  

import com.tangosol.net.CacheFactory
import com.tangosol.net.NamedCache
import java.lang.System
import java.lang.Integer

puts "*********************************************************"
puts "Coherence 3.6 Oracle HR Extend Client Example from JRuby"
puts "*********************************************************"

print "Started at ", Time.now, "\n"

begin

  # setup required properties to connect to proxy server as extend client
  System.setProperty("tangosol.coherence.cacheconfig", "client-cache-config.xml")
  System.setProperty("tangosol.pof.enabled", "true")
  System.setProperty("tangosol.pof.config", "hr-pof-config.xml")
  System.setProperty("proxy.host", "localhost")  # Update for your environment

  # get named caches
  depscache = CacheFactory.getCache("dep-cache")
  empscache = CacheFactory.getCache("emp-cache")

  #retrieve size of caches
  print "\nCache [depscache] size  = " , depscache.size()
  print "\nCache [empscache] size  = " , empscache.size() , "\n\n"

  #show all departments
  print "** All DEPARTMENT Records **\n"
  deptiterator = depscache.entrySet().iterator

  while deptiterator.hasNext()
    entry = deptiterator.next()
    print "Key : [" , entry.getKey() , "] "
    print "Value : " , entry.getValue()
    puts
  end

  #get department 10
  deptid = Integer.new(10)
  deptrecord = depscache.get(deptid)
  print "\n** DEPARTMENT 10 **\n"
  print deptrecord
  puts

  #show all employees who have a JOB ROLE = 'SH_CLERK'
  print "\n** All EMPLOYEE Records with job role = 'SH_CLERK' **\n"
  filter = com.tangosol.util.filter.EqualsFilter.new("getJobId", "SH_CLERK")
  employees = empscache.entrySet(filter)

  print "Size of employees after filter applied = " , employees.size() , "\n"

  empiterator = employees.iterator()
  i = 0
  while empiterator.hasNext()
    empentry = empiterator.next()
    i = i + 1
    print "- Record " , i , "\n"
    print "Key : " , empentry.getKey() , " \n"
    print "Employee [employeeId=" , empentry.getValue().getEmployeeId() ,
          ", firstName=" , empentry.getValue().getFirstName() ,
          ", lastName=" , empentry.getValue().getLastName() ,
          ", jobId=" , empentry.getValue().getJobId() , "]\n"
  end

rescue
  print "\n** Error occured **\n"
	print "Failed to display HR data from cache ", $!, "\n\n"

end

print "\nEnded at ", Time.now, "\n"
      

In your third terminal change to the hr_demo directory and run jruby_cohextendclient.rb.


$ cd ~/hr_demo
$ jruby jruby_cohextendclient.rb
/home/cjones/jruby-1.5.6/lib/ruby/site_ruby/shared/builtin/javasupport/
core_ext/object.rb:99 warning: already initialized constant Integer
*********************************************************
Coherence 3.6 Oracle HR Extend Client Example from JRuby
*********************************************************
Started at Mon Mar 07 17:27:28 -0800 2011
2011-03-07 17:27:28.976/3.819 Oracle Coherence 3.6.1.0 <Info> 
(thread=main, member=n/a): Loaded operational configuration from 
"jar:file:/home/cjones/coherence/lib/coherence.jar!/tangosol-coherence.xml"
2011-03-07 17:27:29.037/3.880 Oracle Coherence 3.6.1.0 <Info> 
(thread=main, member=n/a): Loaded operational overrides from 
"jar:file:/home/cjones/coherence/lib/coherence.jar!/
tangosol-coherence-override-dev.xml"
2011-03-07 17:27:29.041/3.884 Oracle Coherence 3.6.1.0 <D5> 
(thread=main, member=n/a): Optional configuration override 
"/tangosol-coherence-override.xml" is not specified
2011-03-07 17:27:29.103/3.946 Oracle Coherence 3.6.1.0 <D5> 
(thread=main, member=n/a): Optional configuration override 
"/custom-mbeans.xml" is not specified

Oracle Coherence Version 3.6.1.0 Build 19636
 Grid Edition: Development mode
Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.

2011-03-07 17:27:29.718/4.561 Oracle Coherence GE 3.6.1.0 <Info> 
(thread=main, member=n/a): Loaded cache configuration from 
"jar:file:/home/cjones/hr_demo/./oraclehrdemo.jar!/client-cache-config.xml"
2011-03-07 17:27:30.183/5.026 Oracle Coherence GE 3.6.1.0 <Info> 
(thread=RemoteCache:TcpInitiator, member=n/a): Loaded POF configuration from 
"jar:file:/home/cjones/hr_demo/./oraclehrdemo.jar!/hr-pof-config.xml"
2011-03-07 17:27:30.232/5.075 Oracle Coherence GE 3.6.1.0 <Info> 
(thread=RemoteCache:TcpInitiator, member=n/a): Loaded included POF configuration from 
"jar:file:/home/cjones/coherence/lib/coherence.jar!/coherence-pof-config.xml"
2011-03-07 17:27:30.834/5.677 Oracle Coherence GE 3.6.1.0 <D5> 
(thread=RemoteCache:TcpInitiator, member=n/a): Started: 
TcpInitiator{Name=RemoteCache:TcpInitiator, State=(SERVICE_STARTED), ThreadCount=0, 
Codec=Codec(Format=POF), Serializer=com.tangosol.io.pof.ConfigurablePofContext, 
PingInterval=0, PingTimeout=0, RequestTimeout=0, ConnectTimeout=0, 
SocketProvider=SystemSocketProvider, RemoteAddresses=[/10.0.2.15:9099], 
SocketOptions{LingerTimeout=0, KeepAliveEnabled=true, TcpDelayEnabled=false}}
2011-03-07 17:27:30.861/5.704 Oracle Coherence GE 3.6.1.0 <D5> 
(thread=main, member=n/a): Connecting Socket to 10.0.2.15:9099
2011-03-07 17:27:30.872/5.716 Oracle Coherence GE 3.6.1.0 <Info> 
(thread=main, member=n/a): Connected Socket to 10.0.2.15:9099

Cache [depscache] size  = 27
Cache [empscache] size  = 107

** All DEPARTMENT Records **
Key : [100] Value : Department [departmentId=100 ,departmentName=Finance ,
managerId=108 ,locationId=1700]
Key : [20] Value : Department [departmentId=20 ,departmentName=Marketing ,
managerId=201 ,locationId=1800]
Key : [270] Value : Department [departmentId=270 ,departmentName=Payroll ,
managerId=0 ,locationId=1700]
Key : [90] Value : Department [departmentId=90 ,departmentName=Executive ,
managerId=100 ,locationId=1700]
Key : [150] Value : Department [departmentId=150 ,departmentName=Shareholder Services ,
managerId=0 ,locationId=1700]
Key : [250] Value : Department [departmentId=250 ,departmentName=Retail Sales ,
managerId=0 ,locationId=1700]
Key : [80] Value : Department [departmentId=80 ,departmentName=Sales ,
managerId=145 ,locationId=2500]
Key : [110] Value : Department [departmentId=110 ,departmentName=Accounting ,
managerId=205 ,locationId=1700]
Key : [50] Value : Department [departmentId=50 ,departmentName=Shipping ,
managerId=121 ,locationId=1500]
Key : [140] Value : Department [departmentId=140 ,departmentName=Control And Credit ,
managerId=0 ,locationId=1700]
Key : [60] Value : Department [departmentId=60 ,departmentName=IT ,managerId=103 ,
locationId=1400]
Key : [260] Value : Department [departmentId=260 ,departmentName=Recruiting ,
managerId=0 ,locationId=1700]
Key : [40] Value : Department [departmentId=40 ,departmentName=Human Resources ,
managerId=203 ,locationId=2400]
Key : [240] Value : Department [departmentId=240 ,departmentName=Government Sales ,
managerId=0 ,locationId=1700]
Key : [130] Value : Department [departmentId=130 ,departmentName=Corporate Tax ,
managerId=0 ,locationId=1700]
Key : [180] Value : Department [departmentId=180 ,departmentName=Construction ,
managerId=0 ,locationId=1700]
Key : [120] Value : Department [departmentId=120 ,departmentName=Treasury ,
managerId=0 ,locationId=1700]
Key : [70] Value : Department [departmentId=70 ,departmentName=Public Relations ,
managerId=204 ,locationId=2700]
Key : [200] Value : Department [departmentId=200 ,departmentName=Operations ,
managerId=0 ,locationId=1700]
Key : [230] Value : Department [departmentId=230 ,departmentName=IT Helpdesk ,
managerId=0 ,locationId=1700]
Key : [170] Value : Department [departmentId=170 ,departmentName=Manufacturing ,
managerId=0 ,locationId=1700]
Key : [220] Value : Department [departmentId=220 ,departmentName=NOC ,
managerId=0 ,locationId=1700]
Key : [190] Value : Department [departmentId=190 ,departmentName=Contracting ,
managerId=0 ,locationId=1700]
Key : [10] Value : Department [departmentId=10 ,departmentName=Administration ,
managerId=200 ,locationId=1700]
Key : [210] Value : Department [departmentId=210 ,departmentName=IT Support ,
managerId=0 ,locationId=1700]
Key : [160] Value : Department [departmentId=160 ,departmentName=Benefits ,
managerId=0 ,locationId=1700]
Key : [30] Value : Department [departmentId=30 ,departmentName=Purchasing ,
managerId=114 ,locationId=1700]

** DEPARTMENT 10 **
Department [departmentId=10 ,departmentName=Administration ,managerId=200 ,
locationId=1700]

** All EMPLOYEE Records with job role = 'SH_CLERK' **
Size of employees after filter applied = 20
- Record 1
Key : DepertmentIdKey [departmentId=50, employeeId=189]
Employee [employeeId=189, firstName=Jennifer, lastName=Dilly, jobId=SH_CLERK]
- Record 2
Key : DepertmentIdKey [departmentId=50, employeeId=182]
Employee [employeeId=182, firstName=Martha, lastName=Sullivan, jobId=SH_CLERK]
- Record 3
Key : DepertmentIdKey [departmentId=50, employeeId=197]
Employee [employeeId=197, firstName=Kevin, lastName=Feeney, jobId=SH_CLERK]
- Record 4
Key : DepertmentIdKey [departmentId=50, employeeId=191]
Employee [employeeId=191, firstName=Randall, lastName=Perkins, jobId=SH_CLERK]
- Record 5
Key : DepertmentIdKey [departmentId=50, employeeId=183]
Employee [employeeId=183, firstName=Girard, lastName=Geoni, jobId=SH_CLERK]
- Record 6
Key : DepertmentIdKey [departmentId=50, employeeId=187]
Employee [employeeId=187, firstName=Anthony, lastName=Cabrio, jobId=SH_CLERK]
- Record 7
Key : DepertmentIdKey [departmentId=50, employeeId=186]
Employee [employeeId=186, firstName=Julia, lastName=Dellinger, jobId=SH_CLERK]
- Record 8
Key : DepertmentIdKey [departmentId=50, employeeId=180]
Employee [employeeId=180, firstName=Winston, lastName=Taylor, jobId=SH_CLERK]
- Record 9
Key : DepertmentIdKey [departmentId=50, employeeId=199]
Employee [employeeId=199, firstName=Douglas, lastName=Grant, jobId=SH_CLERK]
- Record 10
Key : DepertmentIdKey [departmentId=50, employeeId=181]
Employee [employeeId=181, firstName=Jean, lastName=Fleaur, jobId=SH_CLERK]
- Record 11
Key : DepertmentIdKey [departmentId=50, employeeId=184]
Employee [employeeId=184, firstName=Nandita, lastName=Sarchand, jobId=SH_CLERK]
- Record 12
Key : DepertmentIdKey [departmentId=50, employeeId=185]
Employee [employeeId=185, firstName=Alexis, lastName=Bull, jobId=SH_CLERK]
- Record 13
Key : DepertmentIdKey [departmentId=50, employeeId=188]
Employee [employeeId=188, firstName=Kelly, lastName=Chung, jobId=SH_CLERK]
- Record 14
Key : DepertmentIdKey [departmentId=50, employeeId=194]
Employee [employeeId=194, firstName=Samuel, lastName=McCain, jobId=SH_CLERK]
- Record 15
Key : DepertmentIdKey [departmentId=50, employeeId=195]
Employee [employeeId=195, firstName=Vance, lastName=Jones, jobId=SH_CLERK]
- Record 16
Key : DepertmentIdKey [departmentId=50, employeeId=193]
Employee [employeeId=193, firstName=Britney, lastName=Everett, jobId=SH_CLERK]
- Record 17
Key : DepertmentIdKey [departmentId=50, employeeId=190]
Employee [employeeId=190, firstName=Timothy, lastName=Gates, jobId=SH_CLERK]
- Record 18
Key : DepertmentIdKey [departmentId=50, employeeId=196]
Employee [employeeId=196, firstName=Alana, lastName=Walsh, jobId=SH_CLERK]
- Record 19
Key : DepertmentIdKey [departmentId=50, employeeId=192]
Employee [employeeId=192, firstName=Sarah, lastName=Bell, jobId=SH_CLERK]
- Record 20
Key : DepertmentIdKey [departmentId=50, employeeId=198]
Employee [employeeId=198, firstName=Donald, lastName=OConnell, jobId=SH_CLERK]

Ended at Mon Mar 07 17:27:33 -0800 2011


The file hr_demo/jruby_hash_cohextend.rb copies the data from the department cache into a Ruby hash data structure and iterates over it to verify the copy was successful. Change the location of the Coherence jar to match your system and update the proxy.host if necessary:


# jruby_hash_cohextend.rb

require 'java'
require File.dirname(__FILE__) + '/oraclehrdemo.jar'
require '/home/cjones/coherence/lib/coherence.jar'  # Update for your environment

import com.tangosol.net.CacheFactory
import com.tangosol.net.NamedCache
import java.lang.System

puts "*********************************************************"
puts "Coherence 3.6 Oracle HR Extend Client Example from JRuby"
puts "*********************************************************"

print "Started at ", Time.now, "\n"

begin

  # setup required properties to connect to proxy server as extend client
  System.setProperty("tangosol.coherence.cacheconfig", 
                     "client-cache-config.xml")
  System.setProperty("tangosol.pof.enabled", "true")
  System.setProperty("tangosol.pof.config", "hr-pof-config.xml")
  # Update for your environment  
  System.setProperty("proxy.host", "localhost")  

  # get named cache dep-cache
  depscache = CacheFactory.getCache("dep-cache")

  #retrieve size of caches
  print "\nCache [depscache] size  = " , depscache.size() , "\n\n"

  deptiterator = depscache.entrySet().iterator

  depthash = Hash.new

  #copy all department cache data into ruby hash data structure
  while deptiterator.hasNext()
    entry = deptiterator.next()
    depthash[entry.getKey()] = entry.getValue()
  end

  #display hash
  print "Displaing hash data structure of department cache \n"
  print "Is Hash structure depthash empty : ", depthash.empty?, "\n"
  print "Hash structure depthash size: ", depthash.length, "\n"

  keys = depthash.keys
  for key in 0...depthash.length
    print "key  : ", keys[key], " "
    print "value : ", depthash[keys[key]], "\n"
  end

rescue
  print "\n** Error occured **\n"
	print "Failed to display HR data from cache ", $!, "\n\n"

end

print "\nEnded at ", Time.now, "\n"


Run jruby_hash_cohextend.rb and verify the output as follows:

$ jruby jruby_hash_cohextend.rb
*********************************************************
Coherence 3.6 Oracle HR Extend Client Example from JRUBY
*********************************************************
Started at Mon Mar 07 17:36:12 -0800 2011
2011-03-07 17:36:12.529/3.240 Oracle Coherence 3.6.1.0  
(thread=main, member=n/a): Loaded operational configuration from
"jar:file:/home/cjones/coherence/lib/coherence.jar!/tangosol-coherence.xml"
2011-03-07 17:36:12.565/3.275 Oracle Coherence 3.6.1.0
(thread=main, member=n/a): Loaded operational overrides from
"jar:file:/home/cjones/coherence/lib/coherence.jar!/
tangosol-coherence-override-dev.xml"
2011-03-07 17:36:12.567/3.277 Oracle Coherence 3.6.1.0
(thread=main, member=n/a): Optional configuration override
"/tangosol-coherence-override.xml" is not specified
2011-03-07 17:36:12.611/3.321 Oracle Coherence 3.6.1.0
(thread=main, member=n/a):
Optional configuration override "/custom-mbeans.xml" is not specified

Oracle Coherence Version 3.6.1.0 Build 19636
Grid Edition: Development mode
Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.

2011-03-07 17:36:13.070/3.780 Oracle Coherence GE 3.6.1.0
(thread=main, member=n/a): Loaded cache configuration from
"jar:file:/home/cjones/hr_demo/./oraclehrdemo.jar!/client-cache-config.xml"
2011-03-07 17:36:13.577/4.287 Oracle Coherence GE 3.6.1.0
(thread=RemoteCache:TcpInitiator, member=n/a):
Loaded POF configuration from
"jar:file:/home/cjones/hr_demo/./oraclehrdemo.jar!/hr-pof-config.xml"
2011-03-07 17:36:13.620/4.330 Oracle Coherence GE 3.6.1.0
(thread=RemoteCache:TcpInitiator, member=n/a):
Loaded included POF configuration from
"jar:file:/home/cjones/coherence/lib/coherence.jar!/coherence-pof-config.xml"
2011-03-07 17:36:14.249/4.959 Oracle Coherence GE 3.6.1.0
(thread=RemoteCache:TcpInitiator, member=n/a):
Started: TcpInitiator{Name=RemoteCache:TcpInitiator, State=(SERVICE_STARTED),
ThreadCount=0, Codec=Codec(Format=POF),
Serializer=com.tangosol.io.pof.ConfigurablePofContext, PingInterval=0,
PingTimeout=0, RequestTimeout=0, ConnectTimeout=0,
SocketProvider=SystemSocketProvider,
RemoteAddresses=[/10.0.2.15:9099], SocketOptions{LingerTimeout=0,
KeepAliveEnabled=true,
TcpDelayEnabled=false}}
2011-03-07 17:36:14.275/4.985 Oracle Coherence GE 3.6.1.0
(thread=main, member=n/a): Connecting Socket to 10.0.2.15:9099
2011-03-07 17:36:14.291/5.001 Oracle Coherence GE 3.6.1.0
(thread=main, member=n/a): Connected Socket to 10.0.2.15:9099

Cache [depscache] size = 27

Displaing hash data structure of department cache
Is Hash structure depthash empty : false
Hash structure depthash size: 27
key : 100 value : Department [departmentId=100 ,departmentName=Finance ,
managerId=108 ,locationId=1700]
key : 20 value : Department [departmentId=20 ,departmentName=Marketing ,
managerId=201 ,locationId=1800]
key : 270 value : Department [departmentId=270 ,departmentName=Payroll ,
managerId=0 ,locationId=1700]
key : 90 value : Department [departmentId=90 ,departmentName=Executive ,
managerId=100 ,locationId=1700]
key : 150 value : Department [departmentId=150 ,departmentName=Shareholder Services ,
managerId=0 ,locationId=1700]
key : 250 value : Department [departmentId=250 ,
departmentName=Retail Sales ,managerId=0 ,locationId=1700]
key : 80 value : Department [departmentId=80 ,
departmentName=Sales ,managerId=145 ,locationId=2500]
key : 110 value : Department [departmentId=110 ,
departmentName=Accounting ,managerId=205 ,locationId=1700]
key : 50 value : Department [departmentId=50 ,
departmentName=Shipping ,managerId=121 ,locationId=1500]
key : 140 value : Department [departmentId=140 ,
departmentName=Control And Credit ,managerId=0 ,locationId=1700]
key : 60 value : Department [departmentId=60 ,
departmentName=IT ,managerId=103 ,locationId=1400]
key : 260 value : Department [departmentId=260 ,
departmentName=Recruiting ,managerId=0 ,locationId=1700]
key : 40 value : Department [departmentId=40 ,
departmentName=Human Resources ,managerId=203 ,locationId=2400]
key : 240 value : Department [departmentId=240 ,
departmentName=Government Sales ,managerId=0 ,locationId=1700]
key : 130 value : Department [departmentId=130 ,
departmentName=Corporate Tax ,managerId=0 ,locationId=1700]
key : 180 value : Department [departmentId=180 ,
departmentName=Construction ,managerId=0 ,locationId=1700]
key : 120 value : Department [departmentId=120 ,
departmentName=Treasury ,managerId=0 ,locationId=1700]
key : 70 value : Department [departmentId=70 ,
departmentName=Public Relations ,managerId=204 ,locationId=2700]
key : 200 value : Department [departmentId=200 ,
departmentName=Operations ,managerId=0 ,locationId=1700]
key : 230 value : Department [departmentId=230 ,
departmentName=IT Helpdesk ,managerId=0 ,locationId=1700]
key : 170 value : Department [departmentId=170 ,
departmentName=Manufacturing ,managerId=0 ,locationId=1700]
key : 220 value : Department [departmentId=220 ,
departmentName=NOC ,managerId=0 ,locationId=1700]
key : 190 value : Department [departmentId=190 ,
departmentName=Contracting ,managerId=0 ,locationId=1700]
key : 10 value : Department [departmentId=10 ,
departmentName=Administration ,managerId=200 ,locationId=1700]
key : 210 value : Department [departmentId=210 ,
departmentName=IT Support ,managerId=0 ,locationId=1700]
key : 160 value : Department [departmentId=160 ,
departmentName=Benefits ,managerId=0 ,locationId=1700]
key : 30 value : Department [departmentId=30 ,
departmentName=Purchasing ,managerId=114 ,locationId=1700]

Ended at Mon Mar 07 17:36:15 -0800 2011


Finally we will insert a department into the Coherence cache. This won't persist the change into the database, as that topic is outside of the scope of this article. For more information on using Coherence with support for Read-Through, Write-Through, Refresh-Ahead and Write-Behind caching see this link here. The database is used to seed the cache (populate it) but the topic of udating the database is out of scope here.

Review hr_demo/jruby_insert_cohextend.rb. Change the location of the Coherence jar to match your system and update the proxy.host if necessary: In this JRuby script we create a new Department Java Object and insert it into the named cache "dep-cache" and then verify that indeed it's part of the cache.

# jruby_insert_cohextend.rb

require 'java'
require File.dirname(__FILE__) + '/oraclehrdemo.jar'
require '/home/cjones/coherence/lib/coherence.jar'  # Update for your environment

import com.tangosol.net.CacheFactory
import com.tangosol.net.NamedCache
import java.lang.System
import java.lang.Integer
import "pas.au.coherence.hr.server.Department"

puts "*********************************************************"
puts "Coherence 3.6 Oracle HR Extend Client Example from JRuby"
puts "*********************************************************"

print "Started at ", Time.now, "\n"

begin

  # setup required properties to connect to proxy server as extend client
  System.setProperty("tangosol.coherence.cacheconfig", "client-cache-config.xml")
  System.setProperty("tangosol.pof.enabled", "true")
  System.setProperty("tangosol.pof.config", "hr-pof-config.xml")
  System.setProperty("proxy.host", "localhost")  # Update for your environment

  # get named cache dep-cache
  depscache = CacheFactory.getCache("dep-cache")

  # retrieve size of cache
  print "\nCache [depscache] size  = " , depscache.size() , "\n\n"

  # insert department 280 into cache
  deptid = Integer.new(280)
  newdept = Department.new
  newdept.setDepartmentId(280)
  newdept.setDepartmentName("JRuby Department")

  depscache.put(deptid, newdept)

  puts "Department 280 added to cache"

  # retrieve record 280 ensuring it make it's way into the cache
  deptrecord = depscache.get(deptid)
  print "\n** DEPARTMENT 280 **\n"
  print deptrecord
  puts

  # retrieve size of cache to verify inserted record exists
  print "\nCache [depscache] size  = " , depscache.size() , "\n"

rescue
  print "\n** Error occured **\n"
	print "Failed to display HR data from cache ", $!, "\n\n"

end

print "\nEnded at ", Time.now, "\n"


Run jruby_insert_cohextend.rb as shown below:

$ jruby jruby_insert_cohextend.rb
/home/cjones/jruby-1.5.6/lib/ruby/site_ruby/shared/builtin/
javasupport/core_ext/object.rb:99 warning: already initialized constant Integer
*********************************************************
Coherence 3.6 Oracle HR Extend Client Example from JRUBY
*********************************************************
Started at Mon Mar 07 17:42:50 -0800 2011
2011-03-07 17:42:50.803/3.705 Oracle Coherence 3.6.1.0 <Info> 
(thread=main, member=n/a): Loaded operational configuration from 
"jar:file:/home/cjones/coherence/lib/coherence.jar!/tangosol-coherence.xml"
2011-03-07 17:42:50.832/3.734 Oracle Coherence 3.6.1.0 <Info> 
(thread=main, member=n/a): Loaded operational overrides from 
"jar:file:/home/cjones/coherence/lib/coherence.jar!/tangosol-coherence-override-dev.xml"
2011-03-07 17:42:50.835/3.737 Oracle Coherence 3.6.1.0 <D5> 
(thread=main, member=n/a): Optional configuration override 
"/tangosol-coherence-override.xml" is not specified
2011-03-07 17:42:50.890/3.792 Oracle Coherence 3.6.1.0 <D5> 
(thread=main, member=n/a): Optional configuration override "/custom-mbeans.xml" 
is not specified

Oracle Coherence Version 3.6.1.0 Build 19636
 Grid Edition: Development mode
Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.

2011-03-07 17:42:51.388/4.290 Oracle Coherence GE 3.6.1.0 <Info> 
(thread=main, member=n/a): Loaded cache configuration from 
"jar:file:/home/cjones/hr_demo/./oraclehrdemo.jar!/client-cache-config.xml"
2011-03-07 17:42:51.797/4.699 Oracle Coherence GE 3.6.1.0 <Info> 
(thread=RemoteCache:TcpInitiator, member=n/a): Loaded POF configuration from 
"jar:file:/home/cjones/hr_demo/./oraclehrdemo.jar!/hr-pof-config.xml"
2011-03-07 17:42:51.835/4.737 Oracle Coherence GE 3.6.1.0 <Info> 
(thread=RemoteCache:TcpInitiator, member=n/a): Loaded included POF configuration 
from "jar:file:/home/cjones/coherence/lib/coherence.jar!/coherence-pof-config.xml"
2011-03-07 17:42:52.362/5.264 Oracle Coherence GE 3.6.1.0 <D5> 
(thread=RemoteCache:TcpInitiator, member=n/a): Started: 
TcpInitiator{Name=RemoteCache:TcpInitiator, State=(SERVICE_STARTED), ThreadCount=0, 
Codec=Codec(Format=POF), Serializer=com.tangosol.io.pof.ConfigurablePofContext, 
PingInterval=0, PingTimeout=0, RequestTimeout=0, ConnectTimeout=0, 
SocketProvider=SystemSocketProvider, RemoteAddresses=[/10.0.2.15:9099], 
SocketOptions{LingerTimeout=0, KeepAliveEnabled=true, TcpDelayEnabled=false}}
2011-03-07 17:42:52.389/5.291 Oracle Coherence GE 3.6.1.0 <D5> 
(thread=main, member=n/a): Connecting Socket to 10.0.2.15:9099
2011-03-07 17:42:52.402/5.304 Oracle Coherence GE 3.6.1.0 
<Info> (thread=main, member=n/a): Connected Socket to 10.0.2.15:9099

Cache [depscache] size  = 27

Department 280 added to cache

** DEPARTMENT 280 **
Department [departmentId=280 ,departmentName=JRuby Department ,
managerId=0 ,locationId=0]

Cache [depscache] size  = 28

Ended at Mon Mar 07 17:42:52 -0800 2011


Conclusion

This article showed how we can access the Coherence Grid from a JRuby script as a Coherence Extend Client. From JRuby we were able to query the cache, search for specific records using there key, insert new records and copy cache data into Ruby hashes using Oracle Coherence In-Memory Data Grid.




Pas Apicella
[pas.apicella at oracle.com] is a principal technical support engineer at Oracle.