6. Working with the GemFire APIs

Once the GemFire cache and regions have been configured they can injected and used inside application objects. This chapter describes the integration with Spring's transaction management functionality and DaoException hierarchy. It also covers support for dependency injection of GemFire managed objects.

6.1 Exception translation

Using a new data access technology requires not just accommodating to a new API but also handling exceptions specific to that technology. To accommodate this case, Spring Framework provides a technology agnostic, consistent exception hierarchy that abstracts one from proprietary (and usually checked) exceptions to a set of focused runtime exceptions. As mentioned in the Spring Framework documentation, exception translation can be applied transparently to your data access objects through the use of the @Repository annotation and AOP by defining a PersistenceExceptionTranslationPostProcessor bean. The same exception translation functionality is enabled when using GemFire as long as at least a CacheFactoryBean is declared, e.g., using a <gfe:cache/> declaration) as it acts as an exception translator which is automatically detected by the Spring infrastructure and used accordingly.

6.2 GemfireTemplate

As with many other high-level abstractions provided by the Spring projects, Spring Data GemFire provides a template that simplifies GemFire data access. The class provides several one-line methods, for common region operations but also the ability to execute code against the native GemFire API without having to deal with GemFire checked exceptions for example through the GemfireCallback.

The template class requires a GemFire Region instance and once configured is thread-safe and should be reused across multiple classes:

<bean id="gemfireTemplate" class="org.springframework.data.gemfire.GemfireTemplate" p:region-ref="someRegion"/>

Once the template is configured, one can use it alongside GemfireCallback to work directly with the GemFire Region, without having to deal with checked exceptions, threading or resource management concerns:

template.execute(new GemfireCallback<Iterable<String>>() {
  public Iterable<String> doInGemfire(Region reg) throws GemFireCheckedException, GemFireException {
    // working against a Region of String
    Region<String, String> region = reg;

    region.put("1", "one");
    region.put("3", "three");

    return region.query("length < 5");
  }
});

For accessing the full power of the GemFire query language, one can use the find and findUnique which, as opposed to the query method, can execute queries across multiple regions, execute projections, and the like. The find method should be used when the query selects multiple items (through SelectResults) and the latter, findUnique, as the name suggests, when only one object is returned.

6.3 Support for Spring Cache Abstraction

Since 1.1, Spring GemFire provides an implementation for Spring 3.1 cache abstraction. To use GemFire as a backing implementation, simply add GemfireCacheManager to your configuration:

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:cache="http://www.springframework.org/schema/cache"
  xmlns:gfe="http://www.springframework.org/schema/gemfire"
  xmlns:p="http://www.springframework.org/schema/p"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  	  http://www.springframework.org/schema/gemfire http://www.springframework.org/schema/gemfire/spring-gemfire.xsd
      http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
  <!-- turn on declarative caching -->
  <cache:annotation-driven />
  
  <gfe:cache id="gemfire-cache" />
  <!-- declare GemFire Cache Manager -->
  <bean id="cacheManager" class="org.springframework.data.gemfire.support.GemfireCacheManager" p:cache-ref="gemfire-cache">
</beans>
  	
  	

6.4 Transaction Management

One of the most popular features of Spring Framework is transaction management. If you are not familiar with it, we strongly recommend looking into it as it offers a consistent programming model that works transparently across multiple APIs that can be configured either programmatically or declaratively (the most popular choice).

For GemFire, Spring Data GemFire provides a dedicated, per-cache, transaction manager that once declared, allows region operations to be executed atomically through Spring:

<gfe:transaction-manager id="tx-manager" cache-ref="cache"/>
[Note]Note
The example above can be simplified even more by eliminating the cache-ref attribute if the GemFire cache is defined under the default name gemfireCache. As with the other Spring Data GemFire namespace elements, if the cache name is not configured, the aforementioned naming convention will used. Additionally, the transaction manager name, if not specified is gemfireTransactionManager.

Note that currently GemFire supports optimistic transactions with read committed isolation. Furthermore, to guarantee this isolation, developers should avoid making in-place changes, that is manually modifying the values present in the cache. To prevent this from happening, the transaction manager configured the cache to use copy on read semantics, meaning a clone of the actual value is created, each time a read is performed. This behavior can be disabled if needed through the copyOnRead property. For more information on the semantics of the underlying GemFire transaction manager, see the GemFire documentation.

6.5 GemFire Continuous Query Container

A powerful functionality offered by GemFire is continuous querying (or CQ). In short, CQ allows one to create a query and automatically be notified when new data that gets added to GemFire matches the query. Spring GemFire provides dedicated support for CQs through the org.springframework.data.gemfire.listener package and its listener container; very similar in functionality and naming to the JMS integration in Spring Framework; in fact, users familiar with the JMS support in Spring, should feel right at home. Basically Spring Data GemFire allows methods on POJOs to become end-points for CQ - simply define the query and indicate the method that should be notified when there is a match - Spring Data GemFire takes care of the rest. This is similar Java EE's message-driven bean style, but without any requirement for base class or interface implementations, based on GemFire.

[Note]Note

Currently, continuous queries are supported by GemFire only in client/server topologies. Additionally the pool used is required to have the subscription property enabled. Please refer to the documentation for more information.

6.5.1 Continuous Query Listener Container

Spring Data GemFire simplifies the creation, registration, life-cycle and dispatch of CQs by taking care of the infrastructure around them through ContinuousQueryListenerContainer which does all the heavy lifting on behalf of the user - users familiar with EJB and JMS should find the concepts familiar as it is designed as close as possible to the support in Spring Framework and its message-driven POJOs (MDPs)

ContinuousQueryListenerContainer acts as an event (or message) listener container; it is used to receive the events from the registered CQs and drive the POJOs that are injected into it. The listener container is responsible for all threading of message reception and dispatches into the listener for processing. It acts as the intermediary between an EDP (Event Driven POJO) and the event provider and takes care of creation and registration of CQs (to receive events), resource acquisition and release, exception conversion and the like. This allows you as an application developer to write the (possibly complex) business logic associated with receiving an event (and reacting to it), and delegates boilerplate GemFire infrastructure concerns to the framework.

The container is fully customizable - one can chose either to use the CQ thread to perform the dispatch (synchronous delivery) or a new thread (from an existing pool for examples) for an asynchronous approach by defining the suitable java.util.concurrent.Executor (or Spring's TaskExecutor). Depending on the load, the number of listeners or the runtime environment, one should change or tweak the executor to better serve her needs - in particular in managed environments (such as app servers), it is highly recommended to pick a a proper TaskExecutor to take advantage of its runtime.

6.5.2 The ContinuousQueryListenerAdapter and ContinuousQueryListener

The ContinuousQueryListenerAdapter class is the final component in Spring Data GemFire CQ support: in a nutshell, it allows you to expose almost any class as a EDP (there are of course some constraints) - it implements ContinuousQueryListener, a simpler listener interface similar to GemFire CqListener.

Consider the following interface definition. Notice the various event handling methods and their parameters:

public interface EventDelegate {
     void handleEvent(CqEvent event);
     void handleEvent(Operation baseOp);
     void handleEvent(Object key);
     void handleEvent(Object key, Object newValue);
     void handleEvent(Throwable th);
     void handleQuery(CqQuery cq);
     void handleEvent(CqEvent event, Operation baseOp, byte[] deltaValue);
     void handleEvent(CqEvent event, Operation baseOp, Operation queryOp, Object key, Object newValue);
}
public class DefaultEventDelegate implements EventDelegate {
    // implementation elided for clarity...
}

In particular, note how the above implementation of the EventDelegate interface (the above DefaultEventDelegate class) has no GemFire dependencies at all. It truly is a POJO that we will make into an EDP via the following configuration (note that the class doesn't have to implement an interface, one is present only to better show case the decoupling between contract and implementation).

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:gfe="http://www.springframework.org/schema/gemfire"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/gemfire http://www.springframework.org/schema/gemfire/spring-gemfire.xsd">

	
	<gfe:client-cache pool-name="client"/>
	
	<gfe:pool id="client" subscription-enabled="true">
	   <gfe:server host="localhost" port="40404"/>
	</gfe:pool>
	
	<gfe:cq-listener-container>
	   <!-- default handle method -->
	   <gfe:listener ref="listener" query="SELECT * from /region"/ >
	   <gfe:listener ref="another-listener" query="SELECT * from /another-region" name="my-query" method="handleQuery"/>
	</gfe:cq-listener-container>
  
	<bean id="listener" class="gemfireexample.DefaultMessageDelegate"/>
	<bean id="another-listener" class="gemfireexample.DefaultMessageDelegate"/>
  ...
<beans>
[Note]Note
The example above shows some of the various forms that a listener can have; at its minimum the listener reference and the actual query definition are required. It's possible however to specify a name for the resulting continuous query (useful for monitoring) but also the name of the method (the default is handleEvent). The specified method can have various argument types, the EventDelegate interface lists the allowed types.

The example above uses the Spring Data GemFire namespace to declare the event listener container and automatically register the listeners. The full blown, beans definition is displayed below:

<!-- this is the Event Driven POJO (MDP) -->
<bean id="eventListener" class="org.springframework.data.gemfire.listener.adapter.ContinuousQueryListenerAdapter">
    <constructor-arg>
        <bean class="gemfireexample.DefaultEventDelegate"/>
    </constructor-arg>
</bean>

<!-- and this is the event listener container... -->
<bean id="gemfireListenerContainer" class="org.springframework.data.gemfire.listener.ContinuousQueryListenerContainer">
    <property name="cache" ref="gemfireCache"/>
    <property name="queryListeners">
      <!-- set of listeners -->
      <set>
        <bean class="org.springframework.data.gemfire.listener.ContinuousQueryDefinition" >
               <constructor-arg value="SELECT * from /region" />
               <constructor-arg ref="eventListener" />
        </bean>
      </set>
    </property>
</bean>

Each time an event is received, the adapter automatically performs type translation between the GemFire event and the required method argument(s) transparently. Any exception caused by the method invocation is caught and handled by the container (by default, being logged).

6.6 Wiring Declarable components

GemFire XML configuration (usually named cache.xml allows user objects to be declared as part of the configuration. Usually these objects are CacheLoaders or other pluggable callback components supported by GemFire. Using native GemFire configuration, each user type declared through XML must implement the Declarable interface which allows arbitrary parameters to be passed to the declared class through a Properties instance.

In this section we describe how you can configure these pluggable components defined in cache.xml using Spring while keeping your Cache/Region configuration defined in cache.xml This allows your pluggable components to focus on the application logic and not the location or creation of DataSources or other collaboration objects.

However, if you are starting a green field project, it is recommended that you configure Cache, Region, and other pluggable components directly in Spring. This avoids inheriting from the Declarable interface or the base class presented in this section. See the following sidebar for more information on this approach.

As an example of configuring a Declarable component using Spring, consider the following declaration (taken from the Declarable javadoc):

<cache-loader>
   <class-name>com.company.app.DBLoader</class-name>
   <parameter name="URL">
     <string>jdbc://12.34.56.78/mydb</string>
   </parameter>
</cache-loader>

To simplify the task of parsing, converting the parameters and initializing the object, Spring Data GemFire offers a base class (WiringDeclarableSupport) that allows GemFire user objects to be wired through a template bean definition or, in case that is missing, perform autowiring through the Spring container. To take advantage of this feature, the user objects need to extend WiringDeclarableSupport which automatically locates the declaring BeanFactory and performs wiring as part of the initialization process.

6.6.1 Configuration using template definitions

When used, WiringDeclarableSupport tries to first locate an existing bean definition and use that as wiring template. Unless specified, the component class name will be used as an implicit bean definition name. Let's see how our DBLoader declaration would look in that case:

public class DBLoader extends WiringDeclarableSupport implements CacheLoader {
  private DataSource dataSource;

  public void setDataSource(DataSource ds){
    this.dataSource = ds;
  }

  public Object load(LoaderHelper helper) { ... }
}
<cache-loader>
   <class-name>com.company.app.DBLoader</class-name>
   <!-- no parameter is passed (use the bean implicit name
   that is the class name) -->
</cache-loader>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans.xsd">

  <bean id="dataSource" ... />

     <!-- template bean definition -->
     <bean id="com.company.app.DBLoader" abstract="true" p:dataSource-ref="dataSource"/>
</beans>

In the scenario above, as no parameter was specified, a bean with the id/name com.company.app.DBLoader was used as a template for wiring the instance created by GemFire. For cases where the bean name uses a different convention, one can pass in the bean-name parameter in the GemFire configuration:

<cache-loader>
   <class-name>com.company.app.DBLoader</class-name>
   <!-- pass the bean definition template name
        as parameter -->
   <parameter name="bean-name">
     <string>template-bean</string>
   </parameter>
</cache-loader>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans.xsd">

  <bean id="dataSource" ... />

     <!-- template bean definition -->   
     <bean id="template-bean" abstract="true" p:dataSource-ref="dataSource"/>
     
</beans>
[Note]Note

The template bean definitions do not have to be declared in XML - any format is allowed (Groovy, annotations, etc..).

6.6.2 Configuration using auto-wiring and annotations

If no bean definition is found, by default, WiringDeclarableSupport will autowire the declaring instance. This means that unless any dependency injection metadata is offered by the instance, the container will find the object setters and try to automatically satisfy these dependencies. However, one can also use JDK 5 annotations to provide additional information to the auto-wiring process. We strongly recommend reading the dedicated chapter in the Spring documentation for more information on the supported annotations and enabling factors.

For example, the hypothetical DBLoader declaration above can be injected with a Spring-configured DataSource in the following way:

public class DBLoader extends WiringDeclarableSupport implements CacheLoader {
  // use annotations to 'mark' the needed dependencies 
  @javax.inject.Inject
  private DataSource dataSource;

  public Object load(LoaderHelper helper) { ... }
}
<cache-loader>
   <class-name>com.company.app.DBLoader</class-name>
   <!-- no need to declare any parameters anymore
        since the class is auto-wired -->
</cache-loader>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context.xsd">
            
     <!-- enable annotation processing -->   
     <context:annotation-config/>
     
</beans>

By using the JSR-330 annotations, the cache loader code has been simplified since the location and creation of the DataSource has been externalized and the user code is concerned only with the loading process. The DataSource might be transactional, created lazily, shared between multiple objects or retrieved from JNDI - these aspects can be easily configured and changed through the Spring container without touching the DBLoader code.