Friday, August 14, 2009

Developed a camel-cache component to store, perform lookups and listen for cache events on a camel route

I have recently submitted a Camel cache component which is slated for release in Apache Camel version 2.1 in Septermber, 09

http://issues.apache.org/activemq/browse/CAMEL-1868

The Camel Caching component has the following abilities

a> In Producer mode, the component provides the ability to direct payloads in exchanges to a stored in a pre-existing or created-on-demand Cache. The producer mode supports operations to ADD/UPDATE/DELETE/DELETEALL elements in a cache. (Examples goven below)

b> In Consumer mode, the component provides the ability to listen on a pre-existing or created-on-demand Cache using an event Listener and receive automatic notifications when any cache activity take place (i.e ADD/UPDATE/DELETE/DELETEALL). Upon such an activity taking place, an exchange containing header elements describing the operation and cachekey and a body containing the just added/updated payload is placed and sent. In case of a DELETEALL operation the body of the exchanage is not populated.

The cache itself may be created on demand or if a cache of that name already exists then it is simply utilized with its original settings. The URL itself may take the following form

Configuring the cache
from("cache://MyApplicationCache" +
"?maxElementsInMemory=1000" +
"&memoryStoreEvictionPolicy=" +
"MemoryStoreEvictionPolicy.LFU" +
"&overflowToDisk=true" +
"&eternal=true" +
"&timeToLiveSeconds=300" +
"&timeToIdleSeconds=true" +
"&diskPersistent=true" +
"&diskExpiryThreadIntervalSeconds=300")

// Note that all the attributes of the above URL are
// standard ehCache settings that may be set at
// Cache creation.

Given below are examples of how to create/set routes:

Producer Example 1: Adding keys to the cache with a body received from direct:start

RouteBuilder builder = new RouteBuilder() {
public void configure() {
from("direct:start")
.setHeader("CACHE_OPERATION", constant("ADD"))
.setHeader("CACHE_KEY", constant("Ralph_Waldo_Emerson"))
.to("cache://TestCache1")
}
};


Producer Example 2: Updating existing keys in a cache with a body received from direct:start

RouteBuilder builder = new RouteBuilder() {
public void configure() {
from("direct:start")
.setHeader("CACHE_OPERATION", constant("UPDATE"))
.setHeader("CACHE_KEY", constant("Ralph_Waldo_Emerson"))
.to("cache://TestCache1")
}
};


Producer Example 3: Deleting existing keys in a cache with a body received from direct:start

RouteBuilder builder = new RouteBuilder() {
public void configure() {
from("direct:start")
.setHeader("CACHE_OPERATION", constant("DELETE"))
.setHeader("CACHE_KEY", constant("Ralph_Waldo_Emerson"))
.to("cache://TestCache1")
}
};


Producer Example 4: Deleting all keys in a cache with a body received from direct:start

RouteBuilder builder = new RouteBuilder() {
public void configure() {
from("direct:start")
.setHeader("CACHE_OPERATION", constant("ADD"))
.setHeader("CACHE_KEY", constant("Ralph_Waldo_Emerson"))
.to("cache://TestCache1");

from("direct:start")
.setHeader("CACHE_OPERATION", constant("ADD"))
.setHeader("CACHE_KEY", constant("Ralph_Waldo_Emerson2"))
.to("cache://TestCache1");

from("direct:start")
.setHeader("CACHE_OPERATION", constant("DELETEALL"))
.to("cache://TestCache1");
}
};


Consumer Example 1: Notifying any changes registering in a Cache to Processors and other Producers

// Note: in this example the consumer is
// created first and then 3 routes
// send different message as Cache Producers

RouteBuilder builder = new RouteBuilder() {
public void configure() {
from("cache://TestCache1")
.process(new Processor() {
public void process(Exchange exchange)
throws Exception {
String operation = (String) exchange.getIn().getHeader("CACHE_OPERATION");
String key = (String) exchange.getIn().getHeader("CACHE_KEY");
Object body = exchange.getIn().getBody();
// Do something
}
})
}
};


I have added a set of nice processors to the camel-cache component to provide the ability to perform cache lookups and selectively replace payload content at the
- body
- token
- xpath level

The mechanics of doing this are as follows

Producer Example 3: Deleting existing keys in a cache with a body received from direct:start

RouteBuilder builder = new RouteBuilder() {
public void configure() {
//Message Body Replacer
from("cache://TestCache1")
.filter(header("CACHE_KEY").isEqualTo("greeting"))
.process(new CacheBasedMessageBodyReplacer("cache://TestCache1", "farewell"))
.to("direct:next");

//Message Token replacer
from("cache://TestCache1")
.filter(header("CACHE_KEY").isEqualTo("quote"))
.process(new CacheBasedTokenReplacer("cache://TestCache1", "novel", "#novel#"))
.process(new CacheBasedTokenReplacer("cache://TestCache1", "author", "#author#"))
.process(new CacheBasedTokenReplacer("cache://TestCache1", "number", "#number#"))
.to("direct:next");

from("cache://TestCache1").
.filter(header("CACHE_KEY").isEqualTo("XML_FRAGMENT"))
.process(new CacheBasedXPathReplacer("cache://TestCache1", "book1", "/books/book1"))
.process (new CacheBasedXPathReplacer("cache://TestCache1", "book2", "/books/book2"))
.to("direct:next");
}
};