37 Processing JCache Entries

You can create and use JCache entry processors to modify cache entries.Entry processors are defined in the javax.cache.processor package and described in Chapter 9 of the Java Caching API Specification.

This chapter includes the following sections:

Overview of Processing JCache Entries

JCache supports the use of entry processors to perform updates on cache entries in an atomic, lock-free manner.In distributed cache environments, entries are processed in parallel across each of the different servers that holds cached data.

Entry processors are often used to make multiple changes to a cache in a single operation. For example, the entry processor may evaluate the values for a set of keys and then return different values based on some logic. The application can only view the final result of the processing and does not have access to intermediary results while the entry processor is being invoked. If an error occurs during processing, then an exception is thrown and no changes are made to the cache.

Entry processors must implements the EntryProcessor interface. Entry processor implementations are invoked on cache entries at runtime using the Cache.invoke and Cache.invokeAll methods.

Lastly, the Coherence JCache provider supports the use of JCache entry processors for local, partitioned, and pass-through caches. The provider makes use of the native Coherence InvocableMap.EntryProcessor API to implement JCache entry processors.

Creating Entry Processors

JCache entry processors must implement the EntryProcessor interface.The interface contains a single process method that is used to provide any processing logic for cache entries. Entry processors operate on MutableEntry entries. The MutableEntry interface ensures that entry processors have exclusive access to entries during processing. The process method also allows any arguments to be defined. The arguments can be passed to the processor when the processor is invoked.

Operations that are performed as part of an entry processor are not visible to an application and only take affect after the process method has completed. If an error occurs during the process method, then no changes are made to the cache entries.

Entry processors are not guaranteed to be executed in-process and therefore should always be serializable. For example, when using a Coherence partitioned cache, an entry processor may be executed on the cache server that contains the data to be processed. For Coherence caches, entry processors have the option of using POF serialization. However, POF is not portable among cache provider implementations.

Example 37-1 creates an entry processor that increments the value of a cache entry by one. The value of the entry is an integer. The key, value, and return types are explicitly defined to ensure type safety as required by the API.

Example 37-1 An Example EntryProcessor Implementation

import java.io.Serializable;
import javax.cache.processor.EntryProcessor;
import javax.cache.processor.EntryProcessorException;
import javax.cache.processor.MutableEntry;
 
public class MyEntryProcessor implements EntryProcessor <String, Integer,
   Integer>, Serializable 
   {
      public static final long serialVersionUID = 1L;
 
   public Integer process(MutableEntry<String, Integer> entry, 
      Object... arguments) throws EntryProcessorException 
      {

      if (entry.exists())
      {
         Integer current = entry.getValue();
         entry.setValue(current + 1);
         return current;
      } 
      else 
      {
         entry.setValue(0);
         return -1;
      }
   }
}

Using Entry Processors

The Cache API provides the invoke and invokeAll methods that are used to call an entry processor.The invoke method operates on a single cache entry and the invokeAll method operates on a set of cache entries. Entries are specified using the key name.

This section includes the following topics:

Invoking Entry Processors for a Single Key

Entry processors are invoked on a single key using the invoke method from a Cache instance. The following example demonstrates processing a single key. The examples uses the MyEntryProcessor implementation that was created in Example 37-1.

CachingProvider cachingProvider = Caching.getCachingProvider();
CacheManager cacheManager = cachingProvider.getCacheManager();

MutableConfiguration<String, Integer> config = 
   new MutableConfiguration<String, Integer>();
config.setTypes(String.class, Integer.class);

Cache<String, Integer> cache = cacheManager.createCache("MyCache", config);

String key = "k";
Integer value = 1;

cache.put(key, value);

System.out.println("The value is " + cache.get(key) + "\n");

cache.invoke(key, new MyEntryProcessor());

System.out.println("The value is now " + cache.get(key) + "\n");

Invoking Entry Processors for Multiple Keys

Entry processors are invoked on multiple keys using the invokeAll method. The keys must be grouped together using a Set implementation. The processor is invoked for each key in the set and the order in which keys are processed is not guaranteed. The following example demonstrates processing multiple keys that are grouped using the HashSet class and also uses the EntryProcessorResult.get method to retrieve the value returned by the entry processor for a specific key. The example uses the MyEntryProcessor implementation that was created in Example 37-1.

CachingProvider cachingProvider = Caching.getCachingProvider();
CacheManager cacheManager = cachingProvider.getCacheManager();

MutableConfiguration<String, Integer> config = 
   new MutableConfiguration<String, Integer>();
config.setTypes(String.class, Integer.class);

Cache<String, Integer> cache = cacheManager.createCache("MyCache", config);

String key = "k";
Integer value = 1;
String key1 = "k1";
Integer value1 = 1;

cache.put(key, value);
cache.put(key1, value1);

HashSet hs = new HashSet();
hs.add(key);
hs.add(key1);

Map<String, EntryProcessorResult<Integer>> map = cache.invokeAll(hs, 
   new MyEntryProcessor());
 
System.out.println("The value of k is now " + cache.get(key) + 
   " the result of invokeAll for k is previous value of " + map.get(key).get() +
   "\n");

System.out.println("The value of k1 is now " + cache.get(key1) +
   " the result of invokeAll for k1 is previous value of " + map.get(key1).get() +
   "\n");