Spring Data for Tanzu GemFire 2.0

Configuring a GemFire Client with the Spring Framework

Last Updated February 19, 2025

This Spring Data for Tanzu GemFire topic discusses bootstrapping GemFire with the Spring Container.


Spring Data for Tanzu GemFire provides full configuration and initialization of VMware GemFire using the Spring Inversion of Control (IoC) container. The framework includes several classes to help simplify the configuration of GemFire components, including: Caches, Regions, DiskStores, Functions, persistence and backup and several other Distributed System components to support a variety of application use cases with minimal effort.

This topic assumes that you have basic familiarity with GemFire. For more information about GemFire, see the GemFire product documentation.

Advantages of using Spring over GemFire cache.xml

The Spring Data for Tanzu GemFire XML namespace supports full configuration of GemFire. The XML namespace is one of two ways to configure GemFire in a Spring context to properly manage GemFire’s lifecycle inside the Spring container. The other way to configure GemFire in a Spring context is by using annotation-based configuration.

While support for GemFire’s native cache.xml persists for legacy reasons, GemFire application developers who use XML configuration are encouraged use Spring XML to take advantage of the many features that Spring has to offer, such as modular XML configuration, property placeholders and overrides, SpEL (Spring Expression Language), and environment profiles. Behind the XML namespace, Spring Data for Tanzu GemFire makes extensive use of Spring’s FactoryBean pattern to simplify the creation, configuration, and initialization of GemFire components.

GemFire provides several callback interfaces, such as CacheListener, CacheLoader, and CacheWriter, that let developers add custom event handlers. Using Spring’s Inversion of Control (IoC) container, you can configure these callbacks as normal Spring beans and inject them into GemFire components. This is a significant improvement over native cache.xml, which provides relatively limited configuration options and requires callbacks to implement GemFire’s Declarable interface. For information about continuing to use Declarables within Spring’s container, see Wiring Declarable Components in Working with GemFire APIs.

In addition, IDEs, such as the Spring Tool Suite (STS), provide support for Spring XML namespaces, including code completion, pop-up annotations, and real time validation.

Using the Core Namespace

To simplify configuration, Spring Data for Tanzu GemFire provides a dedicated XML namespace for configuring core GemFire components. You can configure beans directly by using Spring’s standard <bean> definition. All bean properties are exposed through the XML namespace, which minimizes the need to use raw bean definitions.

Spring Data Repository support uses a separate XML namespace. For more information about configuring Spring Data for Tanzu GemFire Repositories, see

Spring Data for Tanzu GemFire Repositories.

To use the Spring Data for Tanzu GemFire XML namespace, declare it in your Spring XML configuration meta-data, as the following example shows:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:gfe="https://www.springframework.org/schema/gemfire/" <!--SEE COMMENT 1--><!--SEE COMMENT 2-->
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
    http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
    https://www.springframework.org/schema/gemfire/ https://www.springframework.org/schema/gemfire/spring-gemfire.xsd <!--SEE COMMENT 3-->
">
  
  <bean id ... >
  
  <gfe:client-cache ...> <!--SEE COMMENT 4-->
  
</beans>

Comments:

  1. Spring Data for Tanzu GemFire XML namespace prefix. This reference documentation uses gfe.

  2. The XML namespace prefix is mapped to the URI.

  3. The XML namespace URI location. Although the location in this example points to an external valid address, Spring resolves the schema locally because the address is included in the Spring Data for Tanzu GemFire library.

  4. Example declaration using the XML namespace with the gfe prefix.

Changing the default namespace

You can change the default namespace from beans to gfe. Change the default namespace to gfe with XML configurations composed mainly of GemFire components to avoid declaring the prefix.

To make this change, replace the namespace prefix declaration shown earlier, as the following example shows:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/gemfire/" <!--SEE COMMENT 1 -->
       xmlns:beans="http://www.springframework.org/schema/beans" <!--SEE COMMENT 2-->
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
    http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
    https://www.springframework.org/schema/gemfire/ https://www.springframework.org/schema/gemfire/spring-gemfire.xsd
">
  
  <beans:bean id ... > <!--SEE COMMENT 3-->
  
  <client-cache ...> <!--SEE COMMENT 4-->
  
</beans>

Comments:

  1. The default namespace declaration for this XML document points to the Spring Data for Tanzu GemFire XML namespace.

  2. The beans namespace prefix declaration for Spring’s raw bean definitions.

  3. Bean declaration using the beans namespace. Observe the prefix.

  4. Bean declaration using the gfe namespace. Observe the lack of prefix because gfe was set as the default namespace.

Using the Data Access Namespace

In addition to the core XML namespace (gfe), Spring Data for Tanzu GemFire provides a data access XML namespace (gfe-data), which is primarily intended to simplify the development of GemFire client applications. This namespace currently contains support for GemFire Repositories and Function execution, as well as a <datasource> tag that offers a convenient way to connect to a GemFire cluster.

An Easy Way to Connect to GemFire

For many applications, a basic connection to a GemFire data grid using default values is sufficient. The Spring Data for Tanzu GemFire <datasource> tag provides a simple way to access data. The data source creates a ClientCache and connection Pool. In addition, it queries the cluster servers for all existing root Regions and creates an empty client Region proxy for each one.

<gfe-data:datasource>
  <locator host="remotehost" port="1234"/>
</gfe-data:datasource>

The <datasource> tag is syntactically similar to <gfe:pool>. It may be configured with one or more nested locator or server elements to connect to an existing data grid. Additionally, all attributes available to configure a Pool are supported. This configuration automatically creates client Region beans for each Region defined on cluster members connected to the Locator, so that they can be seamlessly referenced by Spring Data mapping annotations (GemfireTemplate) and autowired into application classes.

You can also explicitly configure client Regions. For example, if you want to cache data in local memory, as the following example shows:

<gfe-data:datasource>
  <locator host="remotehost" port="1234"/>
</gfe-data:datasource>

<gfe:client-region id="Example" shortcut="CACHING_PROXY"/>

Configuring a Cache

To use GemFire, you must either create a new cache or connect to an existing one. With the current version of GemFire, you can have only one open cache per VM (more strictly speaking, per ClassLoader). In most cases, the cache should only be created once.

Note: The configuration makes use of Spring's Resource abstraction to locate the file. The Resource abstraction lets various search patterns be used, depending on the runtime environment or the prefix specified (if any) in the resource location.

In addition to referencing an external XML configuration file, you can also specify GemFire System properties that use any of Spring’s Properties support features.

For example, you can use the properties element defined in the util namespace to define Properties directly or load properties from a properties file, as follows:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:gfe="https://www.springframework.org/schema/gemfire/"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
    http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
    https://www.springframework.org/schema/gemfire/ https://www.springframework.org/schema/gemfire/spring-gemfire.xsd
    http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd
">
  
  <util:properties id="gemfireProperties" location="file:/path/to/gemfire.properties"/>
  
  <gfe:client-cache properties-ref="gemfireProperties"/>
  
</beans>

Using a properties file is recommended for externalizing environment-specific settings outside the application configuration.

Note: Cache settings apply only when a new cache needs to be created. If an open cache already exists in the VM, these settings are ignored.

Configuring a GemFire ClientCache

Spring Data for Tanzu GemFire supports the definition of a GemFire ClientCache in a Spring container. A ClientCache definition is supported by the org.springframework.data.gemfire.client.ClientCacheFactoryBean.

The simplest definition of a GemFire cache client using default configuration follows:

<beans>
  <gfe:client-cache/>
</beans>

A cache client connects to a remote cache server through a Pool. By default, a Pool is created to connect to a server running on localhost and listening to port 40404. The default Pool is used by all client Regions unless the Region is configured to use a specific Pool.

Pools can be defined with the pool element. This client-side Pool can be used to configure connectivity directly to a server for individual entities or for the entire cache through one or more Locators.

For example, to customize the default Pool used by the client-cache, the developer must define a Pool and wire it to the cache definition, as the following example shows:

<beans>
  <gfe:client-cache id="myCache" pool-name="myPool"/>

  <gfe:pool id="myPool" subscription-enabled="true">
    <gfe:locator host="${gemfire.locator.host}" port="${gemfire.locator.port}"/>
  </gfe:pool>
</beans>

The <client-cache> element also has a ready-for-events attribute. If the attribute is set to true, the client cache initialization includes a call to ClientCache.readyForEvents() (see VMware GemFire Java API Reference).

Client Region describes client-side configuration in more detail.

GemFire’s DEFAULT Pool and Spring Data for Tanzu GemFire Pool Definitions

If a GemFire ClientCache is local-only, then no Pool definition is required. For instance, you can define the following:

<gfe:client-cache/>

<gfe:client-region id="Example" shortcut="LOCAL"/>

In this case, the “Example” Region is LOCAL and no data is distributed between the client and a server. Therefore, no Pool is necessary. This is true for any client-side, local-only Region, as defined by the GemFire’s ClientRegionShortcut (see VMware GemFire Java API Reference).

However, if a client Region is a (caching) proxy to a server-side Region, a Pool is required. In that case, there are several ways to define and use a Pool.

When a ClientCache, a Pool, and a proxy-based Region are all defined but not explicitly identified, Spring Data for Tanzu GemFire resolves the references automatically, as shown in the following example:

<gfe:client-cache/>

<gfe:pool>
  <gfe:locator host="${geode.locator.host}" port="${geode.locator.port}"/>
</gfe:pool>

<gfe:client-region id="Example" shortcut="PROXY"/>

In this example, the ClientCache is identified as gemfireCache, the Pool as gemfirePool, and the client Region as “Example”. However, the ClientCache initializes GemFire’s DEFAULT Pool from gemfirePool, and the client Region uses the gemfirePool when distributing data between the client and the server.

Basically, Spring Data for Tanzu GemFire resolves the preceding configuration to the following:

<gfe:client-cache id="gemfireCache" pool-name="gemfirePool"/>

<gfe:pool id="gemfirePool">
  <gfe:locator host="${geode.locator.host}" port="${geode.locator.port}"/>
</gfe:pool>

<gfe:client-region id="Example" cache-ref="gemfireCache" pool-name="gemfirePool" shortcut="PROXY"/>

GemFire still creates a Pool named DEFAULT. Spring Data for Tanzu GemFire causes the DEFAULT Pool to be initialized from the gemfirePool. Doing so is useful in situations where multiple Pools are defined and client Regions are using separate Pools, or do not declare a Pool at all.

Consider the following:

<gfe:client-cache pool-name="locatorPool"/>

<gfe:pool id="locatorPool">
  <gfe:locator host="${geode.locator.host}" port="${geode.locator.port}"/>
</gfe:pool>

<gfe:pool id="serverPool">
  <gfe:server host="${geode.server.host}" port="${geode.server.port}"/>
</gfe:pool>

<gfe:client-region id="Example" pool-name="serverPool" shortcut="PROXY"/>

<gfe:client-region id="AnotherExample" shortcut="CACHING_PROXY"/>

<gfe:client-region id="YetAnotherExample" shortcut="LOCAL"/>

In this example, the GemFire client-cache DEFAULT pool is initialized from locatorPool, as specified by the pool-name attribute. There is no Spring Data for Tanzu GemFire-defined gemfirePool, since both Pools were explicitly identified (named) as locatorPool and serverPool, respectively.

The “Example” Region explicitly refers to and exclusively uses the serverPool. The AnotherExample Region uses GemFire’s DEFAULT Pool, which was configured from the locatorPool based on the client cache bean definition’s pool-name attribute.

Finally, the YetAnotherExample Region does not use a Pool, because it is LOCAL.

The AnotherExample Region would first look for a Pool bean named gemfirePool, but that would require the definition of an anonymous Pool bean: gfe:pool/ or a Pool bean explicitly named gemfirePool. For example, <gfe:pool id=“gemfirePool”/>.

If we either changed the name of locatorPool to gemfirePool or made the Pool bean definition be anonymous, it would have the same effect as the preceding configuration.

Configuring a Region

A Region is required to store and retrieve data from the cache. org.apache.geode.cache.Region is an interface extending java.util.Map and enables basic data access using familiar key-value semantics. The Region interface is wired into application classes that require it so the actual Region type is decoupled from the programming model. Typically, each Region is associated with one domain object, similar to a table in a relational database.

GemFire implements the following types of Regions:

  • REPLICATE: Data is replicated across all cache members in the cluster that define the Region. This provides very high read performance but writes take longer to perform the replication.

  • PARTITION: Data is partitioned into buckets (sharded) among many cache members in the cluster that define the Region. This provides high read and write performance and is suitable for large data sets that are too large for a single node.

  • LOCAL: Data only exists on the local node.

  • Client: Technically, a client Region is a LOCAL Region that acts as a PROXY to a REPLICATE or PARTITION Region hosted on cache servers in a cluster. It may hold data created or fetched locally. Alternately, it can be empty. Local updates are synchronized to the cache server. Also, a client Region may subscribe to events in order to stay up-to-date (synchronized) with changes originating from remote processes that access the same server Region.

For more information about the various Region types and their capabilities as well as configuration options, see Region Types in the GemFire product documentation.

Using an Externally Configured Region

To reference Regions already configured in a GemFire native cache.xml file, use the lookup-region element. Declare the target Region name with the name attribute. For example, to declare a bean definition identified as ordersRegion for an existing Region named Orders, you can use the following bean definition:

<gfe:lookup-region id="ordersRegion" name="Orders"/>

If name is not specified, the bean’s id will be used as the name of the Region. The example above becomes:

<!-- lookup for a Region called 'Orders' -->
<gfe:lookup-region id="Orders"/>

Warning: If the Region does not exist, an initialization exception will be thrown. To configure new Regions, proceed to the appropriate sections below.

In the previous examples, since no cache name was explicitly defined, the default naming convention (gemfireCache) was used. Alternately, one can reference the cache bean with the cache-ref attribute:

<gfe:client-cache id="myCache"/>
<gfe:lookup-region id="ordersRegion" name="Orders" cache-ref="myCache"/>

lookup-region lets you retrieve existing, pre-configured Regions without exposing the Region semantics or setup infrastructure.

Auto Region Lookup

auto-region-lookup lets you import all Regions defined in a GemFire native cache.xml file into a Spring ApplicationContext when you use the cache-xml-location attribute on the <gfe:client-cache> element.

For example, consider the following cache.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<client-cache xmlns="https://geode.apache.org/schema/cache"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://geode.apache.org/schema/cache https://geode.apache.org/schema/cache/cache-1.0.xsd"
       version="1.0">
  
  <region name="Parent" refid="LOCAL">
    <region name="Child" refid="LOCAL"/>
  </region>
  
</client-cache>

You can import the preceding cache.xml file as follows:

<gfe:client-cache cache-xml-location="cache.xml"/>

You can then use the <gfe:lookup-region> element (for example, <gfe:lookup-region id="Parent"/>) to reference specific Regions as beans in the Spring container, or you can choose to import all Regions defined in cache.xml by using the following:

<gfe:auto-region-lookup/>

Spring Data for Tanzu GemFire automatically creates beans for all GemFire Regions defined in cache.xml that have not been explicitly added to the Spring container with explicit <gfe:lookup-region> bean declarations.

Note that Spring Data for Tanzu GemFire uses a Spring BeanPostProcessor to post-process the cache after it is both created and initialized to determine the Regions defined in GemFire to add as beans in the Spring ApplicationContext.

You can inject these “auto-looked-up” Regions as you would any other bean defined in the Spring ApplicationContext, with one exception: You may need to define a depends-on association with the ‘gemfireCache’ bean, as follows:

package example;
  
import ...
  
@Repository("appDao")
@DependsOn("gemfireCache")
public class ApplicationDao extends DaoSupport {
  
    @Resource(name = "Parent")
    private Region<?, ?> parent;
  
    @Resource(name = "/Parent/Child")
    private Region<?, ?> child;
  
    ...
}

The preceding example only applies when you use Spring’s component-scan functionality.

If you declare your components by using Spring XML config, then you would use the following:

<bean class="example.ApplicationDao" depends-on="gemfireCache"/>

Doing so ensures that the GemFire cache and all the Regions defined in cache.xml are created before any components with auto-wire references when using the <gfe:auto-region-lookup> element.

Configuring Regions

Spring Data for Tanzu GemFire provides support for configuring client Regions through the <client-region> element:

Common Region Attributes

The following table lists the attributes available Regions:

Table 1. Common Region Attributes
Name Values Description
cache-ref GemFire Cache bean reference The name of the bean defining the GemFire Cache. Defaults to gemfireCache.
cloning-enabled boolean (default: false) When true, updates are applied to a clone of the value, then the clone is saved to the cache. When false, the value is modified in place in the cache.
close boolean (default: false) Determines whether the region should be closed at shutdown.
concurrency-checks-enabled boolean (default: true) Determines whether members perform checks to provide consistent handling for concurrent or out-of-order updates to distributed regions.
data-policy See DataPolicy. The region's data policy. Not all data policies are supported for every Region type.
destroy boolean (default: false) Determines whether the region should be destroyed at shutdown.
disk-store-ref The name of a configured disk store. A reference to a bean created through the disk-store element.
disk-synchronous boolean (default: true) Determines whether disk store writes are synchronous.
id Any valid bean name. The default region name if no name attribute is specified.
ignore-if-exists boolean (default: false) Ignores this bean definition if the region already exists in the cache, resulting in a lookup instead.
ignore-jta boolean (default: false) Determines whether this Region participates in JTA (Java Transaction API) transactions.
index-update-type synchronous or asynchronous (default: synchronous) Determines whether Indices are updated synchronously or asynchronously on entry creation.
initial-capacity integer (default: 16) The initial memory allocation for the number of Region entries.
key-constraint Any valid, fully-qualified Java class name. Expected key type.
load-factor float (default: .75) Sets the initial parameters on the underlying java.util.ConcurrentHashMap used for storing region entries.
name Any valid region name. The name of the region. If not specified, it assumes the value of the id attribute, the bean name).
persistent boolean (default: false) Determines whether the region persists entries to local disk (disk store).
shortcut See RegionShortcut The RegionShortcut for this region. Allows easy initialization of the region based on pre-defined defaults.
statistics boolean (default: false) Determines whether the region reports statistics.
template The name of a region template. A reference to a bean created through one of the region-template elements.
value-constraint Any valid, fully-qualified Java class name. Expected value type.

CacheListener instances

CacheListener instances are registered with a Region to handle Region events, such as when entries are created, updated, destroyed, and so on. A CacheListener can be any bean that implements the CacheListener interface (see VMware GemFire Java API Reference). A Region may have multiple listeners, declared with the cache-listener element nested in the containing client-region element.

The following example has two declared CacheListener's. The first references a named, top-level Spring bean. The second is an anonymous inner bean definition.

<bean id="myListener" class="org.example.app.geode.cache.SimpleCacheListener"/>

<gfe:client-region id="regionWithListeners">
  <gfe:cache-listener>
    <!-- nested CacheListener bean reference -->
    <ref bean="myListener"/>
    <!-- nested CacheListener bean definition -->
    <bean class="org.example.app.geode.cache.AnotherSimpleCacheListener"/>
  </gfe:cache-listener>
</gfe:client-region>

The following example uses an alternate form of the cache-listener element with the ref attribute. Doing so allows for more concise configuration when defining a single CacheListener.

The XML namespace allows only a single cache-listener element, so either the style shown in the preceding example or the style in the following example must be used.

<beans>
  <gfe:client-region id="exampleReplicateRegionWithCacheListener">
    <gfe:cache-listener ref="myListener"/>
  </gfe:client-region>

  <bean id="myListener" class="example.CacheListener"/>
</beans>

Warning: Using ref and a nested declaration in the cache-listener element is illegal. The two options are mutually exclusive and using both in the same element results in an exception.

Bean Reference Conventions

The cache-listener element is an example of a common pattern used in the XML namespace anywhere GemFire provides a callback interface to be implemented to invoke custom code in response to cache or Region events. When you use Spring’s IoC container, the implementation is a standard Spring bean. To simplify the configuration, the schema allows a single occurrence of the cache-listener element, but if multiple instances are permitted, it may contain nested bean references and inner bean definitions in any combination. The convention is to use the singular form, cache-listener instead of cache-listeners, reflecting that the most common scenario is a single instance. THis pattern can be seen in the advanced cache configuration example.

CacheLoaders and CacheWriters

Similar to cache-listener, the XML namespace provides cache-loader and cache-writer elements to register these GemFire components for a Region.

A CacheLoader is invoked on a cache miss to let an entry be loaded from an external data source, such as a database. A CacheWriter is invoked before an entry is created or updated, to allow the entry to be synchronized to an external data source. The main difference is that GemFire supports, at most, a single instance of CacheLoader and CacheWriter per Region. However, either declaration style may be used.

The following example declares a Region with both a CacheLoader and a CacheWriter:

<beans>
  <gfe:client-region id="exampleRegionWithCacheLoaderAndCacheWriter">
    <gfe:cache-loader ref="myLoader"/>
    <gfe:cache-writer>
      <bean class="example.CacheWriter"/>
    </gfe:cache-writer>
  </gfe:client-region>

  <bean id="myLoader" class="example.CacheLoader">
    <property name="dataSource" ref="mySqlDataSource"/>
  </bean>
  
  <!-- DataSource bean definition -->
</beans>

For more details, see CacheLoader and CacheWriter in the VMware GemFire Java API Reference).

Compression

GemFire Regions may be compressed to reduce JVM memory consumption and pressure to possibly avoid global GCs. When you enable compression for a Region, all values stored in memory for the Region are compressed, while keys and indexes remain uncompressed. New values are compressed when put into the Region, and all values are decompressed automatically when read back from the Region. Values are not compressed when persisted to disk or when sent over the wire.

The following example shows a Region with compression enabled:

<beans>
  <gfe:client-region id="exampleReplicateRegionWithCompression">
    <gfe:compressor>
      <bean class="org.apache.geode.compression.SnappyCompressor"/>
    </gfe:compressor>
  </gfe:client-region>
</beans>

For more information, see Region Compression in the GemFire product documentation.

Subregions

Spring Data for Tanzu GemFire supports Sub-Regions, allowing Regions to be arranged in a hierarchical relationship.

For example, GemFire allows for a /Customer/Address Region and a different /Employee/Address Region. Additionally, a Sub-Region may have its own Sub-Regions and configuration. A Sub-Region does not inherit attributes from its parent Region. A Sub-Region is naturally declared as a child element of a Region. The Sub-Region’s name attribute is the simple name. The preceding example might be configured as follows:

<beans>
  <gfe:client-region name="Customer">
    <gfe:client-region name="Address"/>
  </gfe:client-region>

  <gfe:client-region name="Employee">
    <gfe:client-region name="Address"/>
  </gfe:client-region>
</beans>

The Monospaced ([id]) attribute is not permitted for a Sub-Region. Sub-Regions are created with bean names such as /Customer/Address and /Employee/Address in this example. This allows them to be injected into other application beans, such as a GemfireTemplate, that requires them by using the full path name of the Region. The full pathname of the Region should also be used in OQL query strings.

Region Templates

Spring Data for Tanzu GemFire supports Region templates.

This feature allows developers to define common Region configuration and attributes once and reuse the configuration among many Region bean definitions declared in the Spring ApplicationContext.

Spring Data for Tanzu GemFire includes five Region template tags in its namespace:

Table 2. Region Template Tags
Tag Name Description
<gfe:region-template> Defines common generic Region attributes. Extends regionType in the XML namespace.
<gfe:client-region-template> Defines common 'Client' Region attributes. Extends clientRegionType in the XML namespace.

In addition to the tags, concrete <gfe:client-region> elements and the abstract <gfe:client-region-template> elements have a template attribute used to define the Region template from which the Region inherits its configuration. Region templates may even inherit from other Region templates.

The following example shows one possible configuration:

<beans>
  
  <gfe:region-template id="BaseRegionTemplate" initial-capacity="51" load-factor="0.85" persistent="false" statistics="true"
      key-constraint="java.lang.Long" value-constraint="java.lang.String">
    <gfe:cache-listener>
      <bean class="example.CacheListenerOne"/>
      <bean class="example.CacheListenerTwo"/>
    </gfe:cache-listener>
    <gfe:entry-ttl timeout="600" action="DESTROY"/>
    <gfe:entry-tti timeout="300" action="INVALIDATE"/>
  </gfe:region-template>

  <gfe:region-template id="ExtendedRegionTemplate" template="BaseRegionTemplate" load-factor="0.55">
    <gfe:cache-loader>
      <bean class="example.CacheLoader"/>
    </gfe:cache-loader>
    <gfe:cache-writer>
      <bean class="example.CacheWriter"/>
    </gfe:cache-writer>
  </gfe:region-template>

  <gfe:client-region-template id="ClientRegionTemplate" template="ExtendedRegionTemplate"
      load-factor="0.70" value-constraint="java.lang.Object">
    <gfe:eviction type="ENTRY_COUNT" threshold="8192000" action="OVERFLOW_TO_DISK"/>
  </gfe:client-region-template>

  <gfe:client-region id="TemplateBasedClientRegion" template="ClientRegionTemplate"
      persistent="true"/>
</beans>

Region templates work for Sub-Regions as well. Notice that ‘TemplateBasedClientRegion’ extends ‘ClientRegionTemplate’, which extends ‘ExtendedRegionTemplate’, which extends ‘BaseRegionTemplate’. Attributes and sub-elements defined in subsequent inherited Region bean definitions override what exists in the parent.

How Templating Works

Spring Data for Tanzu GemFire applies Region templates when the Spring ApplicationContext configuration metadata is parsed, and therefore, Region templates must be declared in the order of inheritance. In other words, parent templates must be defined before child templates. Doing so ensures that the proper configuration is applied, especially when element attributes or sub-elements are overridden.

Caution Concerning Regions, Sub-Regions, and Lookups

When defining a region, the default behavior is to create the Region first. If the Region already exists, the creation logic fails-fast and an appropriate exception is thrown. However, much like the CREATE TABLE IF NOT EXISTS ...​ DDL syntax, the Spring Data for Tanzu GemFire <gfe:client-region> XML namespace elements now include a ignore-if-exists attribute, which first performs a lookup of an existing Region identified by name before attempting to create the Region. If an existing Region is found by name and ignore-if-exists is set to true, then the Region bean definition defined in Spring configuration is ignored.

Warning: The Spring team highly recommends that the client-region XML namespace elements be strictly used for defining new Regions only. One problem that could arise when the Regions defined by these elements already exist and the Region elements perform a lookup first is, if you defined different Region semantics and behaviors for eviction, expiration, subscription, and so on in your application config, then the Region definition might not match and could exhibit contrary behaviors to those required by the application.

As a recommended practice, use only client-region XML namespace elements to define new Regions.

Consider the following native GemFire cache.xml configuration file:

<?xml version="1.0" encoding="UTF-8"?>
<client-cache xmlns="https://geode.apache.org/schema/cache"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://geode.apache.org/schema/cache https://geode.apache.org/schema/cache/cache-1.0.xsd"
       version="1.0">
  
  <region name="Customers" refid="LOCAL">
    <region name="Accounts" refid="LOCAL">
      <region name="Orders" refid="LOCAL">
        <region name="Items" refid="LOCAL"/>
      </region>
    </region>
  </region>
  
</client-cache>

Furthermore, consider that you may have defined an application DAO as follows:

public class CustomerAccountDao extends GemfireDaoSupport {
  
    @Resource(name = "Customers/Accounts")
    private Region customersAccounts;
  
    ...
}

Here, we inject a reference to the Customers/Accounts Region in our application DAO. Consequently, it is not uncommon for a developer to define beans for some or all of these Regions in Spring XML configuration metadata as follows:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:gfe="https://www.springframework.org/schema/gemfire/"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
    http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
    https://www.springframework.org/schema/gemfire/ https://www.springframework.org/schema/gemfire/spring-gemfire.xsd
">
  
  <gfe:client-cache cache-xml-location="classpath:cache.xml"/>
  
  <gfe:lookup-region name="Customers/Accounts"/>
  <gfe:lookup-region name="Customers/Accounts/Orders"/>
  
</beans>

The Customers/Accounts and Customers/Accounts/Orders Regions are referenced as beans in the Spring container as Customers/Accounts and Customers/Accounts/Orders, respectively. The nice thing about using the lookup-region element and the corresponding syntax (described earlier) is that it lets you reference a Sub-Region directly without unnecessarily defining a bean for the parent Region (Customers, in this case).

GemFire is flexible in referencing both parent Regions and Sub-Regions with or without the leading forward slash. For example, the parent can be referenced as /Customers or Customers and the child as /Customers/Accounts or Customers/Accounts. However, Spring Data for Tanzu GemFire is very specific when it comes to naming beans after Regions. It always uses the forward slash (/) to represent Sub-Regions (for example, /Customers/Accounts).

Therefore, you should use the non-nested lookup-region syntax shown earlier or define direct references with a leading forward slash (/), as follows:

<gfe:lookup-region name="/Customers/Accounts"/>
<gfe:lookup-region name="/Customers/Accounts/Orders"/>

Data Eviction (with Overflow)

Based on various constraints, each Region can have an eviction policy in place for evicting data from memory. Currently, in GemFire, eviction applies to the Least Recently Used entry (also known as LRU. Evicted entries are either destroyed or paged to disk (referred to as “overflow to disk”).

Spring Data for Tanzu GemFire supports all eviction policies (entry count, memory, and heap usage) for client, local Regions by using the nested eviction element.

For example, to configure a client Region to overflow to disk if the memory size exceeds more than 512 MB, you can specify the following configuration:

<gfe:client-region id="exampleClientRegionWithEviction">
  <gfe:eviction type="MEMORY_SIZE" threshold="512" action="OVERFLOW_TO_DISK"/>
</gfe:client-region>

When configuring Regions for overflow, you should configure the storage through the disk-store element for maximum efficiency.

For a detailed description of eviction policies, see Eviction in the GemFire product documentation.

Data Expiration

GemFire lets you control how long entries exist in the cache. Expiration is driven by elapsed time, as opposed to eviction, which is driven by the entry count or heap or memory usage. Once an entry expires, it may no longer be accessed from the cache.

GemFire supports the following expiration types:

  • Time-to-Live (TTL): The amount of time in seconds that an object may remain in the cache after the last creation or update. For entries, the counter is set to zero for create and put operations. Region counters are reset when the Region is created and when an entry has its counter reset.

  • Idle Timeout (TTI): The amount of time in seconds that an object may remain in the cache after the last access. The Idle Timeout counter for an object is reset any time its TTL counter is reset. In addition, an entry’s Idle Timeout counter is reset any time the entry is accessed through a get operation or a netSearch. The Idle Timeout counter for a Region is reset whenever the Idle Timeout is reset for one of its entries.

Each of these expiration types may be applied to the Region itself or to entries in the Region. Spring Data for Tanzu GemFire provides <region-ttl>, <region-tti>, <entry-ttl>, and <entry-tti> Region child elements to specify timeout values and expiration actions.

The following example shows a client Region with expiration values set:

<gfe:client-region id="exampleClientRegionWithExpiration" shortcut="LOCAL">
  <gfe:region-ttl timeout="30000" action="INVALIDATE"/>
  <gfe:entry-tti timeout="600" action="LOCAL_DESTROY"/>
</gfe:client-region>

For a detailed description of expiration policies, see Expiration in the GemFire product documentation.

Annotation-based Data Expiration

With Spring Data for Tanzu GemFire, you can define expiration policies and settings on individual Region entry values (or, to put it differently, directly on application domain objects). For instance, you can define expiration policies on a Session-based application domain object as follows:

@Expiration(timeout = "1800", action = "INVALIDATE")
public class SessionBasedApplicationDomainObject {
  ...
}

You can also specify expiration type with settings on Region entries by using the @IdleTimeoutExpiration and @TimeToLiveExpiration annotations for Idle Timeout (TTI) and Time-to-Live (TTL) expiration, respectively, as the following example shows:

@TimeToLiveExpiration(timeout = "3600", action = "LOCAL_DESTROY")
@IdleTimeoutExpiration(timeout = "1800", action = "LOCAL_INVALIDATE")
@Expiration(timeout = "1800", action = "INVALIDATE")
public class AnotherSessionBasedApplicationDomainObject {
  ...
}

Both @IdleTimeoutExpiration and @TimeToLiveExpiration take precedence over the generic @Expiration annotation when more than one expiration annotation type is specified, as shown in the preceding example. Neither @IdleTimeoutExpiration nor @TimeToLiveExpiration overrides the other. Rather, they compliment each other when different Region entry expiration policies, such as TTL and TTI, are configured.

All @Expiration-based annotations apply only to Region entry values. Expiration for a Region is not covered by The Spring Data for Tanzu GemFire expiration annotation support. However, GemFire and Spring Data for Tanzu GemFire do let you set Region expiration by using the Spring Data for Tanzu GemFire XML namespace, as follows:

<gfe:client-region id="Example" persistent="false">
  <gfe:region-ttl timeout="600" action="DESTROY"/>
  <gfe:region-tti timeout="300" action="INVALIDATE"/>
</gfe:client-region>

The Spring Data for Tanzu GemFire @Expiration annotation support is implemented with GemFire’s CustomExpiry interface. For more information, see Configure Data Expiration in the GemFire product documentation.

The Spring Data for Tanzu GemFire AnnotationBasedExpiration class and CustomExpiry implementation is responsible for processing the Spring Data for Tanzu GemFire @Expiration annotations and applying the expiration policy configuration appropriately for Region entry expiration on request.

To use Spring Data for Tanzu GemFire to configure specific GemFire Regions to appropriately apply the expiration policy to your application domain objects annotated with @Expiration-based annotations, you must:

  1. Define a bean in the Spring ApplicationContext of type AnnotationBasedExpiration by using the appropriate constructor or one of the convenient factory methods. When configuring expiration for a specific expiration type, such as Idle Timeout (TTI) or Time-to-Live (TTL), you should use one of the factory methods in the AnnotationBasedExpiration class, as follows:

    <bean id="ttlExpiration" class="org.springframework.data.gemfire.expiration.AnnotationBasedExpiration"
          factory-method="forTimeToLive"/>
    
    <gfe:client-region id="Example" shortcut="LOCAL">
        <gfe:custom-entry-ttl ref="ttlExpiration"/>
    </gfe:client-region>
    

    Note: To configure Idle Timeout (TTI) Expiration instead, use the forIdleTimeout factory method along with the element to set TTI.

  2. (Optional) Annotate your application domain objects that are stored in the Region with expiration policies and custom settings by using one of The Spring Data for Tanzu GemFire @Expiration annotations: @Expiration, @IdleTimeoutExpiration, or @TimeToLiveExpiration

  3. (Optional) In cases where particular application domain objects have not been annotated with The Spring Data for Tanzu GemFire @Expiration annotations at all, but the GemFire Region is configured to use The Spring Data for Tanzu GemFire custom AnnotationBasedExpiration class to determine the expiration policy and settings for objects stored in the Region, you can set “default” expiration attributes on the AnnotationBasedExpiration bean by doing the following:

    <bean id="defaultExpirationAttributes" class="org.apache.geode.cache.ExpirationAttributes">
        <constructor-arg value="600"/>
        <constructor-arg value="#{T(org.apache.geode.cache.ExpirationAction).DESTROY}"/>
    </bean>
    
    <bean id="ttiExpiration" class="org.springframework.data.gemfire.expiration.AnnotationBasedExpiration"
      factory-method="forIdleTimeout">
        <constructor-arg ref="defaultExpirationAttributes"/>
    </bean>
    
    <gfe:client-region id="Example" persistent="false">
        <gfe:custom-entry-tti ref="ttiExpiration"/>
    </gfe:client-region>
    

You may have noticed that The Spring Data for Tanzu GemFire @Expiration annotations use a String as the attribute type rather than being strongly typed — for example, int for ‘timeout’ and The Spring Data for Tanzu GemFire ExpirationActionType for ‘action’. This is to accommodate property placeholders and Spring Expression Language (SpEL) expressions.

For instance, a developer can specify both the expiration ‘timeout’ and ‘action’ by using property placeholders in the @Expiration annotation attributes, as the following example shows:

@TimeToLiveExpiration(timeout = "${geode.region.entry.expiration.ttl.timeout}",
    action = "${geode.region.entry.expiration.ttl.action}")
public class ExampleApplicationDomainObject {
  ...
}

Then, in your Spring XML config or in JavaConfig, you can declare the following beans:

<util:properties id="expirationSettings">
  <prop key="geode.region.entry.expiration.ttl.timeout">600</prop>
  <prop key="geode.region.entry.expiration.ttl.action">INVALIDATE</prop>
  ...
</util:properties>

<context:property-placeholder properties-ref="expirationProperties"/>

This is convenient both when multiple application domain objects might share similar expiration policies and when you wish to externalize the configuration.

However, you may want more dynamic expiration configuration determined by the state of the running system. This is where the power of SpEL comes into play and is the recommended approach, actually. Not only can you refer to beans in the Spring container and access bean properties, invoke methods, and so on, but the values for expiration ‘timeout’ and ‘action’ can be strongly typed. Consider the following example (which builds on the preceding example):

<util:properties id="expirationSettings">
  <prop key="geode.region.entry.expiration.ttl.timeout">600</prop>
  <prop key="geode.region.entry.expiration.ttl.action">#{T(org.springframework.data.gemfire.expiration.ExpirationActionType).DESTROY}</prop>
  <prop key="geode.region.entry.expiration.tti.action">#{T(org.apache.geode.cache.ExpirationAction).INVALIDATE}</prop>
  ...
</util:properties>

<context:property-placeholder properties-ref="expirationProperties"/>

Then, on your application domain object, you can define a timeout and an action as follows:

@TimeToLiveExpiration(timeout = "@expirationSettings['geode.region.entry.expiration.ttl.timeout']",
    action = "@expirationSetting['geode.region.entry.expiration.ttl.action']")
public class ExampleApplicationDomainObject {
  ...
}

You can imagine that the ‘expirationSettings’ bean could be a more interesting and useful object than a simple instance of java.util.Properties. In the preceding example, the properties element (expirationSettings) uses SpEL to base the action value on the actual ExpirationAction enumerated type, quickly leading to identified failures if the enumerated type ever changes.

Data Persistence

Regions can be persistent. GemFire ensures that all the data you put into a Region that is configured for persistence is written to disk in a way that is recoverable the next time you recreate the Region. Doing so lets data be recovered after machine or process failure or even after an orderly shutdown and subsequent restart of the GemFire data node.

To enable persistence with Spring Data for Tanzu GemFire, set the persistent attribute to true on the <client-region> element, as the following example shows:

<gfe:client-region id="examplePersistentClientRegion" persistent="true"/>

For maximum efficiency when persisting Regions, you should configure the storage through the disk-store element. The DiskStore is referenced by using the disk-store-ref attribute. Additionally, the Region may perform disk writes synchronously or asynchronously. The following example shows a synchronous DiskStore:

<gfe:client-region id="yetAnotherExamplePersistentClientRegion" persistent="true"
    disk-store-ref="myDiskStore" disk-synchronous="true"/>

For more information, see Configuring a DiskStore.

Client Region

Spring Data for Tanzu GemFire offers dedicated support for connecting to a GemFire cluster through its client-cache elements: client-region and pool. client-region defines a client Region, and pool defines a Pool of connections used and shared by the various client Regions.

The following example shows a typical client Region configuration:

<bean id="myListener" class="example.CacheListener"/>
  
<!-- client Region using the default Spring Data for Tanzu GemFire gemfirePool Pool -->
<gfe:client-region id="Example">
  <gfe:cache-listener ref="myListener"/>
</gfe:client-region>
  
<!-- client Region using its own dedicated Pool -->
<gfe:client-region id="AnotherExample" pool-name="myPool">
  <gfe:cache-listener ref="myListener"/>
</gfe:client-region>
  
<!-- Pool definition -->
<gfe:pool id="myPool" subscription-enabled="true">
  <gfe:locator host="remoteHost" port="12345"/>
</gfe:pool>

client-region supports CacheListener instances as well as a CacheLoader and a CacheWriter. It also requires a connection Pool for connecting to a set of either Locators or servers. Each client Region can have its own Pool, or they can share the same one. If a Pool is not specified, then the “DEFAULT” Pool will be used.

Note: In the preceding example, the Pool is configured with a Locator. A Locator is a separate process used to discover cache servers and peer data members in the distributed system and is recommended for production systems. It is also possible to configure the Pool to connect directly to one or more cache servers by using the server element.

For a full list of options to set on the client and especially on the Pool, see Client Configuration in the GemFire product documentation.

Client Interests

To minimize network traffic, each client can separately define its own ‘interests’ policies, indicating to GemFire the data it actually requires. In Spring Data for Tanzu GemFire, ‘interests’ can be defined for each client Region separately. Both key-based and regular expression-based interest types are supported.

The following example shows both key-based and regular expression-based interest types:

<gfe:client-region id="Example" pool-name="myPool">
    <gfe:key-interest durable="true" result-policy="KEYS">
        <bean id="key" class="java.lang.String">
            <constructor-arg value="someKey"/>
        </bean>
    </gfe:key-interest>
    <gfe:regex-interest pattern=".*" receive-values="false"/>
</gfe:client-region>

A special key, ALL_KEYS, means that interest is registered for all keys. The same can be accomplished by using the regular expression, ".\*".

The <gfe:*-interest> key and regular expression elements support three attributes: durable, receive-values, and result-policy.

durable

durable indicates whether the interest policy and subscription queue created for the client when the client connects to one or more servers in the cluster is maintained across client sessions. If the client goes away and comes back, a durable subscription queue on the servers for the client is maintained while the client is disconnected. When the client reconnects, the client receives any events that occurred while the client was disconnected from the servers in the cluster.

A subscription queue on the servers in the cluster is maintained for each Pool of connections defined in the client where a subscription has also been “enabled” for that Pool. The subscription queue is used to store (and possibly conflate) events sent to the client. If the subscription queue is durable, it persists between client sessions (that is, connections), potentially up to a specified timeout. If the client does not return within a given time frame the client Pool subscription queue is destroyed to reduce resource consumption on servers in the cluster. If the subscription queue is not durable, it is destroyed immediately when the client disconnects. You must decide whether your client should receive events that came while it was disconnected or if it needs to receive only the latest events after it reconnects.

receive-values

The receive-values attribute indicates whether the entry values are received for create and update events. If true, values are received. If false, only invalidation events are received.

result-policy

The result-policy is an enumeration of KEYS, KEYS_VALUE, and NONE. The default is KEYS_VALUES. The result-policy controls the initial dump when the client first connects to initialize the local cache, essentially seeding the client with events for all the entries that match the interest policy.

Client-side interest registration does not do much good without enabling subscription on the Pool, as mentioned earlier. In fact, it is an error to attempt interest registration without subscription enabled. The following example shows how to do so:

<gfe:pool ... subscription-enabled="true">
  ...
</gfe:pool>

In addition to subscription-enabled, can you also set subscription-ack-interval, subscription-message-tracking-timeout, and subscription-redundancy. subscription-redundancy is used to control how many copies of the subscription queue should be maintained by the servers in the cluster. If redundancy is greater than one, and the “primary” subscription queue (that is, the server) goes down, then a “secondary” subscription queue takes over, keeping the client from missing events in a HA scenario.

To control the amount of time (in seconds) that a “durable” subscription queue is maintained after a client is disconnected from the servers in the cluster, set the durable-client-timeout attribute on the <gfe:client-cache> element as follows:

<gfe:client-cache durable-client-timeout="600">
  ...
</gfe:client-cache>

For more information, see Client-to-Server Event Distribution in the GemFire product documentation.

JSON Support

GemFire has support for caching JSON documents in Regions, along with the ability to query stored JSON documents using the GemFire OQL (Object Query Language). JSON documents are stored internally as PdxInstance types by using the JSONFormatter class (see VMware GemFire Java API Reference) to perform conversion to and from JSON documents (as a String).

Spring Data for Tanzu GemFire provides the <gfe-data:json-region-autoproxy/> element to enable an AOP component to advise appropriate, proxied Region operations, which encapsulates the JSONFormatter and allows your applications to work directly with JSON Strings.

In addition, Java objects written to JSON configured Regions are automatically converted to JSON using Jackson’s ObjectMapper. When these values are read back, they are returned as a JSON String.

By default, <gfe-data:json-region-autoproxy/> performs the conversion for all Regions. To apply this feature to selected Regions, provide a comma-delimited list of Region bean IDs in the region-refs attribute. Other attributes include a pretty-print flag (defaults to false) and convert-returned-collections.

Also, by default, the results of the getAll() and values() Region operations are converted for configured Regions. This is done by creating a parallel data structure in local memory. This can incur significant overhead for large collections, so set the convert-returned-collections to false if you want to disable automatic conversion for these Region operations.

Note: Certain Region operations that use GemFire's proprietary Region.Entry, such as entries(boolean), entrySet(boolean), and getEntry() type, are not targeted for AOP advice. Additionally, , the entrySet() method, which returns a Set<java.util.Map.Entry<?, ?>>, is not affected.

The following example configuration shows how to set the pretty-print and convert-returned-collections attributes:

<gfe-data:json-region-autoproxy region-refs="myJsonRegion" pretty-print="true" convert-returned-collections="false"/>

This feature also works seamlessly with GemfireTemplate operations, provided that the template is declared as a Spring bean. The native QueryService operations are not supported.

Configuring a DiskStore

Spring Data for Tanzu GemFire supports DiskStore configuration and creation through the disk-store element, as the following example shows:

<gfe:disk-store id="Example" auto-compact="true" max-oplog-size="10"
                queue-size="50" time-interval="9999">
    <gfe:disk-dir location="/disk/location/one" max-size="20"/>
    <gfe:disk-dir location="/disk/location/two" max-size="20"/>
</gfe:disk-store>

Note: Spring Data for Tanzu GemFire only configures client-side `DiskStores` and cannot configure server-side `DiskStores`

DiskStore instances are used by Regions for file system persistent backup and overflow of evicted entries. Multiple GemFire components may share the same DiskStore. Additionally, multiple file system directories may be defined for a single DiskStore, as shown in the preceding example.

For an explanation of persistence, overflow, and configuration options on DiskStore instances, see Persistence and Overflow in the GemFire product documentation.

Configuring the Function Service

Spring Data for Tanzu GemFire provides annotation support for executing GemFire Functions. You can declare function executions using @FunctionId, like in the following example.

@OnRegion(region = "Numbers")
interface MixedResultTypeFunctionExecutions {

  @FunctionId("returnSingleObject")
  BigDecimal returnFive();

  @FunctionId("returnList")
  List<BigDecimal> returnList();

  @FunctionId("returnPrimitive")
  int returnPrimitive();
}

The interface methods act as stand-ins for functions which are executed when their corresponding method is called.

For more information about the Function execution framework, see Function Execution in the GemFire product documentation.