Normally, a Spring-based application will bootstrap GemFire using
Spring Data GemFire's XML namespace. Just by specifying a <gfe:cache/>
element
in Spring Data GemFire configuration meta-data, a single, peer GemFire Cache instance will be created
and initialized with default settings in the same JVM process as your application.
However, sometimes it is a requirement, perhaps imposed by your IT operations team, that GemFire must be fully managed and operated using the provided GemFire tool suite, such as with Gfsh. Using Gfsh, even though the application and GemFire will share the same JVM process, GemFire will bootstrap your Spring application context rather than the other way around. So, using this approach GemFire, instead of an application server, or a Java main class using Spring Boot, will bootstrap and host your application.
Keep in mind, however, that GemFire is not an application server. In addition, there are limitations to using this approach where GemFire Cache configuration is concerned.
In order to bootstrap a Spring application context in GemFire when starting a GemFire Server process using Gfsh, a user must make use of GemFire's Initalizer functionality. An Initializer can be used to specify a callback application that is launched after the Cache is initialized by GemFire.
An Initializer is specified within an
initializer
element using a minimal snippet of GemFire's native configuration meta-data inside
a cache.xml
file. The cache.xml
file is required in order to
bootstrap the Spring application context, much like a minimal snippet of Spring XML config is needed to
bootstrap a Spring application context configured with component scanning
(e.g. <context:component-scan base-packages="..."/>
)
As of Spring Data GemFire 1.4, such an Initializer is already conveniently provided
by the framework, the org.springframework.data.gemfire.support.SpringContextBootstrappingInitializer
.
The typical, yet minimal configuration for this class inside GemFire's cache.xml
file
will look like the following:
<?xml version="1.0"?> <!DOCTYPE cache PUBLIC "-//GemStone Systems, Inc.//GemFire Declarative Caching 7.0//EN" "http://www.gemstone.com/dtd/cache7_0.dtd"> <cache> <initializer> <class-name>org.springframework.data.gemfire.support.SpringContextBootstrappingInitializer</class-name> <parameter name="contextConfigLocations"> <string>classpath:application-context.xml</string> </parameter> </initializer> </cache>
The SpringContextBootstrappingInitializer
class follows similar conventions as
Spring's ContextLoaderListener class for bootstrapping a Spring context inside a Web Application, where
application context configuration files are specified with the contextConfigLocations
Servlet Context Parameter. In addition, the SpringContextBootstrappingInitializer
class can also be used with a basePackages
parameter to specify a comma-separated list
of base package containing the appropriately annotated application components that the Spring container
will search using component scanning and create Spring beans for:
<?xml version="1.0"?> <!DOCTYPE cache PUBLIC "-//GemStone Systems, Inc.//GemFire Declarative Caching 7.0//EN" "http://www.gemstone.com/dtd/cache7_0.dtd"> <cache> <initializer> <class-name>org.springframework.data.gemfire.support.SpringContextBootstrappingInitializer</class-name> <parameter name="basePackages"> <string>org.mycompany.myapp.services,org.mycompany.myapp.dao,...</string> </parameter> </initializer> </cache>
Then, with a properly configured and constructed CLASSPATH
along with the
cache.xml
file shown above specified as a command-line option when starting
a GemFire Server in Gfsh, the command-line would be:
gfsh>start server --name=Server1 --log-level=config ... --classpath="/path/to/spring-data-gemfire-1.4.0.jar:/path/to/application/classes.jar" --cache-xml-file="/path/to/gemfire/cache.xml"
The application-context.xml
can be any valid Spring context configuration meta-data
including all the SDG namespace elements. The only limitation with this approach is that the GemFire
Cache cannot be configured using the Spring Data GemFire namespace. In other words, none of the
<gfe:cache/>
element attributes, such as cache-xml-location
,
properties-ref
, critical-heap-percentage
, pdx-serializer-ref
,
lock-lease
, etc can be specified. If used, these attributes will be ignored.
The main reason for this is that GemFire itself has already created an initialized the Cache before
the Initializer gets invoked. As such, the Cache will already exist and since it is
a "Singleton", it cannot be re-initialized or have any of it's configuration augmented.
Spring Data GemFire already provides existing support for wiring GemFire components (such as CacheListeners,
CacheLoaders or CacheWriters) that are declared and created by GemFire in cache.xml
using the WiringDeclarableSupport
class as described in
Section 6.6.2, “Configuration using auto-wiring and annotations”.
However, this only works when Spring does the bootstrapping (i.e. bootstraps GemFire). When your Spring
application context is the one bootstrapped by GemFire, then these GemFire components go unnoticed since
the Spring application context does not even exist yet! The Spring application context will not get created
until GemFire calls the Initializer, which occurs after all the other GemFire components
and configuration have already been created and initialized.
So, in order to solve this problem, a new LazyWiringDeclarableSupport
class was
introduced, that is, in a sense, Spring application context aware. The intention of this abstract
base class is that any implementing class will register itself to be configured by the Spring
application context created by GemFire after the Initializer is called. In essence,
this give your GemFire managed component a chance to be configured and auto-wired with Spring beans defined
in the Spring application context.
In order for your GemFire application component to be auto-wired by the Spring container, create a
application class that extends the LazyWiringDeclarableSupport
and annotate
any class member that needs to be provided as a Spring bean dependency, similar to:
public static final class UserDataSourceCacheLoader extends LazyWiringDeclarableSupport implements CacheLoader<String, User> { @Autowired private DataSource userDataSource; ... }
As implied by the CacheLoader example above, you might necessarily (although, rare) have defined both
a Region and CacheListener component in GemFire cache.xml
. The CacheLoader may need
access to an application DAO, or perhaps Spring application context defined JDBC Data Source
for loading "Users" into a GemFire Cache REPLICATE
Region on start. Of course,
one should be careful in mixing the different life-cycles of GemFire and the Spring Container together
in this manner as not all use cases and scenarios are supported. The GemFire cache.xml
configuration would be similar to the following (which comes from SDG's test suite):
<?xml version="1.0"?> <!DOCTYPE cache PUBLIC "-//GemStone Systems, Inc.//GemFire Declarative Caching 7.0//EN" "http://www.gemstone.com/dtd/cache7_0.dtd"> <cache> <region name="Users" refid="REPLICATE"> <region-attributes initial-capacity="101" load-factor="0.85"> <key-constraint>java.lang.String</key-constraint> <value-constraint>org.springframework.data.gemfire.repository.sample.User</value-constraint> <cache-loader> <class-name>org.springframework.data.gemfire.support.SpringContextBootstrappingInitializerIntegrationTest$UserDataStoreCacheLoader</class-name> </cache-loader> </region-attributes> </region> <initializer> <class-name>org.springframework.data.gemfire.support.SpringContextBootstrappingInitializer</class-name> <parameter name="basePackages"> <string>org.springframework.data.gemfire.support.sample</string> </parameter> </initializer> </cache>