Friday, February 15, 2008

Multiplexed CacheStore

Oracle Coherence provides a hook called CacheStore that can be used to store and load data to and from an external data source. Typically this external datasource is one relational database. You may encounter a scenario to design a disaster recovery and high availability and the data in Coherence grid needs to be replicated to multiple datasources. For example, to a relational database and to another Coherence cluster. Coherence configuration does not support a composite CacheStore for one Cache configuration. There are two approaches that can be taken:

1. Write everything in one CacheStore.
2. Make it pluggable with a Multiplexed CacheStore.

For the sake of clean code and domain scope it is better to avoid the option (1). What is the (2)nd?

Its nothing but a sweet use of Java reflection and JDK1.5 new threading model. Here are the steps to how you do it:

1. Create a MultiplexCacheStore that implements CacheStore interface.
2. Configure the coherence cache config to set MultiplexCacheStore as the CacheStore provider.
3. Write different CacheStore implementations each for each data source that needs data replicated to.
4. Pass the class names of those other cache stores to the MuliplexCacheStore's constructor.
5. Load the cache stores in MultiplexCacheStore. One way is:
for-each cachestore:
ExternalizableHelper.loadClass (className, getContextClassLoader(), null);
6. Initialize each CacheStore as (CacheStore)cClass.newInstance (). Define a default constructor in each of your CacheStore implementations.
7. Add the following method in MultiplexCacheStore that prepares Java Task to be submitted:



// -- Initialize the ExecutorService
service = Executors.newSingleThreadExecutor(new ThreadFactory() {
// -- Return a new daemon thread
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setDaemon(true);
return thread;
}
});

// -- Now some methods...
private void createTasks(CacheStore[] cS, String method, Object[] o) {
for (int i = 0; i < cS.length; i++) {
Task task = new Task(cS[i], method, o);
submitTask(task);
}
}

private void submitTask(Runnable r) {
service.execute(r);
}

Now, use the following example to submit store tasks on each of the CacheStores you want data replicated:

// -- sCS and eCS are two CacheStores that MultiplexCacheStore is composed of
public void store(Object key, Object value) {
createTasks (new CacheStore [] {sCS, eCS}, "store",
new Object[] { key, value });
}


// -- Repeat the same for the other store and erase methods. load and loadAll doesn't have to
be from all the datasources, just pick one.
Tasks can be defined as follows:

private class Task implements Runnable {
private CacheStore cS;
private String methodName;
private Object[] args;

public Task(CacheStore cS, String methodName, Object[] args) {
this.cS = cS;
this.methodName = methodName;
this.args = args;
}

/**
* The method invokes a method specified by the methodName on the
* specified CacheStore instance.
* @since 12/02/2008
*/
public void run() {
try {
ClassHelper.invoke(cS, methodName, args);
} catch (Exception e) {
// -- whatever
}
}
}

Enjoy Coherence!

No comments: