Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Wiki Markup
h2. Cache Component

*Available as of Camel 2.1*

The *cache* component enables you to perform caching operations using EHCache as the Cache Implementation. The cache itself is created on demand or if a cache of that name already exists then it is simply utilized with its original settings.

This component supports producer and event based consumer endpoints.

The Cache consumer is an event based consumer and can be used to listen and respond to specific cache activities. If you need to perform selections from a pre-existing cache, used the processors defined for the cache component.

Maven users will need to add the following dependency to their {{pom.xml}} for this component:
{code:xml}
<dependency>
    <groupId>org.apache.camel</groupId>
    <artifactId>camel-cache</artifactId>
    <version>x.x.x</version>
    <!-- use the same version as your Camel core version -->
</dependency>
{code}

h3. URI format

{code}
cache://cacheName[?options]
{code}

You can append query options to the URI in the following format, {{?option=value&option=#beanRef&...}}

h3. Options
{div:class=confluenceTableSmall}
|| Name || Default Value || Description ||
| {{maxElementsInMemory}} | {{1000}} | The numer of elements that may be stored in the defined cache |
| {{memoryStoreEvictionPolicy}} | {{MemoryStoreEvictionPolicy.LFU}} | The number of elements that may be stored in the defined cache. Options include
     - MemoryStoreEvictionPolicy.LFU - Least frequently used
     - MemoryStoreEvictionPolicy.LRU - Least recently used 
     - MemoryStoreEvictionPolicy.FIFO - first in first out, the oldest element by creation time |
| {{overflowToDisk}} | {{true}} | Specifies whether cache may overflow to disk |
| {{eternal}} | {{false}} | Sets whether elements are eternal. If eternal, timeouts are ignored and the\\
 element is never expired. |
| {{timeToLiveSeconds}} | {{300}} | The maximum time between creation time and when an element expires.\\
 Is only used if the element is not eternal |
| {{timeToIdleSeconds}} | {{300}} | The maximum amount of time between accesses before an element expires |
| {{diskPersistent}} | {{true}} | Whether the disk store persists between restarts of the Virtual Machine.\\
 The default value is false. |
| {{diskExpiryThreadIntervalSeconds}} | {{120}} | The number of seconds between runs of the disk expiry thread. The default value\\
 is 120 seconds |
| {{cacheManagerFactory}} | {{null}} | *Camel 2.8:* If you want to use a custom factory which instantiates and creates the EHCache {{net.sf.ehcache.CacheManager}}. \\  \\  _Type:_ abstract org.apache.camel.component.cache.CacheManagerFactory |
| {{eventListenerRegistry}} | {{null}} | *Camel 2.8:* Sets a list of EHCache {{net.sf.ehcache.event.CacheEventListener}} for all new caches\- no need to define it per cache in EHCache xml config anymore. \\  \\  _Type:_ org.apache.camel.component.cache.CacheEventListenerRegistry |
| {{cacheLoaderRegistry}} | {{null}} | *Camel 2.8:* Sets a list of {{org.apache.camel.component.cache.CacheLoaderWrapper}} that extends EHCache {{net.sf.ehcache.loader.CacheLoader}} for all new caches\- no need to define it per cache in EHCache xml config anymore. \\  \\  _Type:_ org.apache.camel.component.cache.CacheLoaderRegistry |
{div}

h3. Sending/Receiving Messages to/from the cache

h4. Message Headers up to Camel 2.7
{div:class=confluenceTableSmall}
|| Header || Description ||
| {{CACHE_OPERATION}} | The operation to be performed on the cache. The valid options are
- GET
- CHECK
- ADD
- UPDATE
- DELETE
- DELETEALL\\
 The - {{GET}} and {{CHECK}} requires *Camel 2.3* onwards. |
| {{CACHE_KEY}} | The cache key used to -store CHECKthe Message in the cache. 
The cache key is optional if the CACHE_OPERATION is - ADD
        - UPDATE
        - DELETE
        - DELETEALL 
The {{GET}} and {{CHECK}} requires *Camel 2.3* onwards. |
| {{CACHE_KEY}} | The cache key used to store the Message in the cache. The cache key is optional if the CACHE_OPERATION is DELETEALL |
{div}

h4. Message Headers Camel 2.8+

{info:title=Header changes in Camel 2.8}
The header names and supported values have changed to be prefixed with 'CamelCache' and use mixed case.  This makes them easier to identify and keep separate from other headers.  The CacheConstants variable names remain unchanged, just their values have been changed.  Also, these headers are now being removed from the exchange after the cache operation is performed.
{info}

{div:class=confluenceTableSmall}
|| Header || Description ||
| {{CamelCacheOperation}} | The operation to be performed on the cache. The valid options are
        - CamelCacheGet       
        - CamelCacheCheck     
        - CamelCacheAdd
        - CamelCacheUpdate
        - CamelCacheDelete
        - CamelCacheDeleteAll 
| {{CamelCacheKey}} | The cache key used to store the Message in the cache. The cache key is optional if the CamelCacheOperation is CamelCacheDeleteAll |
{div}

h4. Cache Producer 
Sending data to the cache involves the ability to direct payloads in exchanges to be stored in a pre-existing or created-on-demand cache. The mechanics of doing this involve
    - setting the Message Exchange Headers shown above.
    - ensuring that the Message Exchange Body contains the message directed to the cache

h4. Cache Consumer 
Receiving data from the cache involves the ability of the CacheConsumer 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 CamelCacheGet/CamelCacheUpdate/CamelCacheDelete/CamelCacheDeleteAll). Upon such an activity taking place
    - an exchange containing Message Exchange Headers and a Message Exchange Body containing the just added/updated payload is placed and sent. 
    - in case of a CamelCacheDeleteAll operation, the Message Exchange Header CamelCacheKey and the Message Exchange Body are not populated.

h4. Cache Processors 
There are a set of nice processors with the ability to perform cache lookups and selectively replace payload content at the
    - body
    - token
    - xpath level

h3. Cache Usage Samples

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

h4. Example 2: Adding keys to the cache 
{code}
DELETEALL |
{div}

h4. Message Headers Camel 2.8\+

{info:title=Header changes in Camel 2.8}
The header names and supported values have changed to be prefixed with 'CamelCache' and use mixed case.  This makes them easier to identify and keep separate from other headers.  The CacheConstants variable names remain unchanged, just their values have been changed.  Also, these headers are now being removed from the exchange after the cache operation is performed.
{info}

{div:class=confluenceTableSmall}
|| Header || Description ||
| {{CamelCacheOperation}} | The operation to be performed on the cache. The valid options are
- CamelCacheGet
- CamelCacheCheck
- CamelCacheAdd
- CamelCacheUpdate
- CamelCacheDelete
- CamelCacheDeleteAll |
| {{CamelCacheKey}} | The cache key used to store the Message in the cache. The cache key is optional if the CamelCacheOperation is CamelCacheDeleteAll |
{div}

h4. Cache Producer

Sending data to the cache involves the ability to direct payloads in exchanges to be stored in a pre-existing or created-on-demand cache. The mechanics of doing this involve
- setting the Message Exchange Headers shown above.
- ensuring that the Message Exchange Body contains the message directed to the cache

h4. Cache Consumer

Receiving data from the cache involves the ability of the CacheConsumer 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 CamelCacheGet/CamelCacheUpdate/CamelCacheDelete/CamelCacheDeleteAll). Upon such an activity taking place
- an exchange containing Message Exchange Headers and a Message Exchange Body containing the just added/updated payload is placed and sent.
- in case of a CamelCacheDeleteAll operation, the Message Exchange Header CamelCacheKey and the Message Exchange Body are not populated.

h4. Cache Processors

There are a set of nice processors with the ability to perform cache lookups and selectively replace payload content at the
- body
- token
- xpath level

h3. Cache Usage Samples

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

h4. Example 2: Adding keys to the cache
{code}
RouteBuilder builder = new RouteBuilder() {
    public void configure() {
     from("direct:start")
     .setHeader(CacheConstants.CACHE_OPERATION, constant(CacheConstants.CACHE_OPERATION_ADD))
     .setHeader(CacheConstants.CACHE_KEY, constant("Ralph_Waldo_Emerson"))
     .to("cache://TestCache1")
   }
};
{code}

h4. Example 2: Updating existing keys in a cache
{code}
RouteBuilder builder = new RouteBuilder() {
    public void configure() {
     from("direct:start")
     .setHeader(CacheConstants.CACHE_OPERATION, constant(CacheConstants.CACHE_OPERATION_UPDATE))
     .setHeader(CacheConstants.CACHE_KEY, constant("Ralph_Waldo_Emerson"))
     .to("cache://TestCache1")
   }
};
{code}

h4. Example 3: Deleting existing keys in a cache
{code}
RouteBuilder builder = new RouteBuilder() {
    public void configure() {
     from("direct:start")
     .setHeader(CacheConstants.CACHE_OPERATION, constant(CacheConstants.CACHE_DELETE))
     .setHeader(CacheConstants.CACHE_KEY", constant("Ralph_Waldo_Emerson"))
     .to("cache://TestCache1")
   }
};
{code}

h4. Example 4: Deleting all existing keys in a cache
{code}
RouteBuilder builder = new RouteBuilder() {
    public void configure() {
     from("direct:start")
     .setHeader(CacheConstants.CACHE_OPERATION, constant(CacheConstants.CACHE_OPERATION_ADDDELETEALL))
     .setHeader(CacheConstants.CACHE_KEY, constant("Ralph_Waldo_Emerson"))
     .to("cache://TestCache1");
    }
};
{code}

h4. Example 25: UpdatingNotifying any existingchanges keysregistering in a cache Cache to Processors and other Producers
{code}
RouteBuilder builder = new RouteBuilder() {
    public void configure() {
     from("directcache:start//TestCache1")
     .setHeader(CacheConstants.CACHE_OPERATION, constant(CacheConstants.CACHE_OPERATION_UPDATE))process(new Processor() {
     .setHeader(CacheConstants.CACHE_KEY, constant("Ralph_Waldo_Emerson"))
   public void process(Exchange exchange)
        .to("cache://TestCache1")
   }
};
{code}

h4. Example 3: Deleting existingthrows keysException in{
 a cache 
{code}
RouteBuilder builder = new RouteBuilder() {
   String publicoperation void= configure(String) {exchange.getIn().getHeader(CacheConstants.CACHE_OPERATION);
     from("direct:start")
     .setHeader(CacheConstants.CACHE_OPERATION, constant      String key = (String) exchange.getIn().getHeader(CacheConstants.CACHE_DELETEKEY));
     .setHeader(CacheConstants.CACHE_KEY", constant("Ralph_Waldo_Emerson"))      Object body = exchange.getIn().getBody();
     .to("cache://TestCache1"      // Do something
        }
     })
   }
};
{code}

h4. Example 46: DeletingUsing Processors allto existingselectively keysreplace inpayload awith cache values
{code}
RouteBuilder builder = new RouteBuilder() {
    public void configure() {
     //Message Body Replacer
     from("directcache:start//TestCache1")
     .setHeaderfilter(header(CacheConstants.CACHE_OPERATION, constant(CacheConstants.CACHE_DELETEALLKEY).isEqualTo("greeting"))
     .toprocess(new CacheBasedMessageBodyReplacer("cache://TestCache1","farewell");)
    }
};
{code}

h4. Example 5: Notifying any changes registering in a Cache to Processors and other Producers
{code}
RouteBuilder builder = new RouteBuilder() {
    public void configure() {
     from .to("direct:next");

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

    //Message XPath replacer
     throws Exception {
           String operation = (String) exchange.getIn().getHeader(CacheConstants.CACHE_OPERATION);from("cache://TestCache1").
    .filter(header(CacheConstants.CACHE_KEY).isEqualTo("XML_FRAGMENT"))
           String key = (String) exchange.getIn().getHeader(CacheConstants.CACHE_KEY);.process(new CacheBasedXPathReplacer("cache://TestCache1","book1","/books/book1"))
    .process       Object body = exchange.getIn().getBody();
           // Do something
        } 
     })(new CacheBasedXPathReplacer("cache://TestCache1","book2","/books/book2"))
    .to("direct:next");
   } 
};
{code}

h4. Example 67: UsingGetting Processorsan toentry selectivelyfrom replace payload with cache valuesthe Cache
{code}
RouteBuilder builder = new RouteBuilder() {from("direct:start")
    // Prepare headers
   public void configure() { .setHeader(CacheConstants.CACHE_OPERATION, constant(CacheConstants.CACHE_OPERATION_GET))
     //Message Body Replacer.setHeader(CacheConstants.CACHE_KEY, constant("Ralph_Waldo_Emerson")).
     from.to("cache://TestCache1").
    // .filterCheck if entry was not found
    .choice().when(header(CacheConstants.CACHE_KEYELEMENT_WAS_FOUND).isEqualToisNull("greeting")).
       .process(new CacheBasedMessageBodyReplacer("cache://TestCache1","farewell"))
     .to("direct:next");  
 If not found, get the payload and put it to cache
    //Message Token replacer
    from.to("cache://TestCache1cxf:bean:someHeavyweightOperation").
        .filter(headersetHeader(CacheConstants.CACHE_OPERATION, constant(CacheConstants.CACHE_OPERATION_KEY).isEqualTo("quote"))ADD))
        .process(new CacheBasedTokenReplacer("cache://TestCache1","novel","#novel#"))
setHeader(CacheConstants.CACHE_KEY, constant("Ralph_Waldo_Emerson"))
      .process(new CacheBasedTokenReplacer  .to("cache://TestCache1","author","#author#"))
    .process(new CacheBasedTokenReplacer("cache://TestCache1","number","#number#"))end()
    .to("direct:nextnextPhase");
{code}

h4. Example 8:  //Message XPath replacer
    from("cache://TestCache1").
    .filter(header(CacheConstants.CACHE_KEY).isEqualTo("XML_FRAGMENT"Checking for an entry in the Cache

Note: CHECK command tests existence of the entry in the cache but doesn't place message to the body.
{code}
from("direct:start")
    // Prepare headers
    .setHeader(CacheConstants.CACHE_OPERATION, constant(CacheConstants.CACHE_OPERATION_CHECK))
    .process(new CacheBasedXPathReplacer("cache://TestCache1","book1","/books/book1"))setHeader(CacheConstants.CACHE_KEY, constant("Ralph_Waldo_Emerson")).
    .process (new CacheBasedXPathReplacer(to("cache://TestCache1","book2","/books/book2"))).
    // Check if entry was not found
    .to("direct:next");.choice().when(header(CacheConstants.CACHE_ELEMENT_WAS_FOUND).isNull()).
   }
};
{code}

h4. Example 7: Getting an entry from the Cache
{code}
from("direct:start")  // If not found, get the payload and put it to cache
        .to("cxf:bean:someHeavyweightOperation").
    // Prepare headers
    .setHeader(CacheConstants.CACHE_OPERATION, constant(CacheConstants.CACHE_OPERATION_GETADD))
        .setHeader(CacheConstants.CACHE_KEY, constant("Ralph_Waldo_Emerson")).
        .to("cache://TestCache1").
    // Check if entry was not found
    .choice().when(header(CacheConstants.CACHE_ELEMENT_WAS_FOUND).isNull()).
        // If not found, get the payload and put it to cache
        .to("cxf:bean:someHeavyweightOperation").
        .setHeader(CacheConstants.CACHE_OPERATION, constant(CacheConstants.CACHE_OPERATION_ADD))
        .setHeader(CacheConstants.CACHE_KEY, constant("Ralph_Waldo_Emerson"))
        .to("cache://TestCache1")
    .end()
    .to("direct:nextPhase");
{code}

h4. Example 8: Checking for an entry in the Cache
Note: CHECK command tests existence of the entry in the cache but doesn't place message to the body.
{code}
from("direct:start")
    // Prepare headers
    .setHeader(CacheConstants.CACHE_OPERATION, constant(CacheConstants.CACHE_OPERATION_CHECK))
    .setHeader(CacheConstants.CACHE_KEY, constant("Ralph_Waldo_Emerson")).
    .to("cache://TestCache1").
    // Check if entry was not found
    .choice().when(header(CacheConstants.CACHE_ELEMENT_WAS_FOUND).isNull()).
        // If not found, get the payload and put it to cache
        .to("cxf:bean:someHeavyweightOperation").
        .setHeader(CacheConstants.CACHE_OPERATION, constant(CacheConstants.CACHE_OPERATION_ADD))
        .setHeader(CacheConstants.CACHE_KEY, constant("Ralph_Waldo_Emerson"))
        .to("cache://TestCache1")
    .end();
{code}


h3. Management of EHCache

EHCache has its own statistics and management from [JMX|Camel JMX].

Here's a snippet on how to expose them via JMX in a Spring application context:
{code:xml}
<bean id="ehCacheManagementService" class="net.sf.ehcache.management.ManagementService" init-method="init" lazy-init="false">
  <constructor-arg>
    <bean class="net.sf.ehcache.CacheManager" factory-method="getInstance"/>
  </constructor-arg>
  <constructor-arg>
    <bean class="org.springframework.jmx.support.JmxUtils" factory-method="locateMBeanServer"/>
  </constructor-arg>
  <constructor-arg value="true"/>
  <constructor-arg value="true"/>
  <constructor-arg value="true"/>
  <constructor-arg value="true"/>
</bean>
{code}

Of course you can do the same thing in straight Java:
{code:java}
ManagementService.registerMBeans(CacheManager.getInstance(), mbeanServer, true, true, true, true);
{code}

You can get cache hits, misses, in-memory hits, disk hits, size stats this way. You can also change CacheConfiguration parameters on the fly.

.end();
{code}


h3. Management of EHCache

EHCache has its own statistics and management from [JMX|Camel JMX].

Here's a snippet on how to expose them via JMX in a Spring application context:
{code:xml}
<bean id="ehCacheManagementService" class="net.sf.ehcache.management.ManagementService" init-method="init" lazy-init="false">
  <constructor-arg>
    <bean class="net.sf.ehcache.CacheManager" factory-method="getInstance"/>
  </constructor-arg>
  <constructor-arg>
    <bean class="org.springframework.jmx.support.JmxUtils" factory-method="locateMBeanServer"/>
  </constructor-arg>
  <constructor-arg value="true"/>
  <constructor-arg value="true"/>
  <constructor-arg value="true"/>
  <constructor-arg value="true"/>
</bean>
{code}

Of course you can do the same thing in straight Java:
{code:java}
ManagementService.registerMBeans(CacheManager.getInstance(), mbeanServer, true, true, true, true);
{code}

You can get cache hits, misses, in-memory hits, disk hits, size stats this way. You can also change CacheConfiguration parameters on the fly.

h3. Cache replication Camel 2.8\+

Camel Cache component is able to distribute cache across server nodes using several different replication mechanisms like: RMI, JGroups, JMS and Cache Server.


There are two different ways To make it work:

*1.* you need to configure {{ehcache.xml}} manually

OR

*2.* you have to take care about these three options:
- cacheManagerFactory
- eventListenerRegistry
- cacheLoaderRegistry


Configuring Camel Cache replication using 1st option\- the {{ehcache.xml}} file is a bit hard work as you have to configure all caches separately. So in a situation when the all names of caches are not know, using {{ehcache.xml}} is not a good idea.

The second option is much better when you want to use many different caches as you do not need to define options per cache. This is because replication options are set per {{CacheManager}} and per {{CacheEndpoint}}. Also it is the only way when cache names are not know at the development phase.


{note}
It might be useful to read EHCache manual to get better understanding of Camel Cache replication mechanism.
{note}

h4. Example: JMS cache replication

JMS replication is the most powerful and secured way. Used altogether with Camel Cache replication options is also the most easy way.
This example is available on [separated page|cacheReplicationJMSExample].