Chapter 1. Bootstrapping GemFire through the Spring container

One of the first tasks when using GemFire and Spring is to configure the data grid using dependency injection via the Spring container. While this is possible out of the box, the configuration tends to be verbose and only address basic cases. To address this problem, the Spring GemFire project provides several classes that enable the configuration of distributed caches or regions to support a variety of scenarios with minimal effort.

1.1. Using the Spring GemFire Namespace

To simplify configuration, SGF provides a dedicated namespace for most of its components. However, one can opt to configure the objects directly through the usual <object> definition. For more information about XML Schema-based configuration in Spring.NET, see this appendix in the Spring.NET Framework reference documentation.

<?xml version="1.0" encoding="UTF-8"?>
<objects xmlns="http://www.springframework.net" 
         xmlns:gfe1="http://www.springframework.net/gemfire2">

  <gfe:cache ...>3

</objects>
    

1

Spring GemFire namespace prefix. Any name can do but through out the reference documentation, the gfe will be used.

2

The namespace URI.

3

Declaration example for the GemFire namespace. Notice the prefix usage.

For the remainder of this doc, to improve readability, the XML examples will simply refer to the <gfe> namespace without the namespace declaration, where possible. To enable Spring.NET to recognize this namespace you need to register it in the applications App.config section.

<configuration>

  <configSections>
    <sectionGroup name="spring">
      <!-- other Spring config sections handler like context, typeAliases, etc not shown for brevity -->   
      <section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core"/>        
     </sectionGroup>
  </configSections>

  <spring>
    <parsers> 
      <parser type="Spring.Data.GemFire.Config.GemfireNamespaceParser, Spring.Data.GemFire" />
    </parsers> 
  </spring>

</configuration>
    

1.2. Configuring the GemFire Cache

In order to use the GemFire Fabric, one needs to either create a new Cache or connect to an existing one. As of the current version of GemFire (6.0.x) there can be only one opened cache per application. In most cases the cache is created once and then all other consumers connect to it.

In its simplest form, a cache can be defined in one line:

<gfe:cache />  

The declaration above declares an object(CacheFactoryObject) for the GemFire Cache, named gemfire-cache. All the other SGF components use this naming convention if no name is specified, allowing for very concise configurations. The definition above will try to connect to an existing cache and, in case one does not exist, create it. Since no additional properties were specified the created cache uses the default cache configuration.Especially in environments with opened caches, this basic configuration can go a long way.

For scenarios where the cache needs to be configured, the user can pass in a reference the GemFire configuration file:

<gfe:cache id="cache-with-xml" cache-xml-location="cache-config.xml"/>

In this example, if the cache needs to be created, it will use the file named cache.xml located in the root of the runtime directory. Only if the cache is created will the configuration file be used.

In addition to referencing an external configuration file one can specify GemFire settings directly through NameValueCollection properties. This can be quite handy when just a few settings need to be changed.

  <gfe:cache id="cache-with-xml" cache-xml-location="cache-config.xml" properties-ref="props"/>

  <object name="props" type="System.Collections.Specialized.NameValueCollection">
    <property name="['log-level']" value="warning"/>
    <!-- set properties such as durable-client-id, durable-timeout for a durable cache -->
  </object>

Other properties that can be set in the namespace are disconnect-on-close, keepalive-on-close, and distributed-system-name as shown below.

  <gfe:cache disconnect-on-close="false" keepalive-on-close="true" distributed-system-name="MySystemName"/>

The disconnect-on-close property is set to true by default. It should be changed to false in case you encounter a race condition in the 3.0.0.5 client library that results in the disconnect call hanging the application.

Or can use fallback to a raw <objects> declaration:

  <object name="cache-with-props" type="Spring.Data.GemFire.CacheFactoryObject, Spring.Data.GemFire">   
    <property name="Properties">
      <name-values>
        <add key="log-level" value="warning"/>
      </name-values>
    </property>
  </object>

In this last example, the SGF classes are declared and configured directly without relying on the namespace. As one can tell, this approach is a generic one, exposing more of the backing infrastructure.

<object name="default-cache" type="Spring.Data.GemFire.CacheFactoryObject, Spring.Data.GemFire"/>  

Here, the default-cache will try to connect to an existing cache and, in case one does not exist, create a local client cache. Since no additional properties were specified the created cache uses the default cache configuration.

The name of the cache will be the name of the Spring object definition unless you specify a different name using the NameValueCollection property described next. Note, you can specify the name of the underlying DistributedSystem that will be created with the property DistributedSystemName

Especially in environments with opened caches, this basic configuration can go a long way. For scenarios where the cache needs to be configured, the user can pass in a reference the GemFire configuration file:

<object name="cache-with-xml" type="Spring.Data.GemFire.CacheFactoryObject, Spring.Data.GemFire">
  <property name="CacheXml" value="cache.xml"/>
</object>

In this example, if the cache needs to be created, it will use the file named cache.xml located in the runtime directory.

In addition to referencing an external configuration file one can specify GemFire settings directly through .NET name value properties. This can be quite handy when just a few settings need to be changed:

<object name="cache-with-props" type="Spring.Data.GemFire.CacheFactoryObject, Spring.Data.GemFire"> 
  <property name="Properties">
    <name-values>
      <add key="log-level" value="warning"/>
    </name-values>
  </property>
</object>

The 'name' property is used to set the name of the Cache object. For a complete list of properties refer to the GemFire reference documentation.

Spring object definitions support property replacement through the use of variable replacement. The following configuration allows you to externalize the properties from the Spring configuration file which is a best practice. The Spring configuration file is usually an embedded assembly resource so as to prevent accidental changes in production. There are seven locations supported out of the box in Spring.NET where you can place your externalized configuration data and can be extended to support your own locations. In the following example the configuration flues would come from a name-value configuration section in App/Web.config

<gfe:cache id="cache-with-props" cache-xml-location="cache-config.xml" properties-ref="props"/>

<object name="props" type="System.Collections.Specialized.NameValueCollection">
  <property name="['log-level']" value="${cache.log-level}"/>
</object>

<object type="Spring.Objects.Factory.Config.VariablePlaceholderConfigurer, Spring.Core">
   <property name="VariableSources">
      <list>
         <object type="Spring.Objects.Factory.Config.ConfigSectionVariableSource, Spring.Core">
            <property name="SectionNames" value="CacheConfiguration" />
         </object>
      </list>
   </property>
</object>

The CacheConfiguration section in App.config would then look like the following

<configuration>
  <configSections>
    <section name="CacheConfiguration" type="System.Configuration.NameValueSectionHandler"/>
  </configSections>

  <CacheConfiguration>
    <add key="cache.log-level" value="warning"/>
  </CacheConfiguration>

</configuration>

It is worth pointing out again, that the cache settings apply only if the cache needs to be created, there is no opened cache in existence otherwise the existing cache will be used and the configuration will simply be discarded.

1.3. Configuring a GemFire Region

Once the Cache is configured, one needs to configure one or more Regions to interact with the data fabric. SGF allows various region types to be configured and created directly from Spring or in case they are created directly in GemFire, retrieved as such.

For more information about the various region types and their capabilities as well as configuration options, please refer to the GemFire Developer's Guide and community site.

1.3.1. Using an externally configured Region

For consuming but not creating Regions (for example in case, the regions are already configured through GemFire native configuration, the cache.xml), one can use the lookup-region element. Simply declare the target region name the name attribute; for example to declare a object definition, named region-object for an existing region named orders one can use the following definition:

<gfe:lookup-region id="region-object" name="orders"/>

If the name is not specified, the object name will be used automatically. The example above becomes:

<!-- lookup for a region called 'orders' -->
<gfe:lookup-region id="orders"/>
[Note]Note

If the region does not exist, an initialization exception will be thrown. For configuring new GemFire regions proceed to the sections below for client region and advanced client region configuration.

Note that in the previous examples, since no cache name was defined, the default SGF naming convention (gemfire-cache) was used. If that is not an option, one can point to the cache object through the cache-ref attribute:

<gfe:cache id="cache"/>
		
<gfe:lookup-region id="region-object" name="orders" cache-ref="cache"/>

The lookup-region provides a simple way of retrieving existing, pre-configured regions without exposing the region semantics or setup infrastructure.

1.3.2. Client Region

GemFire supports various deployment topologies for managing and distributing data. For .NET the currenlty supported topoloy is client-server. Additional topologies such as peer-to-peer may be supported in future releases of the .NET client as they are supported in the Java client already. To declare client regions which connect to a backing cache server, SGF offers dedicated support for such configuration through the client-region and pool elements. As the name imply, the former defines a client region while the latter connection pools to be used/shared by the various client regions.

Below is a usual configuration for a client region:

<gfe:cache/>

<!-- client region declaration -->
<gfe:client-region id="complex" pool-name="gemfire-pool">
    <gfe:cache-listener ref="c-listener"/>
</gfe:client-region>
	
<object id="c-listener" type="Spring.Data.GemFire.Tests.SimpleCacheListener, Spring.Data.GemFire.Tests"/>

<!-- pool declaration -->	
<gfe:pool id="gemfire-pool" subscription-enabled="false">
    <gfe:server host="localhost" port="40404"/>  
</gfe:pool>

Just as the other region types, client-region allows defining CacheListeners. It also relies on the same naming conventions in case the region name or the cache are not set explicitely. However, it also requires a connection pool to be specified for connecting to the server. Each client can have its own pool or they can share the same one.

The client-region can also reference multiple cache-listeners:

<gfe:cache/>

  <gfe:client-region id="complex" pool-name="gemfire-pool">    
    <gfe:cache-listener>      
      <ref object="c-listener"/>
      <object type="Spring.Data.GemFire.Tests.SimpleCacheListener, Spring.Data.GemFire.Tests"/>
    </gfe:cache-listener> 

  </gfe:client-region>

  <object id="c-listener" type="Spring.Data.GemFire.Tests.SimpleCacheListener, Spring.Data.GemFire.Tests"/>

  <!-- pool declaration -->	
  <gfe:pool id="gemfire-pool" subscription-enabled="false">
    <gfe:server host="localhost" port="40404"/>  
  </gfe:pool>

Other options to configure the client-region are to specify a specific cache by name, set the and persistence of the reigon via the persistent attribute, and also to set the various RegionAttributes. RegionAttributes are set using the attributes-ref element:

  <gfe:client-region id="simple" pool-name="gemfire-pool" attributes-ref="myCustomRegionAttributes" />

  <!-- some common base cache configuration that can be used later in parent object referencts -->
  <object id="cachingProxy" type="Spring.Data.GemFire.RegionAttributesFactoryObject, Spring.Data.GemFire">
    <property name="ClientNotification" value="true"/>
  </object>

  <object id="myCustomRegionAttributes" 
          parent="cachingProxy"
          type="Spring.Data.GemFire.RegionAttributesFactoryObject, Spring.Data.GemFire">
    <property name="LruEntriesLimit" value="1234"/>
  </object>

The use of Spring's parent referent allows you set setup commonly used configurations and then extend them by overriding or adding additional properties.

For a full list of options to set on the client and especially on the pool, please refer to the SGF schema (Appendix A, Spring.NET for Gemfire's spring-gemfire-1.0.xsd) and the GemFire documentation.

1.3.2.1. Client Interests

To minimize network traffic, each client can define its own 'interest', pointing out to GemFire, the data it actually needs. In SGF, interests can be defined for each client, both key-based and regular-expression-based types being supported; for example:

  <gfe:client-region id="complex" pool-name="gemfire-pool">    

    <gfe:all-keys-interest durable="true" result-policy="Keys"/>
    
    <gfe:key-interest result-policy="KeysAndValues" key-ref="cacheableString"/>

    <gfe:regex-interest pattern=".*"/>    

  </gfe:client-region>

  <object id="cacheableString" type="GemStone.GemFire.Cache.CacheableString, GemStone.GemFire.Cache">
    <constructor-arg index="0" value="hello"/>
  </object>

1.4. Advanced Region Creation

SGF namespaces allow short and easy configuration of the major GemFire regions and associated entities. However, there might be corner cases where the namespaces are not enough, where a certain combination or set of attributes needs to be used. For such situations, using directly the SGF FactoryObjects is a possible alternative as it gives access to the full set of options at the expense of conciseness.

As a warm up, below are some common configurations, declared through raw objects definitions.

<object name="basic" type="Spring.Data.GemFire.ClientRegionFactoryObject, Spring.Data.GemFire">
  <property Name="Cache">
    <object type="Spring.Data.GemFire.CacheFactoryObject, Spring.Data.GemFire"/> 
  </property> 
</object>

By default the region name is the name of the object definition unless explicitly specified using the Name property. Notice how the GemFire cache definition has been nested into the declaring region definition. Let's add more regions and make the cache a top level object.

Since the region object definition name is usually the same with that of the cache, the name property can be omitted (the object name will be used automatically).

<!-- shared cache across regions -->
<object id="cache" type="Spring.Data.GemFire.CacheFactoryObject, Spring.Data.GemFire"/> 

<!-- region named 'basic' -->
<object name="basic" type="Spring.Data.GemFire.ClientRegionFactoryObject, Spring.Data.GemFire">
  <property name="Endpoints" value="localhost:40404"/>
  <property name="Cache" ref="Cache"/>
</object>

<!-- region with a name different then the object definition -->
<object name="basic" type="Spring.Data.GemFire.ClientRegionFactoryObject, Spring.Data.GemFire">
  <property name="Endpoints" value="localhost:40404"/>
  <property name="Cache" ref="Cache"/>
  <property name="Name" value="default-region"/>
</object>

It is worth pointing out, that for the vast majority of cases configuring the cache loader, listener and writer through the Spring container is preferred since the same instances can be reused across multiple regions and additionally, the instances themselves can benefit from the container's rich feature set:

  <object name="baseRegion" abstract="true">
    <property name="Endpoints" value="localhost:40404"/>
    <property name="Cache" ref="Cache"/>   <!-- definition not shown here -->
  </object>

  <object name="listeners" type="Spring.Data.GemFire.ClientRegionFactoryObject, Spring.Data.GemFire"
          parent="baseRegion">
    <property name="CacheListener">
      <object type="Spring.Data.GemFire.Tests.SimpleCacheListener, Spring.Data.GemFire.Tests">
        <!-- set properties or constructor arguments -->
      </object>
    </property>
    <property name="CacheLoader">
      <object type="Spring.Data.GemFire.Tests.SimpleCacheLoader, Spring.Data.GemFire.Tests"/>      
    </property>
    <property name="CacheWriter">
      <object type="Spring.Data.GemFire.Tests.SimpleCacheWriter, Spring.Data.GemFire.Tests"/>
    </property>
  </object>

1.4.1. Configuring update interest for client Region

For scenarios where a CacheServer is used and clients need to be configured and the namespace is not an option, SGF offers a dedicated configuration class named: ClientRegionFactoryObject. This allows client interests to be registered in both key and regex form through AllKeysInterest, KeyInterest, and RegexInterest classes in the Spring.Data.GemFire namespace. Here is an example of how to configure the AllKeys interest in the region.

  <object name="baseRegion" abstract="true">
    <property name="Endpoints" value="localhost:40404"/>
    <property name="Cache" ref="Cache"/>   <!-- Cache definition not shown here -->
    <property name="Attributes">
      <object type="Spring.Data.GemFire.RegionAttributesFactoryObject, Spring.Data.GemFire">
        <property name="ClientNotification" value="true"/>
      </object>
    </property>
  </object>

  <object name="region-all-keys-interest" type="Spring.Data.GemFire.ClientRegionFactoryObject, Spring.Data.GemFire"
          parent="baseRegion">
    <property name="interests">
      <list>
        <object type="Spring.Data.GemFire.AllKeysInterest"/>
      </list>
    </property>
  </object>

This example makes use of Spring's object configuration inheritance allowing you to place common configuration in the baseRegion object definition and then refer to it later via the parent attribute.

Users that need fine control over a region, can configure it in Spring by using the Attributes property. To ease declarative configuration in Spring, SGF provides an RegionAttributesFactoryObject. The previous examples shows configuring the baseRegion by embedding the use of the RegionAttributesFactoryObject:

To register interest for a set of key, use the KeyInterest class, as shown below from the sample application

  <object name="region" type="Spring.Data.GemFire.ClientRegionFactoryObject, Spring.Data.GemFire"
                parent="baseRegion">  <!-- baseRegion definition not shown here -->
    <property name="Interests">
      <list>
        <object type="Spring.Data.GemFire.KeyInterest">
          <property name="Keys">
            <list>
              <object type="GemStone.GemFire.Cache.CacheableString" factory-method="Create">
                <constructor-arg value="Key-123"/>
              </object>
            </list>
          </property>
        </object>
      </list>
    </property>
  </object>

To register interest based on a regular expression, use the following configuration

  <object name="Region" type="Spring.Data.GemFire.ClientRegionFactoryObject, Spring.Data.GemFire"   
          parent="baseRegion">  <!-- baseRegion definition not shown here -->
    <property name="Interests">
      <list>
        <object type="Spring.Data.GemFire.RegexInterest">
          <property name="Regex" value="Key-.*"/>
        </object>
      </list>
    </property>
  </object>

This would only register interest in keys of the type 'Key-123' or 'Key-4abc'.

See below an example of configuring a region through Spring XML embedding the use of the RegionAttributesFactoryObject, no parent object definition is used.

  <object name="Region" type="Spring.Data.GemFire.ClientRegionFactoryObject, Spring.Data.GemFire"
    <property name="Endpoints" value="localhost:40404"/>
    <property name="Cache" ref="Cache"/>
    <property name="Name" value="exampleregion"/>
    <property name="Attributes">
      <object type="Spring.Data.GemFire.RegionAttributesFactoryObject, Spring.Data.GemFire">
        <property name="ClientNotification" value="true"/>
      </object>
    </property>
    <property name="Interests">
      <list>
        <object type="Spring.Data.GemFire.RegexInterest">
          <property name="Regex" value="Key-.*"/>
        </object>
      </list>
    </property>
  </object>

Please refer to the API documentation for more information on the various IInterest subclasses.

1.5. Advantages of using Spring over GemFire cache.xml

With SGF, GemFire regions, pools and cache can be configured either through Spring or directly inside GemFire, native, cache.xml file. While both are valid approaches, it's worth pointing out that Spring's powerful DI container and AOP functionality makes it very easy to wire GemFire into an application. This lalso lets you leverage features such as replacement of varaiable placeholders, e..g. ${port}. Also configuring a region cache loader, listener and writer through the Spring container is preferred since the same instances can be reused across multiple regions. Spring object definition inheritance also allows you to create base region and region attribute object definitions that can later be referred to using Spring's 'parent' attribute.

Whatever route one chooses to go, SGF supports both approaches allowing for easy migrate between them without forcing an upfront decision.