2445 lines
116 KiB
Plaintext
2445 lines
116 KiB
Plaintext
[[bootstrap-annotation-config]]
|
|
= Bootstrapping {data-store-name} with the Spring Container using Annotations
|
|
|
|
{sdg-name} ({sdg-acronym}) 2.0 introduces a new annotation-based configuration model
|
|
to configure and bootstrap {data-store-name} using the Spring container.
|
|
|
|
The primary motivation for introducing an annotation-based approach to the configuration of {data-store-name}
|
|
in a Spring context is to enable Spring application developers to _get up and running_ as _quickly_
|
|
and as _easily_ as possible.
|
|
|
|
Let's get started!
|
|
|
|
TIP: If you would like to get started even faster, refer to
|
|
the <<bootstap-annotations-quickstart, Quick Start>> section.
|
|
|
|
[[bootstrap-annotation-config-introduction]]
|
|
== Introduction
|
|
|
|
{data-store-name}, along with Spring Data for {data-store-name}, offer many configuration options:
|
|
|
|
* {x-data-store-javadoc}[Java API]
|
|
* {x-data-store-docs}/reference/topics/chapter_overview_cache_xml.html[`cache.xml`]
|
|
* {x-data-store-docs}/tools_modules/gfsh/chapter_overview.html[_Gfsh_]
|
|
with {x-data-store-docs}/configuring/chapter_overview.html[Cluster Configuration]
|
|
* <<bootstrap,Spring XML/Java-based configuration>>
|
|
|
|
In addition, {data-store-name} and Spring Data for {data-store-name} both support different topologies:
|
|
|
|
* {x-data-store-docs}/topologies_and_comm/cs_configuration/chapter_overview.html[client/server]
|
|
* {x-data-store-docs}/topologies_and_comm/p2p_configuration/chapter_overview.html[P2P]
|
|
* {x-data-store-docs}/topologies_and_comm/multi_site_configuration/chapter_overview.html[WAN]
|
|
* {x-data-store-wiki}/Geode+Internal+Architecture?src=contextnavpagetreemode[distributed system design patterns]
|
|
(such as shared-nothing architecture).
|
|
|
|
All of these configuration options and topology arrangements can pose challenges when setting up and using
|
|
{data-store-name} properly. The Spring Data for {data-store-name} annotation-based configuration model aims to
|
|
simplify configuration in the context of topology, plus more.
|
|
|
|
The annotation-based configuration model is an alternative to XML-based configuration using {sdg-name}'s XML namespace.
|
|
With XML, you could use the `gfe` XML schema for configuration and the `gfe-data` XML schema for data access.
|
|
See "<<bootstrap>>" for more details.
|
|
|
|
NOTE: As of {sdg-acronym} 2.0, the annotation-based configuration model does not yet support the configuration of
|
|
{data-store-name}'s WAN components and topology.
|
|
|
|
Like Spring Boot, {sdg-name}'s annotation-based configuration model was designed as an opinionated,
|
|
convention-over-configuration approach for using {data-store-name}. Indeed, this annotation-based configuration model
|
|
was inspired by Spring Boot as well as several other Spring and Spring Data projects, collectively.
|
|
|
|
By following convention, all annotations provide reasonable and sensible defaults for all configuration attributes.
|
|
The default value for a given annotation attribute directly corresponds to the default value provided in
|
|
{data-store-name} for the same configuration property.
|
|
|
|
The intention is to let you enable {data-store-name} features or an embedded services by declaring the appropriate
|
|
annotation on your Spring `@Configuration` or `@SpringBootApplication` class without needing to unnecessarily configure
|
|
a large number of properties just to use the feature or service.
|
|
|
|
Again, _getting started_, _quickly_ and as _easily_, is the primary objective.
|
|
|
|
However, the option to customize the configuration metadata and behavior of {data-store-name} is there if you need it,
|
|
and {sdg-name}'s annotation-based configuration quietly backs away. You need only specify the configuration attributes
|
|
you wish to adjust. Also, as we will see later in this document, there are several ways to configure a {data-store-name}
|
|
feature or embedded service by using the annotations.
|
|
|
|
You can find all the new {sdg-acronym} Java `Annotations` in the `org.springframework.data.gemfire.config.annotation`
|
|
package.
|
|
|
|
[[bootstrap-annotation-config-geode-applications]]
|
|
== Configuring {data-store-name} Applications with Spring
|
|
|
|
Like all Spring Boot applications that begin by annotating the application class with `@SpringBootApplication`,
|
|
a Spring Boot application can easily become a {data-store-name} cache application by declaring any one of three
|
|
main annotations:
|
|
|
|
* `@ClientCacheApplication`
|
|
* `@PeerCacheApplication`
|
|
* `@CacheServerApplication`
|
|
|
|
These three annotations are the Spring application developer's starting point when working with {data-store-name}.
|
|
|
|
To realize the intent behind these annotations, you must understand that there are two types of cache instances
|
|
that can be created with {data-store-name}: a client cache or a peer cache.
|
|
|
|
You can configure a Spring Boot application as a {data-store-name} cache client with an instance of `ClientCache`,
|
|
which can communicate with an existing cluster of {data-store-name} servers used to manage the application's data.
|
|
The client-server topology is the most common system architecture employed when using {data-store-name} and you can
|
|
make your Spring Boot application a cache client, with a `ClientCache` instance, simply by annotating it with
|
|
`@ClientCacheApplication`.
|
|
|
|
Alternatively, a Spring Boot application may be a peer member of a {data-store-name} cluster. That is, the application
|
|
itself is just another server in a cluster of servers that manages data. The Spring Boot application creates
|
|
an "embedded", peer `Cache` instance when you annotate your application class with `@PeerCacheApplication`.
|
|
|
|
By extension, a peer cache application may also serve as a `CacheServer` too, allowing cache clients to connect
|
|
and perform data access operations on the server. This is accomplished by annotating the application class with
|
|
`@CacheServerApplication` in place of `@PeerCacheApplication`, which creates a peer `Cache` instance along with
|
|
the `CacheServer` that allows cache clients to connect.
|
|
|
|
NOTE: A {data-store-name} server is not necessarily a cache server by default. That is, a server is not necessarily
|
|
set up to serve cache clients just because it is a server. A {data-store-name} server can be a peer member (data node)
|
|
of the cluster managing data without serving any clients while other peer members in the cluster are indeed set up
|
|
to serve clients in addition to managing data. It is also possible to set up certain peer members in the cluster as
|
|
non-data nodes, called {x-data-store-docs}/developing/region_options/data_hosts_and_accessors.html[data accessors],
|
|
which do not store data, but act as a proxy to service clients as `CacheServers`. Many different topologies
|
|
and cluster arrangements are supported by {data-store-name}, but are beyond the scope of this document.
|
|
|
|
By way of example, if you want to create a Spring Boot cache client application, start with the following:
|
|
|
|
.Spring-based {data-store-name} `ClientCache` application
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@ClientCacheApplication
|
|
class ClientApplication { .. }
|
|
----
|
|
|
|
Or, if you want to create a Spring Boot application with an embedded peer `Cache` instance, where your application
|
|
will be a server and peer member of a cluster (distributed system) formed by {data-store-name},
|
|
start with the following:
|
|
|
|
.Spring-based {data-store-name} embedded peer `Cache` application
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@PeerCacheApplication
|
|
class ServerApplication { .. }
|
|
----
|
|
|
|
Alternatively, you can use the `@CacheServerApplication` annotation in place of `@PeerCacheApplication` to create
|
|
both an embedded peer `Cache` instance along with a `CacheServer` running on `localhost`, listening on the default
|
|
cache server port, `40404`, as follows:
|
|
|
|
.Spring-based {data-store-name} embedded peer `Cache` application with `CacheServer`
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@CacheServerApplication
|
|
class ServerApplication { .. }
|
|
----
|
|
|
|
[[bootstrap-annotation-config-client-server-applications]]
|
|
== Client/Server Applications In-Detail
|
|
|
|
There are multiple ways that a client can connect to and communicate with servers in a {data-store-name} cluster.
|
|
The most common and recommended approach is to use {data-store-name} Locators.
|
|
|
|
NOTE: A cache client can connect to one or more Locators in the {data-store-name} cluster instead of directly to a
|
|
`CacheServer`. The advantage of using Locators over direct `CacheServer` connections is that Locators provide metadata
|
|
about the cluster to which the client is connected. This metadata includes information such as which servers contain
|
|
the data of interest or which servers have the least amount of load. A client `Pool` in conjunction with a Locator
|
|
also provides fail-over capabilities in case a `CacheServer` crashes. By enabling the `PARTITION` Region (PR)
|
|
single-hop feature in the client `Pool`, the client is routed directly to the server containing the data requested
|
|
and needed by the client.
|
|
|
|
NOTE: Locators are also peer members in a cluster. Locators actually constitute what makes up a cluster of
|
|
{data-store-name} nodes. That is, all nodes connected by a Locator are peers in the cluster, and new members
|
|
use Locators to join a cluster and find other members.
|
|
|
|
By default, {data-store-name} sets up a "DEFAULT" `Pool` connected to a `CacheServer` running on `localhost`,
|
|
listening on port `40404` when a `ClientCache` instance is created. A `CacheServer` listens on port `40404`,
|
|
accepting connections on all system NICs. You do not need to do anything special to use the client-server topology.
|
|
Simply annotate your server-side Spring Boot application with `@CacheServerApplication` and your client-side
|
|
Spring Boot application with `@ClientCacheApplication`, and you are ready to go.
|
|
|
|
If you prefer, you can even start your servers with Gfsh's `start server` command. Your Spring Boot `@ClientCacheApplication`
|
|
can still connect to the server regardless of how it was started. However, you may prefer to configure and start your
|
|
servers by using the {sdg-name} approach since a properly annotated Spring Boot application class is far more intuitive
|
|
and easier to debug.
|
|
|
|
As an application developer, you will no doubt want to customize the "DEFAULT" `Pool` set up by {data-store-name}
|
|
to possibly connect to one or more Locators, as the following example demonstrates:
|
|
|
|
.Spring-based {data-store-name} `ClientCache` application using Locators
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@ClientCacheApplication(locators = {
|
|
@Locator(host = "boombox" port = 11235),
|
|
@Locator(host = "skullbox", port = 12480)
|
|
})
|
|
class ClientApplication { .. }
|
|
----
|
|
|
|
Along with the `locators` attribute, the `@ClientCacheApplication` annotation has a `servers` attribute as well.
|
|
The `servers` attribute can be used to specify one or more nested `@Server` annotations that let the cache client
|
|
connect directly to one or more servers, if necessary.
|
|
|
|
NOTE: You can use either the `locators` or `servers` attribute, but not both (this is enforced by {data-store-name}).
|
|
|
|
You can also configure additional `Pool` instances (other than the "DEFAULT" `Pool` provided by {data-store-name}
|
|
when a `ClientCache` instance is created with the `@ClientCacheApplication` annotation) by using the `@EnablePool`
|
|
and `@EnablePools` annotations.
|
|
|
|
NOTE: `@EnablePools` is a composite annotation that aggregates several nested `@EnablePool` annotations on
|
|
a single class. Java 8 and earlier does not allow more than one annotation of the same type to be declared
|
|
on a single class.
|
|
|
|
The following example uses the `@EnablePool` and `@EnablePools` annotations:
|
|
|
|
.Spring-based {data-store-name} `ClientCache` application using multiple named `Pools`
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@ClientCacheApplication(logLevel = "info")
|
|
@EnablePool(name = "VenusPool", servers = @Server(host = "venus", port = 48484),
|
|
min-connections = 50, max-connections = 200, ping-internal = 15000,
|
|
prSingleHopEnabled = true, readTimeout = 20000, retryAttempts = 1,
|
|
subscription-enable = true)
|
|
@EnablePools(pools = {
|
|
@EnablePool(name = "SaturnPool", locators = @Locator(host="skullbox", port=20668),
|
|
subsription-enabled = true),
|
|
@EnablePool(name = "NeptunePool", severs = {
|
|
@Server(host = "saturn", port = 41414),
|
|
@Server(host = "neptune", port = 42424)
|
|
}, min-connections = 25))
|
|
})
|
|
class ClientApplication { .. }
|
|
----
|
|
|
|
The `name` attribute is the only required attribute of the `@EnablePool` annotation. As we will see later, the value
|
|
of the `name` attribute corresponds to both the name of the `Pool` bean created in the Spring container as well as
|
|
the name used to reference the corresponding configuration properties. It is also the name of the `Pool` registered
|
|
and used by {data-store-name}.
|
|
|
|
Similarly, on the server, you can configure multiple `CacheServers` that a client can connect to, as follows:
|
|
|
|
.Spring-based {data-store-name} `CacheServer` application using multiple named `CacheServers`
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@CacheSeverApplication(logLevel = "info", autoStartup = true, maxConnections = 100)
|
|
@EnableCacheServer(name = "Venus", autoStartup = true,
|
|
hostnameForClients = "venus", port = 48484)
|
|
@EnableCacheServers(servers = {
|
|
@EnableCacheServer(name = "Saturn", hostnameForClients = "saturn", port = 41414),
|
|
@EnableCacheServer(name = "Neptune", hostnameForClients = "neptune", port = 42424)
|
|
})
|
|
class ServerApplication { .. }
|
|
----
|
|
|
|
NOTE: Like `@EnablePools`, `@EnableCacheServers` is a composite annotation for aggregating multiple `@EnableCacheServer`
|
|
annotations on a single class. Again, Java 8 and earlier does not allow more than one annotation of the same type
|
|
to be declared on a single class.
|
|
|
|
One thing an observant reader may have noticed is that, in all cases, you have specified hard-coded values for all
|
|
hostnames, ports, and configuration-oriented annotation attributes. This is not ideal when the application gets
|
|
promoted and deployed to different environments, such as from DEV to QA to STAGING to PROD.
|
|
|
|
The next section covers how to handle dynamic configuration determined at runtime.
|
|
|
|
[[bootstrap-annotation-config-locator]]
|
|
== Configuring and Bootstrapping Locators
|
|
|
|
Besides {data-store-name} Cache applications, you may also create {data-store-name} Locator applications.
|
|
|
|
A {data-store-name} Locator is a JVM process that allows nodes to join a {data-store-name} cluster as peer members.
|
|
Locators also enable clients to discover servers in a cluster. A Locator provides meta-data to the clients to uniformly
|
|
balance the load across the members in the cluster, enables single-hop data access operations, along with other things.
|
|
|
|
A complete discussion of Locators is beyond the scope of this document. Readers are encouraged to read
|
|
the {data-store-name} {apache-geode-docs}/topologies_and_comm/topology_concepts/how_member_discovery_works.html[User Guide]
|
|
for more details on Locators and their role in the cluster.
|
|
|
|
To configure and bootstrap a standalone Locator process, do the following:
|
|
|
|
.Spring Boot, {data-store-name} Locator Application
|
|
[source,java]
|
|
----
|
|
@SpringBootApplication
|
|
@LocatorApplication(port = 12345)
|
|
class LocatorApplication { ... }
|
|
----
|
|
|
|
You can start multiple Locators in your cluster. The only requirement is that the member name must be unique
|
|
in the cluster. Use the `name` attribute of the `@LocatorApplication` annotation to name the member Locator
|
|
in the cluster accordingly. Alternatively, you can set the `spring.data.gemfire.locator.name` property in Spring Boot's
|
|
`application.properties`.
|
|
|
|
Additionally, you must ensure that each Locator starts on a unique port if you fork multiple Locators on the same
|
|
machine. Set either the `port` annotation attribute or the `spring.data.gemfire.locator.port` property.
|
|
|
|
You may then start 1 or more {data-store-name} peer cache members in the cluster joined by the Locator, or Locators,
|
|
also configured and bootstrapped with Spring, like so:
|
|
|
|
.Spring Boot, {data-store-name} `CacheServer` Application joined by the Locator on `localhost`, port `12345`
|
|
[source,java]
|
|
----
|
|
@SpringBootApplication
|
|
@CacheServerApplication(locators = "localhost[12345]")
|
|
class ServerApplication { ... }
|
|
----
|
|
|
|
Again, you can start as many of the `ServerApplication` classes, joined by our Locator above, as you want.
|
|
You just need to make sure the member is uniquely named.
|
|
|
|
`@LocatorApplication` is for configuring and bootstrapping standalone, {data-store-name} Locator application processes.
|
|
This process can only be a Locator and nothing else. If you try to start a Locator with a cache instance, SDG will
|
|
throw an error.
|
|
|
|
If you want to simultaneously start a cache instance along with an embedded Locator, then you should use
|
|
the `@EnableLocator` annotation instead.
|
|
|
|
Starting an embedded Locator is convenient during development. However, it is highly recommended that you run standalone
|
|
Locator processes in production for high availability. If all your Locators in the cluster go down, then the cluster
|
|
will remain intact, however, no new members will be able to join the cluster, which is important to scale-out linearly
|
|
in order to satisfy demand.
|
|
|
|
See the section on <<bootstrap-annotation-config-embedded-services-locator,Configuring an Embedded Locator>>
|
|
for more details.
|
|
|
|
[[bootstrap-annotation-config-configurers]]
|
|
== Runtime configuration using `Configurers`
|
|
|
|
Another goal when designing the annotation-based configuration model was to preserve type safety in the annotation
|
|
attributes. For example, if the configuration attribute could be expressed as an `int` (such as a port number),
|
|
then the attribute's type should be an `int`.
|
|
|
|
Unfortunately, this is not conducive to dynamic and resolvable configuration at runtime.
|
|
|
|
One of the finer features of Spring is the ability to use property placeholders and SpEL expressions
|
|
in properties or attributes of the configuration metadata when configuring beans in the Spring container.
|
|
However, this would require all annotation attributes to be of type `String`, thereby giving up type safety,
|
|
which is not desirable.
|
|
|
|
So, {sdg-name} borrows from another commonly used pattern in Spring, `Configurers`. Many different `Configurer`
|
|
interfaces are provided in Spring Web MVC, including the
|
|
{spring-framework-javadoc}/org/springframework/web/servlet/config/annotation/ContentNegotiationConfigurer.html[`org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer`].
|
|
|
|
The `Configurers` design pattern enables application developers to receive a callback to customize the configuration
|
|
of a component or bean on startup. The framework calls back to user-provided code to adjust the configuration
|
|
at runtime. One of the more common uses of this pattern is to supply conditional configuration based on
|
|
the application's runtime environment.
|
|
|
|
{sdg-name} provides several `Configurer` callback interfaces to customize different aspects of the annotation-based
|
|
configuration metadata at runtime, before the Spring managed beans that the annotations create are initialized:
|
|
|
|
* `CacheServerConfigurer`
|
|
* `ClientCacheConfigurer`
|
|
* `ContinuousQueryListenerContainerConfigurer`
|
|
* `DiskStoreConfigurer`
|
|
* `IndexConfigurer`
|
|
* `PeerCacheConfigurer`
|
|
* `PoolConfigurer`
|
|
* `RegionConfigurer`
|
|
* `GatewayReceiverConfigurer`
|
|
* `GatewaySenderConfigurer`
|
|
|
|
For example, you can use the `CacheServerConfigurer` and `ClientCacheConfigurer` to customize the port numbers
|
|
used by your Spring Boot `CacheServer` and `ClientCache` applications, respectively.
|
|
|
|
Consider the following example from a server application:
|
|
|
|
.Customizing a Spring Boot `CacheServer` application with a `CacheServerConfigurer`
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@CacheServerApplication(name = "SpringServerApplication")
|
|
class ServerApplication {
|
|
|
|
@Bean
|
|
CacheServerConfigurer cacheServerPortConfigurer(
|
|
@Value("${gemfire.cache.server.host:localhost}") String cacheServerHost
|
|
@Value("${gemfire.cache.server.port:40404}") int cacheServerPort) {
|
|
|
|
return (beanName, cacheServerFactoryBean) -> {
|
|
cacheServerFactoryBean.setBindAddress(cacheServerHost);
|
|
cacheServerFactoryBean.setHostnameForClients(cacheServerHost);
|
|
cacheServerFactoryBean.setPort(cacheServerPort);
|
|
};
|
|
}
|
|
}
|
|
----
|
|
|
|
Next, consider the following example from a client application:
|
|
|
|
.Customizing a Spring Boot `ClientCache` application with a `ClientCacheConfigurer`
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@ClientCacheApplication
|
|
class ClientApplication {
|
|
|
|
@Bean
|
|
ClientCacheConfigurer clientCachePoolPortConfigurer(
|
|
@Value("${gemfire.cache.server.host:localhost}") String cacheServerHost
|
|
@Value("${gemfire.cache.server.port:40404}") int cacheServerPort) {
|
|
|
|
return (beanName, clientCacheFactoryBean) ->
|
|
clientCacheFactoryBean.setServers(Collections.singletonList(
|
|
new ConnectionEndpoint(cacheServerHost, cacheServerPort)));
|
|
}
|
|
}
|
|
----
|
|
|
|
By using the provided `Configurers`, you can receive a callback to further customize the configuration
|
|
that is enabled by the associated annotation at runtime, during startup.
|
|
|
|
In addition, when the `Configurer` is declared as a bean in the Spring container, the bean definition can take advantage
|
|
of other Spring container features, such as property placeholders, SpEL expressions by using the `@Value` annotation
|
|
on factory method parameters, and so on.
|
|
|
|
All `Configurers` provided by {sdg-name} take two bits of information in the callback: the name of the bean created
|
|
in the Spring container by the annotation and a reference to the `FactoryBean` used by the annotation to
|
|
create and configure the {data-store-name} component (for example, a `ClientCache` instance is created
|
|
and configured with `ClientCacheFactoryBean`).
|
|
|
|
NOTE: {sdg-acronym} `FactoryBeans` are part of the {sdg-acronym} public API and are what you would use in Spring's
|
|
{spring-framework-docs}/core.html#beans-java[Java-based container configuration] if this new annotation-based
|
|
configuration model were not provided. Indeed, the annotations themselves are using these same `FactoryBeans`
|
|
for their configuration. So, in essence, the annotations are a facade providing an extra layer of abstraction
|
|
for convenience.
|
|
|
|
Given that a `Configurer` can be declared as a regular bean definition like any other POJO, you can combine different
|
|
Spring configuration options, such as the use of Spring Profiles with `Conditions` that use both property placeholders
|
|
and SpEL expressions. These and other nifty features let you create even more sophisticated and flexible configurations.
|
|
|
|
However, `Configurers` are not the only option.
|
|
|
|
[[bootstrap-annotation-config-properties]]
|
|
== Runtime configuration using `Properties`
|
|
|
|
In addition to `Configurers`, each annotation attribute in the annotation-based configuration model is associated with
|
|
a corresponding configuration property (prefixed with `spring.data.gemfire.`), which can be declared in a Spring Boot
|
|
`application.properties` file.
|
|
|
|
Building on the earlier examples, the client's `application.properties` file would define the following
|
|
set of properties:
|
|
|
|
.Client `application.properties`
|
|
[source, java]
|
|
----
|
|
spring.data.gemfire.cache.log-level=info
|
|
spring.data.gemfire.pool.Venus.servers=venus[48484]
|
|
spring.data.gemfire.pool.Venus.max-connections=200
|
|
spring.data.gemfire.pool.Venus.min-connections=50
|
|
spring.data.gemfire.pool.Venus.ping-interval=15000
|
|
spring.data.gemfire.pool.Venus.pr-single-hop-enabled=true
|
|
spring.data.gemfire.pool.Venus.read-timeout=20000
|
|
spring.data.gemfire.pool.Venus.subscription-enabled=true
|
|
spring.data.gemfire.pool.Saturn.locators=skullbox[20668]
|
|
spring.data.gemfire.pool.Saturn.subscription-enabled=true
|
|
spring.data.gemfire.pool.Neptune.servers=saturn[41414],neptune[42424]
|
|
spring.data.gemfire.pool.Neptune.min-connections=25
|
|
----
|
|
|
|
The corresponding server's `application.properties` file would define the following properties:
|
|
|
|
.Server `application.properties`
|
|
[source, java]
|
|
----
|
|
spring.data.gemfire.cache.log-level=info
|
|
spring.data.gemfire.cache.server.port=40404
|
|
spring.data.gemfire.cache.server.Venus.port=43434
|
|
spring.data.gemfire.cache.server.Saturn.port=41414
|
|
spring.data.gemfire.cache.server.Neptune.port=41414
|
|
----
|
|
|
|
Then you can simplify the `@ClientCacheApplication` class to the following:
|
|
|
|
.Spring `@ClientCacheApplication` class
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@ClientCacheApplication
|
|
@EnablePools(pools = {
|
|
@EnablePool(name = "Venus"),
|
|
@EnablePool(name = "Saturn"),
|
|
@EnablePool(name = "Neptune")
|
|
})
|
|
class ClientApplication { .. }
|
|
----
|
|
|
|
Also, the `@CacheServerApplication` class becomes the following:
|
|
|
|
.Spring `@CacheServerApplication` class
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@CacheServerApplication(name = "SpringServerApplication")
|
|
@EnableCacheServers(servers = {
|
|
@EnableCacheServer(name = "Venus"),
|
|
@EnableCacheServer(name = "Saturn"),
|
|
@EnableCacheServer(name = "Neptune")
|
|
})
|
|
class ServerApplication { .. }
|
|
----
|
|
|
|
The preceding example shows why it is important to "name" your annotation-based beans (other than because it is required
|
|
in certain cases). Doing so makes it possible to reference the bean in the Spring container from XML, properties,
|
|
and Java. It is even possible to inject annotation-defined beans into an application class, for whatever purpose,
|
|
as the following example demonstrates:
|
|
|
|
[source, java]
|
|
----
|
|
@Component
|
|
class MyApplicationComponent {
|
|
|
|
@Resource(name = "Saturn")
|
|
CacheServer saturnCacheServer;
|
|
|
|
...
|
|
}
|
|
----
|
|
|
|
Likewise, naming an annotation-defined bean lets you code a `Configurer` to customize a specific, "named" bean
|
|
since the `beanName` is 1 of 2 arguments passed to the callback.
|
|
|
|
Oftentimes, an associated annotation attribute property takes two forms: a "named" property along with
|
|
an "unnamed" property.
|
|
|
|
The following example shows such an arrangement:
|
|
|
|
[source, java]
|
|
----
|
|
spring.data.gemfire.cache.server.bind-address=10.105.20.1
|
|
spring.data.gemfire.cache.server.Venus.bind-address=10.105.20.2
|
|
spring.data.gemfire.cache.server.Saturn...
|
|
spring.data.gemfire.cache.server.Neptune...
|
|
----
|
|
|
|
While there are three named `CacheServers` above, there is also one unnamed `CacheServer` property providing
|
|
the default value for any unspecified value of that property, even for "named" `CacheServers`. So, while "Venus"
|
|
sets and overrides its own `bind-address`, "Saturn" and "Neptune" inherit from the "unnamed"
|
|
`spring.data.gemfire.cache.server.bind-address` property.
|
|
|
|
See an annotation's Javadoc for which annotation attributes support property-based configuration
|
|
and whether they support "named" properties over default, "unnamed" properties.
|
|
|
|
[[bootstrap-annotation-config-properties-of-properties]]
|
|
=== `Properties` of `Properties`
|
|
|
|
In the usual Spring fashion, you can even express `Properties` in terms of other `Properties`, whether that is by
|
|
The following example shows a nested property being set in an `application.properties` file:
|
|
|
|
.Properties of Properties
|
|
[source, java]
|
|
----
|
|
spring.data.gemfire.cache.server.port=${gemfire.cache.server.port:40404}
|
|
----
|
|
|
|
The following example shows a nested property being set in Java:
|
|
|
|
.Property placehodler nesting
|
|
[source, java]
|
|
----
|
|
@Bean
|
|
CacheServerConfigurer cacheServerPortConfigurer(
|
|
@Value("${gemfire.cache.server.port:${some.other.property:40404}}")
|
|
int cacheServerPort) {
|
|
...
|
|
}
|
|
----
|
|
|
|
TIP: Property placeholder nesting can be arbitrarily deep.
|
|
|
|
[[bootstrap-annotation-config-embedded-services]]
|
|
== Configuring Embedded Services
|
|
|
|
{data-store-name} provides the ability to start many different embedded services that are required by an application,
|
|
depending on the use case.
|
|
|
|
[[bootstrap-annotation-config-embedded-services-locator]]
|
|
=== Configuring an Embedded Locator
|
|
|
|
As mentioned previously, {data-store-name} Locators are used by clients to connect to and find servers in a cluster.
|
|
In addition, new members joining an existing cluster use Locators to find their peers.
|
|
|
|
It is often convenient for application developers as they are developing their Spring Boot and {sdg-name}
|
|
applications to startup up a small cluster of two or three {data-store-name} servers. Rather than starting
|
|
a separate Locator process, you can annotate your Spring Boot `@CacheServerApplication` class with `@EnableLocator`,
|
|
as follows:
|
|
|
|
.Spring, {data-store-name} `CacheServer` application running an embedded Locator
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@CacheServerApplication
|
|
@EnableLocator
|
|
class ServerApplication { .. }
|
|
----
|
|
|
|
The `@EnableLocator` annotation starts an embedded Locator in the Spring {data-store-name} `CacheServer` application
|
|
running on `localhost`, listening on the default Locator port, `10334`. You can customize the `host` (bind address)
|
|
and `port` that the embedded Locator binds to by using the corresponding annotation attributes.
|
|
|
|
Alternatively, you can set the `@EnableLocator` attributes by setting the corresponding
|
|
`spring.data.gemfire.locator.host` and `spring.data.gemfire.locator.port` properties in `application.properties`.
|
|
|
|
Then you can start other Spring Boot `@CacheServerApplication`-enabled applications by connecting to this
|
|
Locator with the following:
|
|
|
|
.Spring, {data-store-name} `CacheServer` application connecting to a Locator
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@CacheServerApplication(locators = "localhost[10334]")
|
|
class ServerApplication { .. }
|
|
----
|
|
|
|
You can even combine both application classes shown earlier into a single class and use your IDE to create different
|
|
run profile configurations to launch different instances of the same class with slightly modified configuration
|
|
by using Java system properties, as follows:
|
|
|
|
.Spring `CacheServer` application running an embedded Locator and connecting to the Locator
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@CacheServerApplication(locators = "localhost[10334]")
|
|
public class ServerApplication {
|
|
|
|
public static void main(String[] args) {
|
|
SpringApplication.run(ServerApplication.class);
|
|
}
|
|
|
|
@EnableLocator
|
|
@Profile("embedded-locator")
|
|
static class Configuration { }
|
|
|
|
}
|
|
----
|
|
|
|
Then, for each run profile, you can set and change the following system properties:
|
|
|
|
.IDE run profile configuration
|
|
[source, java]
|
|
----
|
|
spring.data.gemfire.name=SpringCacheServerOne
|
|
spring.data.gemfire.cache.server.port=41414
|
|
spring.profiles.active=embedded-locator
|
|
----
|
|
|
|
Only 1 of the run profiles for the `ServerApplication` class should set the `-Dspring.profiles.active=embedded-locator`
|
|
Java system property. Then you can change the `..name` and `..cache.server.port` for each of the other run profiles
|
|
and have a small cluster (distributed system) of {data-store-name} servers running on your local system.
|
|
|
|
NOTE: The `@EnableLocator` annotation was meant to be a development-time annotation only and not something
|
|
an application developer would use in production. We strongly recommend running Locators as standalone,
|
|
independent processes in the cluster.
|
|
|
|
More details on how {data-store-name} Locators work can be found
|
|
{x-data-store-docs}/topologies_and_comm/topology_concepts/how_member_discovery_works.html[here].
|
|
|
|
[[bootstrap-annotation-config-embedded-services-manager]]
|
|
=== Configuring an Embedded Manager
|
|
|
|
A {data-store-name} Manager is another peer member or node in the cluster that is responsible for cluster "management".
|
|
Management involves creating `Regions`, `Indexes`, `DiskStores`, among other things, along with monitoring the runtime
|
|
operations and behavior of the cluster components.
|
|
|
|
The Manager lets a JMX-enabled client (such as the _Gfsh_ shell tool) connect to the Manager to manage the cluster.
|
|
It is also possible to connect to a Manager with JDK-provided tools such as JConsole or JVisualVM, given that these are
|
|
both JMX-enabled clients as well.
|
|
|
|
Perhaps you would also like to enable the Spring `@CacheServerApplication` shown earlier as a Manager as well. To do so,
|
|
annotate your Spring `@Configuration` or `@SpringBootApplication` class with `@EnableManager`.
|
|
|
|
By default, the Manager binds to `localhost`, listening on the default Manager port of `1099`. Several aspects of
|
|
the Manager can be configured with annotation attributes or the corresponding properties.
|
|
|
|
The following example shows how to create an embedded Manager in Java:
|
|
|
|
.Spring `CacheServer` application running an embedded Manager
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@CacheServerApplication(locators = "localhost[10334]")
|
|
public class ServerApplication {
|
|
|
|
public static void main(String[] args) {
|
|
SpringApplication.run(ServerApplication.class);
|
|
}
|
|
|
|
@EnableLocator
|
|
@EnableManager
|
|
@Profile("embedded-locator-manager")
|
|
static class Configuration { }
|
|
|
|
}
|
|
----
|
|
|
|
With the preceding class, you can even use _Gfsh_ to connect to the small cluster and manage it, as follows:
|
|
|
|
[source, java]
|
|
----
|
|
$ gfsh
|
|
_________________________ __
|
|
/ _____/ ______/ ______/ /____/ /
|
|
/ / __/ /___ /_____ / _____ /
|
|
/ /__/ / ____/ _____/ / / / /
|
|
/______/_/ /______/_/ /_/ 1.2.1
|
|
|
|
Monitor and Manage {data-store-name}
|
|
|
|
gfsh>connect
|
|
Connecting to Locator at [host=localhost, port=10334] ..
|
|
Connecting to Manager at [host=10.99.199.5, port=1099] ..
|
|
Successfully connected to: [host=10.99.199.5, port=1099]
|
|
|
|
gfsh>list members
|
|
Name | Id
|
|
---------------------- | ----------------------------------------------------
|
|
SpringCacheServerOne | 10.99.199.5(SpringCacheServerOne:14842)<ec><v0>:1024
|
|
SpringCacheServerTwo | 10.99.199.5(SpringCacheServerTwo:14844)<v1>:1025
|
|
SpringCacheServerThree | 10.99.199.5(SpringCacheServerThree:14846)<v2>:1026
|
|
----
|
|
|
|
Because we also have the embedded Locator enabled, we can connect indirectly to the Manager through the Locator.
|
|
A Locator lets JMX clients connect and find a Manager in the cluster. If none exists, the Locator assumes the role
|
|
of a Manager. However, if no Locator exists, we would need to connect directly to the Manager by using the following:
|
|
|
|
._Gfsh_ `connect` command connecting directly to the Manager
|
|
[source, java]
|
|
----
|
|
gfsh>connect --jmx-manager=localhost[1099]
|
|
----
|
|
|
|
NOTE: Like the `@EnableLocator` annotation, the `@EnableManager` annotation is also meant to be a development-time
|
|
only annotation and not something an application developer would use in production. We strongly recommend
|
|
that Managers, like Locators, be standalone, independent and dedicated processes in the cluster.
|
|
|
|
More details on {data-store-name} management and monitoring can be found
|
|
{x-data-store-docs}/managing/book_intro.html[here].
|
|
|
|
[[bootstrap-annotation-config-embedded-services-http]]
|
|
=== Configuring the Embedded HTTP Server
|
|
|
|
{data-store-name} is also capable of running an embedded HTTP server. The current implementation is backed by
|
|
https://www.eclipse.org/jetty/[Eclipse Jetty].
|
|
|
|
The embedded HTTP server is used to host {data-store-name}'s Management (Admin) REST API (not a publicly advertised API),
|
|
the {x-data-store-docs}/rest_apps/book_intro.html[Developer REST API],
|
|
and the {x-data-store-docs}/tools_modules/pulse/pulse-overview.html[Pulse Monitoring Web Application].
|
|
|
|
However, to use any of these {data-store-name}-provided web applications, you must have a full installation of
|
|
{data-store-name} installed on your system, and you must set the `GEODE_HOME` environment variable to
|
|
your installation directory.
|
|
|
|
To enable the embedded HTTP server, add the `@EnableHttpService` annotation to any `@PeerCacheApplication`
|
|
or `@CacheServerApplication` annotated class, as follows:
|
|
|
|
.Spring `CacheServer` application running the embedded HTTP server
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@CacheServerApplication
|
|
@EnableHttpService
|
|
public class ServerApplication { .. }
|
|
----
|
|
|
|
By default, the embedded HTTP server listens on port `7070` for HTTP client requests. Of course, you can use
|
|
the annotation attributes or corresponding configuration properties to adjust the port as needed.
|
|
|
|
Follow the earlier links for more details on HTTP support and the services provided.
|
|
|
|
[[bootstrap-annotation-config-embedded-services-memcached]]
|
|
=== Configuring the Embedded Memcached Server (Gemcached)
|
|
|
|
{data-store-name} also implements the Memcached protocol with the ability to service Memcached clients. That is,
|
|
Memcached clients can connect to a {data-store-name} cluster and perform Memcached operations as if
|
|
the {data-store-name} servers in the cluster were actual Memcached servers.
|
|
|
|
To enable the embedded Memcached service, add the `@EnableMemcachedServer` annotation to any `@PeerCacheApplication`
|
|
or `@CacheServerApplication` annotated class, as follows:
|
|
|
|
.Spring `CacheServer` application running an embedded Memcached server
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@CacheServerApplication
|
|
@EnabledMemcachedServer
|
|
public class ServerApplication { .. }
|
|
----
|
|
|
|
More details on {data-store-name}'s Memcached service (called "Gemcached") can be found
|
|
{x-data-store-docs}/tools_modules/gemcached/chapter_overview.html[here].
|
|
|
|
[[bootstrap-annotation-config-logging]]
|
|
== Configuring Logging
|
|
|
|
Oftentimes, it is necessary to turn up logging in order to understand exactly what {data-store-name} is doing and when.
|
|
|
|
To enable Logging, annotate your application class with `@EnableLogging` and set the appropriate attributes
|
|
or associated properties, as follows:
|
|
|
|
.Spring `ClientCache` application with Logging enabled
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@ClientCacheApplication
|
|
@EnableLogging(logLevel="info", logFile="/absolute/file/system/path/to/application.log)
|
|
public class ClientApplication { .. }
|
|
----
|
|
|
|
While the `logLevel` attribute can be specified with all the cache-based application annotations
|
|
(for example, `@ClientCacheApplication(logLevel="info")`), it is easier to customize logging behavior with
|
|
the `@EnableLogging` annotation.
|
|
|
|
Additionally, you can configure the `log-level` by setting the `spring.data.gemfire.logging.level` property
|
|
in `application.properties`.
|
|
|
|
See the https://docs.spring.io/spring-data/gemfire/docs/current/api/org/springframework/data/gemfire/config/annotation/EnableLogging.html[`@EnableLogging` annotation Javadoc]
|
|
for more details.
|
|
|
|
[[bootstrap-annotation-config-statistics]]
|
|
== Configuring Statistics
|
|
|
|
To gain even deeper insight into {data-store-name} at runtime, you can enable statistics. Gathering statistical data
|
|
facilitates system analysis and troubleshooting when complex problems, which are often distributed in nature
|
|
and where timing is a crucial factor, occur.
|
|
|
|
When statistics are enabled, you can use {data-store-name}'s
|
|
{x-data-store-docs}/tools_modules/vsd/chapter_overview.html[VSD (Visual Statistics Display)] tool
|
|
to analyze the statistical data that is collected.
|
|
|
|
To enable statistics, annotate your application class with `@EnableStatistics`, as follows:
|
|
|
|
.Spring `ClientCache` application with Statistics enabled
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@ClientCacheApplication
|
|
@EnableStatistics
|
|
public class ClientApplication { .. }
|
|
----
|
|
|
|
Enabling statistics on a server is particularly valuable when evaluating performance. To do so,
|
|
annotate your `@PeerCacheApplication` or `@CacheServerApplication` class with `@EnableStatistics`.
|
|
|
|
You can use the `@EnableStatistics` annotation attributes or associated properties to customize
|
|
the statistics gathering and collection process.
|
|
|
|
See the https://docs.spring.io/spring-data/gemfire/docs/current/api/org/springframework/data/gemfire/config/annotation/EnableStatistics.html[`@EnableStatistics` annotation Javadoc]
|
|
for more details.
|
|
|
|
More details on {data-store-name}'s statistics can be found
|
|
{x-data-store-docs}/managing/statistics/chapter_overview.html[here].
|
|
|
|
[[bootstrap-annotation-config-pdx]]
|
|
== Configuring PDX
|
|
|
|
One of the more powerful features of {data-store-name} is
|
|
{x-data-store-docs}/developing/data_serialization/gemfire_pdx_serialization.html[PDX serialization].
|
|
While a complete discussion of PDX is beyond the scope of this document, serialization using PDX is a much better
|
|
alternative to Java serialization, with the following benefits:
|
|
|
|
* PDX uses a centralized type registry to keep the serialized bytes of an object more compact.
|
|
* PDX is a neutral serialization format, allowing both Java and Native clients to operate on the same data set.
|
|
* PDX supports versioning and lets object fields be added or removed without affecting existing applications
|
|
using either older or newer versions of the PDX serialized objects that have changed, without data loss.
|
|
* PDX lets object fields be accessed individually in OQL query projections and predicates without the object
|
|
needing to be de-serialized first.
|
|
|
|
In general, serialization in {data-store-name} is required any time data is transferred to or from clients and servers
|
|
or between peers in a cluster during normal distribution and replication processes as well as when data is overflowed
|
|
or persisted to disk.
|
|
|
|
Enabling PDX serialization is much simpler than modifying all of your application domain object types to implement
|
|
`java.io.Serializable`, especially when it may be undesirable to impose such restrictions on your
|
|
application domain model or you do not have any control over the objects your are serializing, which is especially
|
|
true when using a 3rd party library (e.g. think of a geo-spatial API with `Coordinate` types).
|
|
|
|
To enable PDX, annotate your application class with `@EnablePdx`, as follows:
|
|
|
|
.Spring `ClientCache` application with PDX enabled
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@ClientCacheApplication
|
|
@EnablePdx
|
|
public class ClientApplication { .. }
|
|
----
|
|
|
|
Typically, an application's domain object types either implements the
|
|
{x-data-store-javadoc}/org/apache/geode/pdx/PdxSerializable.html[`org.apache.geode.pdx.PdxSerializable`]
|
|
interface or you can implement and register a non-invasive implementation of the
|
|
{x-data-store-javadoc}/org/apache/geode/pdx/PdxSerializer.html[`org.apache.geode.pdx.PdxSerializer`]
|
|
interface to handle all the application domain object types that need to be serialized.
|
|
|
|
Unfortunately, {data-store-name} only lets one `PdxSerializer` be registered, which suggests that all application
|
|
domain object types need to be handled by a single `PdxSerializer` instance. However, that is a serious anti-pattern
|
|
and an unmaintainable practice.
|
|
|
|
Even though only a single `PdxSerializer` instance can be registered with {data-store-name}, it makes sense to create a
|
|
single `PdxSerializer` implementation per application domain object type.
|
|
|
|
By using the https://en.wikipedia.org/wiki/Composite_pattern[Composite Software Design Pattern], you can provide
|
|
an implementation of the `PdxSerializer` interface that aggregates all of the application domain object type-specific
|
|
`PdxSerializer` instances, but acts as a single `PdxSerializer` instance and register it.
|
|
|
|
You can declare this composite `PdxSerializer` as a managed bean in the Spring container and refer to this composite
|
|
`PdxSerializer` by its bean name in the `@EnablePdx` annotation using the `serializerBeanName` attribute. {sdg-name}
|
|
takes care of registering it with {data-store-name} on your behalf.
|
|
|
|
The following example shows how to create a custom composite `PdxSerializer`:
|
|
|
|
.Spring `ClientCache` application with PDX enabled, using a custom composite `PdxSerializer`
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@ClientCacheApplication
|
|
@EnablePdx(serializerBeanName = "compositePdxSerializer")
|
|
public class ClientApplication {
|
|
|
|
@Bean
|
|
PdxSerializer compositePdxSerializer() {
|
|
return new CompositePdxSerializerBuilder()...
|
|
}
|
|
}
|
|
----
|
|
|
|
It is also possible to declare {data-store-name}'s
|
|
{x-data-store-javadoc}/org/apache/geode/pdx/ReflectionBasedAutoSerializer.html[`org.apache.geode.pdx.ReflectionBasedAutoSerializer`]
|
|
as a bean definition in a Spring context.
|
|
|
|
Alternatively, you should use {sdg-name}'s more robust
|
|
{sdg-javadoc}/org/springframework/data/gemfire/mapping/MappingPdxSerializer.html[`org.springframework.data.gemfire.mapping.MappingPdxSerializer`],
|
|
which uses Spring Data mapping metadata and infrastructure applied to the serialization process for more efficient
|
|
handling than reflection alone.
|
|
|
|
Many other aspects and features of PDX can be adjusted with the `@EnablePdx` annotation attributes
|
|
or associated configuration properties.
|
|
|
|
See the https://docs.spring.io/spring-data/gemfire/docs/current/api/org/springframework/data/gemfire/config/annotation/EnablePdx.html[`@EnablePdx` annotation Javadoc]
|
|
for more details.
|
|
|
|
[[bootstrap-annotation-config-gemfire-properties]]
|
|
== Configuring {data-store-name} Properties
|
|
|
|
While many of the {x-data-store-docs}/reference/topics/gemfire_properties.html[gemfire.properties]
|
|
are conveniently encapsulated in and abstracted with an annotation in the {sdg-acronym} annotation-based
|
|
configuration model, the less commonly used {data-store-name} properties are still accessible from
|
|
the `@EnableGemFireProperties` annotation.
|
|
|
|
Annotating your application class with `@EnableGemFireProperties` is convenient and a nice alternative to creating
|
|
a `gemfire.properties` file or setting {data-store-name} properties as Java system properties on the command line
|
|
when launching your application.
|
|
|
|
TIP: We recommend that these {data-store-name} properties be set in a `gemfire.properties` file when deploying
|
|
your application to production. However, at development time, it can be convenient to set these properties individually,
|
|
as needed, for prototyping, debugging and testing purposes.
|
|
|
|
A few examples of some of the less common {data-store-name} properties that you usually need not worry about include,
|
|
but are not limited to: `ack-wait-threshold`, `disable-tcp`, `socket-buffer-size`, and others.
|
|
|
|
To individually set any {data-store-name} property, annotate your application class with `@EnableGemFireProperties`
|
|
and set the {data-store-name} properties you want to change from the default value set by {data-store-name}
|
|
with the corresponding attribute, as follows:
|
|
|
|
.Spring `ClientCache` application with specific {data-store-name} properties set
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@ClientCacheApplication
|
|
@EnableGemFireProperties(conflateEvents = true, socketBufferSize = 16384)
|
|
public class ClientApplication { .. }
|
|
----
|
|
|
|
Keep in mind that some of the {data-store-name} properties are client-specific (for example, `conflateEvents`),
|
|
while others are server-specific (for example `distributedSystemId`, `enableNetworkPartitionDetection`,
|
|
`enforceUniqueHost`, `memberTimeout`, `redundancyZone`, and others).
|
|
|
|
More details on {data-store-name} properties can be found
|
|
{x-data-store-docs}/reference/topics/gemfire_properties.html[here].
|
|
|
|
[[bootstrap-annotation-config-regions]]
|
|
== Configuring Regions
|
|
|
|
So far, outside of PDX, our discussion has centered around configuring {data-store-name}'s more administrative functions:
|
|
creating a cache instance, starting embedded services, enabling logging and statistics, configuring PDX, and using
|
|
`gemfire.properties` to affect low-level configuration and behavior. While all these configuration options are important,
|
|
none of them relate directly to your application. In other words, we still need some place to store our application data
|
|
and make it generally available and accessible.
|
|
|
|
{data-store-name} organizes data in a cache into {x-data-store-docs}/basic_config/data_regions/chapter_overview.html[Regions].
|
|
You can think of a Region as a table in a relational database. Generally, a Region should only store a single type of object,
|
|
which makes it more conducive for building effective indexes and writing queries. We cover indexing
|
|
<<bootstrap-annotation-config-indexes,later>>.
|
|
|
|
Previously, {sdg-name} users needed to explicitly define and declare the Regions used by their applications to store data
|
|
by writing very verbose Spring configuration metadata, whether using {sdg-acronym}'s `FactoryBeans` from the API
|
|
with Spring's {spring-framework-docs}/core.html#beans-java[Java-based container configuration]
|
|
or using <<bootstrap:region, XML>>.
|
|
|
|
The following example demonstrates how to configure a Region bean in Java:
|
|
|
|
.Example Region bean definition using Spring's Java-based container configuration
|
|
[source, java]
|
|
----
|
|
@Configuration
|
|
class GemFireConfiguration {
|
|
|
|
@Bean("Example")
|
|
PartitionedRegionFactoryBean exampleRegion(GemFireCache gemfireCache) {
|
|
|
|
PartitionedRegionFactoryBean<Long, Example> exampleRegion =
|
|
new PartitionedRegionFactoryBean<>();
|
|
|
|
exampleRegion.setCache(gemfireCache);
|
|
exampleRegion.setClose(false);
|
|
exampleRegion.setPersistent(true);
|
|
|
|
return exampleRegion;
|
|
}
|
|
|
|
...
|
|
}
|
|
----
|
|
|
|
The following example demonstrates how to configure the same Region bean in XML:
|
|
|
|
.Example Region bean definition using {sdg-acronym}'s XML Namespace
|
|
[source, xml]
|
|
----
|
|
<gfe:partitioned-region id="exampleRegion" name="Example" persistent="true">
|
|
...
|
|
</gfe:partitioned-region>
|
|
----
|
|
|
|
While neither Java nor XML configuration is all that difficult to specify, either one can be cumbersome, especially if
|
|
an application requires a large number of Regions. Many relational database-based applications can have hundreds
|
|
or even thousands of tables.
|
|
|
|
Defining and declaring all these Regions by hand would be cumbersome and error prone. Well, now there is a better way.
|
|
|
|
Now you can define and configure Regions based on their application domain objects (entities) themselves. No longer do
|
|
you need to explicitly define `Region` bean definitions in Spring configuration metadata, unless you require
|
|
finer-grained control.
|
|
|
|
To simplify Region creation, {sdg-name} combines the use of Spring Data Repositories with the expressive
|
|
power of annotation-based configuration using the new `@EnableEntityDefinedRegions` annotation.
|
|
|
|
NOTE: Most Spring Data application developers should already be familiar with the
|
|
{spring-data-commons-docs-html}/#repositories[Spring Data Repository abstraction]
|
|
and {sdg-name}'s <<gemfire-repositories,implementation/extension>>,
|
|
which has been specifically customized to optimize data access operations for {data-store-name}.
|
|
|
|
First, an application developer starts by defining the application's domain objects (entities), as follows:
|
|
|
|
.Application domain object type modeling a Book
|
|
[source, java]
|
|
----
|
|
@Region("Books")
|
|
class Book {
|
|
|
|
@Id
|
|
private ISBN isbn;
|
|
|
|
private Author author;
|
|
|
|
private Category category;
|
|
|
|
private LocalDate releaseDate;
|
|
|
|
private Publisher publisher;
|
|
|
|
private String title;
|
|
|
|
}
|
|
----
|
|
|
|
Next, you define a basic repository for `Books` by extending Spring Data Commons
|
|
`org.springframework.data.repository.CrudRepository` interface, as follows:
|
|
|
|
.Repository for Books
|
|
[source, java]
|
|
----
|
|
interface BookRepository extends CrudRepository<Book, ISBN> { .. }
|
|
----
|
|
|
|
The `org.springframe.data.repository.CrudRepository` is a Data Access Object (DAO) providing basic data access
|
|
operations (CRUD) along with support for simple queries (such as `findById(..)`). You can define additional,
|
|
more sophisticated queries by declaring query methods on the repository interface
|
|
(for example, `List<BooK> findByAuthor(Author author);`).
|
|
|
|
Under the hood, {sdg-name} provides an implementation of your application's repository interfaces when
|
|
the Spring container is bootstrapped. {sdg-acronym} even implements the query methods you define so long as you follow
|
|
the <<gemfire-repositories.executing-queries,conventions>>.
|
|
|
|
Now, when you defined the `Book` class, you also specified the Region in which instances of `Book` are mapped (stored)
|
|
by declaring the {sdg-name} mapping annotation, `@Region` on the entity's type. Of course, if the entity type (`Book`,
|
|
in this case) referenced in the type parameter of the repository interface (`BookRepository`, in this case)
|
|
is not annotated with `@Region`, the name is derived from the simple class name of the entity type (also `Book`,
|
|
in this case).
|
|
|
|
{sdg-name} uses the mapping context, which contains mapping metadata for all the entities defined in your application,
|
|
to determine all the Regions that are needed at runtime.
|
|
|
|
To enable and use this feature, annotate the application class with `@EnableEntityDefinedRegions`, as follows:
|
|
|
|
.Entity-defined Region Configuration
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@ClientCacheApplication
|
|
@EnableEntityDefinedRegions(basePackages = "example.app.domain")
|
|
@EnableGemfireRepositories(basePackages = "example.app.repo")
|
|
class ClientApplication { .. }
|
|
----
|
|
|
|
TIP: Creating Regions from entity classes is most useful when using Spring Data Repositories in your application.
|
|
{sdg-name}'s Repository support is enabled with the `@EnableGemfireRepositories` annotation, as shown in
|
|
the preceding example.
|
|
|
|
NOTE: Currently, only entity classes explicitly annotated with `@Region` are picked up by the scan
|
|
and will have Regions created. If an entity class is not explicitly mapped with `@Region` no Region will be created.
|
|
|
|
By default, the `@EnableEntityDefinedRegions` annotation scans for entity classes recursively, starting from
|
|
the package of the configuration class on which the `@EnableEntityDefinedRegions` annotation is declared.
|
|
|
|
However, it is common to limit the search during the scan by setting the `basePackages` attribute with
|
|
the package names containing your application entity classes.
|
|
|
|
Alternatively, you can use the more type-safe `basePackageClasses` attribute for specifying the package to scan
|
|
by setting the attribute to an entity type in the package that contains the entity's class, or by using a non-entity
|
|
placeholder class specifically created for identifying the package to scan.
|
|
|
|
The following example shows how to specify the entity types to scan:
|
|
|
|
.Entity-defined Region Configuration using the Entity class type
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@ClientCacheApplication
|
|
@EnableGemfireRepositories
|
|
@EnableEntityDefinedRegions(basePackageClasses = {
|
|
example.app.books.domain.Book.class,
|
|
example.app.customers.domain.Customer.class
|
|
})
|
|
class ClientApplication { .. }
|
|
----
|
|
|
|
In addition to specifying where to begin the scan, like Spring's `@ComponentScan` annotation, you can specify `include`
|
|
and `exclude` filters with all the same semantics of the `org.springframework.context.annotation.ComponentScan.Filter`
|
|
annotation.
|
|
|
|
See the https://docs.spring.io/spring-data/gemfire/docs/current/api/org/springframework/data/gemfire/config/annotation/EnableEntityDefinedRegions.html[`@EnableEntityDefinedRegions` annotation Javadoc]
|
|
for more details.
|
|
|
|
[[bootstrap-annotation-config-region-types]]
|
|
=== Configuring Type-specific Regions
|
|
|
|
{data-store-name} supports many different {x-data-store-docs}/developing/region_options/region_types.html[types of Regions].
|
|
Each type corresponds to the Region's {x-data-store-javadoc}/org/apache/geode/cache/DataPolicy.html[`DataPolicy`],
|
|
which determines exactly how the data in the Region will be managed (i.e. distributed, replicated, and so on).
|
|
|
|
NOTE: Other configuration settings (such as the Region's `scope`) can also affect how data is managed.
|
|
See {x-data-store-docs}/developing/region_options/storage_distribution_options.html["`Storage and Distribution Options`"]
|
|
in the {data-store-name} User Guide for more details.
|
|
|
|
When you annotate your application domain object types with the generic `@Region` mapping annotation, {sdg-name} decides
|
|
which type of Region to create. {sdg-acronym}'s default strategy takes the cache type into consideration when
|
|
determining the type of Region to create.
|
|
|
|
For example, if you declare the application as a `ClientCache` by using the `@ClientCacheApplication` annotation,
|
|
{sdg-acronym} creates a client `PROXY` `Region` by default. Alternatively, if you declare the application as a
|
|
peer `Cache` by using either the `@PeerCacheApplication` or `@CacheServerApplication` annotations,
|
|
{sdg-acronym} creates a server `PARTITION` `Region` by default.
|
|
|
|
Of course, you can always override the default when necessary. To override the default applied by {sdg-name},
|
|
four new Region mapping annotations have been introduced:
|
|
|
|
* `@ClientRegion`
|
|
* `@LocalRegion`
|
|
* `@PartitionRegion`
|
|
* `@ReplicateRegion`
|
|
|
|
The `@ClientRegion` mapping annotation is specific to client applications. All of the other Region mapping annotations
|
|
listed above can only be used in server applications that have an embedded peer `Cache`.
|
|
|
|
It is sometimes necessary for client applications to create and use local-only Regions, perhaps to aggregate data
|
|
from other Regions in order to analyze the data locally and carry out some function performed by the application
|
|
on the user's behalf. In this case, the data does not need to be distributed back to the server unless other
|
|
applications need access to the results. This Region might even be temporary and discarded after use, which could be
|
|
accomplished with Idle-Timeout (TTI) and Time-To-Live (TTL) expiration policies on the Region itself.
|
|
(See "`<<bootstrap-annotation-config-region-expiration>>`" for more on expiration policies.)
|
|
|
|
NOTE: Region-level Idle-Timeout (TTI) and Time-To-Live (TTL) expiration policies are independent of and different from
|
|
entry-level TTI and TTL expiration policies.
|
|
|
|
In any case, if you want to create a local-only client Region where the data is not going to be distributed back to
|
|
a corresponding Region on the server with the same name, you can declare the `@ClientRegion` mapping annotation
|
|
and set the `shortcut` attribute to `ClientRegionShortcut.LOCAL`, as follows:
|
|
|
|
.Spring `ClientCache` application with a local-only, client Region
|
|
[source, java]
|
|
----
|
|
@ClientRegion(shortcut = ClientRegionShortcut.LOCAL)
|
|
class ClientLocalEntityType { .. }
|
|
----
|
|
|
|
All Region type-specific annotations provide additional attributes that are both common across Region types
|
|
as well as specific to only that type of Region. For example, the `collocatedWith` and `redundantCopies` attributes
|
|
in the `PartitionRegion` annotation apply to server-side, `PARTITION` Regions only.
|
|
|
|
More details on {data-store-name} Region types can be found
|
|
{x-data-store-docs}/developing/region_options/region_types.html[here].
|
|
|
|
[[bootstrap-annotation-config-region-cluster-defined]]
|
|
=== Configured Cluster-defined Regions
|
|
|
|
In addition to the `@EnableEntityDefinedRegions` annotation, {sdg-name} also provides the inverse annotation,
|
|
`@EnableClusterDefinedRegions`. Rather than basing your Regions on the entity classes defined and driven from
|
|
your application use cases (UC) and requirements (the most common and logical approach), alternatively, you can
|
|
declare your Regions from the Regions already defined in the cluster to which your `ClientCache` application
|
|
will connect.
|
|
|
|
This allows you to centralize your configuration using the cluster of servers as the primary source of data definitions
|
|
and ensure that all client applications of the cluster have a consistent configuration. This is particularly useful
|
|
when quickly scaling up a large number instances of the same client application to handle the increased load
|
|
in a cloud-managed environment.
|
|
|
|
The idea is, rather than the client application(s) driving the data dictionary, the user defines Regions
|
|
using {data-store-name}'s _Gfsh_ CLI shell tool. This has the added advantage that when additional peers are added
|
|
to the cluster, they too will also have and share the same configuration since it is remembered by {data-store-name}'s
|
|
_Cluster Configuration Service_.
|
|
|
|
By way of example, a user might defined a Region in _Gfsh_, as follows:
|
|
|
|
.Defining a Region with Gfsh
|
|
[source,txt]
|
|
----
|
|
gfsh>create region --name=Books --type=PARTITION
|
|
Member | Status
|
|
--------- | --------------------------------------
|
|
ServerOne | Region "/Books" created on "ServerOne"
|
|
ServerTwo | Region "/Books" created on "ServerTwo"
|
|
|
|
gfsh>list regions
|
|
List of regions
|
|
---------------
|
|
Books
|
|
|
|
gfsh>describe region --name=/Books
|
|
..........................................................
|
|
Name : Books
|
|
Data Policy : partition
|
|
Hosting Members : ServerTwo
|
|
ServerOne
|
|
|
|
Non-Default Attributes Shared By Hosting Members
|
|
|
|
Type | Name | Value
|
|
------ | ----------- | ---------
|
|
Region | size | 0
|
|
| data-policy | PARTITION
|
|
----
|
|
|
|
With {data-store-name}'s _Cluster Configuration Service_, any additional peer members added to the cluster of servers
|
|
to handle the increased load (on the backend) will also have the same configuration, for example:
|
|
|
|
.Adding an additional peer member to the cluster
|
|
[source,txt]
|
|
----
|
|
gfsh>list members
|
|
Name | Id
|
|
--------- | ----------------------------------------------
|
|
Locator | 10.0.0.121(Locator:68173:locator)<ec><v0>:1024
|
|
ServerOne | 10.0.0.121(ServerOne:68242)<v3>:1025
|
|
ServerTwo | 10.0.0.121(ServerTwo:68372)<v4>:1026
|
|
|
|
gfsh>start server --name=ServerThree --log-level=config --server-port=41414
|
|
Starting a Geode Server in /Users/you/geode/cluster/ServerThree...
|
|
...
|
|
Server in /Users/you/geode/cluster/ServerThree... on 10.0.0.121[41414] as ServerThree is currently online.
|
|
Process ID: 68467
|
|
Uptime: 3 seconds
|
|
Geode Version: 1.2.1
|
|
Java Version: 1.8.0_152
|
|
Log File: /Users/you/geode/cluster/ServerThree/ServerThree.log
|
|
JVM Arguments: -Dgemfire.default.locators=10.0.0.121[10334]
|
|
-Dgemfire.use-cluster-configuration=true
|
|
-Dgemfire.start-dev-rest-api=false
|
|
-Dgemfire.log-level=config
|
|
-XX:OnOutOfMemoryError=kill -KILL %p
|
|
-Dgemfire.launcher.registerSignalHandlers=true
|
|
-Djava.awt.headless=true
|
|
-Dsun.rmi.dgc.server.gcInterval=9223372036854775806
|
|
Class-Path: /Users/you/geode/cluster/apache-geode-1.2.1/lib/geode-core-1.2.1.jar
|
|
:/Users/you/geode/cluster/apache-geode-1.2.1/lib/geode-dependencies.jar
|
|
|
|
gfsh>list members
|
|
Name | Id
|
|
----------- | ----------------------------------------------
|
|
Locator | 10.0.0.121(Locator:68173:locator)<ec><v0>:1024
|
|
ServerOne | 10.0.0.121(ServerOne:68242)<v3>:1025
|
|
ServerTwo | 10.0.0.121(ServerTwo:68372)<v4>:1026
|
|
ServerThree | 10.0.0.121(ServerThree:68467)<v5>:1027
|
|
|
|
gfsh>describe member --name=ServerThree
|
|
Name : ServerThree
|
|
Id : 10.0.0.121(ServerThree:68467)<v5>:1027
|
|
Host : 10.0.0.121
|
|
Regions : Books
|
|
PID : 68467
|
|
Groups :
|
|
Used Heap : 37M
|
|
Max Heap : 3641M
|
|
Working Dir : /Users/you/geode/cluster/ServerThree
|
|
Log file : /Users/you/geode/cluster/ServerThree/ServerThree.log
|
|
Locators : 10.0.0.121[10334]
|
|
|
|
Cache Server Information
|
|
Server Bind :
|
|
Server Port : 41414
|
|
Running : true
|
|
Client Connections : 0
|
|
----
|
|
|
|
As you can see, "ServerThree" now has the "Books" Region. If the any or all of the server go down, they will have
|
|
the same configuration along with the "Books" Region when they come back up.
|
|
|
|
On the client-side, many Book Store client application instances might be started to process books against
|
|
the Book Store online service. The "Books" Region might be 1 of many different Regions needed to implement
|
|
the Book Store application service. Rather than have to create and configure each Region individually, {sdg-acronym}
|
|
conveniently allows the client application Regions to be defined from the cluster, as follows:
|
|
|
|
.Defining Client Regions from the Cluster with `@EnableClusterDefinedRegions`
|
|
[source,java]
|
|
----
|
|
@ClientCacheApplication
|
|
@EnableClusterDefinedRegions
|
|
class BookStoreClientApplication {
|
|
|
|
public static void main(String[] args) {
|
|
....
|
|
}
|
|
|
|
...
|
|
}
|
|
----
|
|
|
|
NOTE: `@EnableClusterDefinedRegions` can only used on the client.
|
|
|
|
TIP: You can use the `clientRegionShortcut` annotation attribute to control the type of Region created on the client.
|
|
By default, a client `PROXY` Region is created. Set `clientRegionShortcut` to `ClientRegionShortcut.CACHING_PROXY`
|
|
to implement "_near caching_". This setting applies to all client Regions created from Cluster-defined Regions.
|
|
If you want to control individual settings (like data policy) of the client Regions created from Regions defined
|
|
on the Cluster, then you can implement a
|
|
{sdg-javadoc}/org/springframework/data/gemfire/config/annotation/RegionConfigurer.html[`RegionConfigurer`]
|
|
with custom logic based on the Region name.
|
|
|
|
Then, it becomes a simple matter to use the "Books" Region in your application. You can inject the "Books" Region
|
|
directly, as follows:
|
|
|
|
.Using the "Books" Region
|
|
[source,java]
|
|
----
|
|
@org.springframework.stereotype.Repository
|
|
class BooksDataAccessObject {
|
|
|
|
@Resource(name = "Books")
|
|
private Region<ISBN, Book> books;
|
|
|
|
// implement CRUD and queries with the "Books" Region
|
|
}
|
|
----
|
|
|
|
Or, even define a Spring Data Repository definition based on the application domain type (entity), `Book`,
|
|
mapped to the "Books" Region, as follows:
|
|
|
|
.Using the "Books" Region with a SD Repository
|
|
[source,java]
|
|
----
|
|
interface BookRepository extends CrudRepository<Book, ISBN> {
|
|
...
|
|
}
|
|
----
|
|
|
|
You can then either inject your custom `BooksDataAccessObject` or the `BookRepository` into your application service
|
|
components to carry out whatever business function required.
|
|
|
|
[[bootstrap-annotation-config-region-eviction]]
|
|
=== Configuring Eviction
|
|
|
|
Managing data with {data-store-name} is an active task. Tuning is generally required, and you must employ a combination
|
|
of features (for example, both eviction and <<bootstrap-annotation-config-region-expiration, expiration>>)
|
|
to effectively manage your data in memory with {data-store-name}.
|
|
|
|
Given that {data-store-name} is an In-Memory Data Grid (IMDG), data is managed in-memory and distributed to other nodes
|
|
that participate in a cluster in order to minimize latency, maximize throughput and ensure that data is highly available.
|
|
Since not all of an application's data is going to typically fit in memory (even across an entire cluster of nodes,
|
|
much less on a single node), you can increase capacity by adding new nodes to the cluster. This is commonly referred to
|
|
as linear scale-out (rather than scaling up, which means adding more memory, more CPU, more disk,
|
|
or more network bandwidth -- basically more of every system resource in order to handle the load).
|
|
|
|
Still, even with a cluster of nodes, it is usually imperative that only the most important data be kept in memory.
|
|
Running out of memory, or even venturing near full capacity, is rarely, if ever, a good thing. Stop-the-world GCs
|
|
or worse, `OutOfMemoryErrors`, will bring your application to a screaming halt.
|
|
|
|
So, to help manage memory and keep the most important data around, {data-store-name} supports Least Recently Used (LRU)
|
|
eviction. That is, {data-store-name} evicts Region entries based on when those entries were last accessed by using
|
|
the Least Recently Used algorithm.
|
|
|
|
To enable eviction, annotate the application class with `@EnableEviction`, as follows:
|
|
|
|
.Spring application with eviction enabled
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@PeerCacheApplication
|
|
@EnableEviction(policies = {
|
|
@EvictionPolicy(regionNames = "Books", action = EvictionActionType.INVALIDATE),
|
|
@EvictionPolicy(regionNames = { "Customers", "Orders" }, maximum = 90,
|
|
action = EvictionActionType.OVERFLOW_TO_DISK,
|
|
type = EvictonPolicyType.HEAP_PERCENTAGE)
|
|
})
|
|
class ServerApplication { .. }
|
|
----
|
|
|
|
Eviction policies are usually set on the Regions in the servers.
|
|
|
|
As shown earlier, the `policies` attribute can specify one or more nested `@EvictionPolicy` annotations, with each one
|
|
being individually catered to one or more Regions where the eviction policy needs to be applied.
|
|
|
|
Additionally, you can reference a custom implementation of {data-store-name}'s
|
|
{x-data-store-javadoc}/org/apache/geode/cache/util/ObjectSizer.html[`org.apache.geode.cache.util.ObjectSizer`] interface,
|
|
which can be defined as a bean in the Spring container and referenced by name by using the `objectSizerName` attribute.
|
|
|
|
An `ObjectSizer` lets you define the criteria used to evaluate and determine the the size of objects stored in a Region.
|
|
|
|
See the https://docs.spring.io/spring-data/gemfire/docs/current/api/org/springframework/data/gemfire/config/annotation/EnableEviction.html[`@EnableEviction` annotation Javadoc]
|
|
for a complete list of eviction configuration options.
|
|
|
|
More details on {data-store-name} eviction can be found
|
|
{x-data-store-docs}/developing/eviction/chapter_overview.html[here].
|
|
|
|
[[bootstrap-annotation-config-region-expiration]]
|
|
=== Configuring Expiration
|
|
|
|
Along with <<bootstrap-annotation-config-region-eviction, eviction>>, expiration can also be used to manage memory
|
|
by allowing entries stored in a Region to expire. {data-store-name} supports both Time-to-Live (TTL)
|
|
and Idle-Timeout (TTI) entry expiration policies.
|
|
|
|
{sdg-name}'s annotation-based expiration configuration is based on the
|
|
<<bootstrap:region:expiration:annotation, earlier and existing entry expiration annotation support>>
|
|
added in {sdg-name} version 1.5.
|
|
|
|
Essentially, {sdg-name}'s expiration annotation support is based on a custom implementation of {data-store-name}'s
|
|
{x-data-store-javadoc}/org/apache/geode/cache/CustomExpiry.html[`org.apache.geode.cache.CustomExpiry`] interface.
|
|
This `o.a.g.cache.CustomExpiry` implementation inspects the user's application domain objects stored in a Region
|
|
for the presence of type-level expiration annotations.
|
|
|
|
{sdg-name} provides the following expiration annotations:
|
|
|
|
* `Expiration`
|
|
* `IdleTimeoutExpiration`
|
|
* `TimeToLiveExpiration`
|
|
|
|
An application domain object type can be annotated with one or more of the expiration annotations, as follows:
|
|
|
|
.Applicaton domain object specific expiration policy
|
|
[source, java]
|
|
----
|
|
@Region("Books")
|
|
@TimeToLiveExpiration(timeout = 30000, action = "INVALIDATE")
|
|
class Book { .. }
|
|
----
|
|
|
|
To enable expiration, annotate the application class with `@EnableExpiration`, as follows:
|
|
|
|
.Spring application with expiration enabled
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@PeerCacheApplication
|
|
@EnableExpiration
|
|
class ServerApplication { .. }
|
|
----
|
|
|
|
In addition to application domain object type-level expiration policies, you can directly and individually configure
|
|
expiration policies on a Region by Region basis using the `@EnableExpiration` annotation, as follows:
|
|
|
|
.Spring application with region-specific expiration policies
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@PeerCacheApplication
|
|
@EnableExpiration(policies = {
|
|
@ExpirationPolicy(regionNames = "Books", types = ExpirationType.TIME_TO_LIVE),
|
|
@ExpirationPolicy(regionNames = { "Customers", "Orders" }, timeout = 30000,
|
|
action = ExpirationActionType.LOCAL_DESTROY)
|
|
})
|
|
class ServerApplication { .. }
|
|
----
|
|
|
|
The preceding example sets expiration policies for the `Books`, `Customers`, and `Orders` Regions.
|
|
|
|
Expiration policies are usually set on the Regions in the servers.
|
|
|
|
See the https://docs.spring.io/spring-data/gemfire/docs/current/api/org/springframework/data/gemfire/config/annotation/EnableExpiration.html[`@EnableExpiration` annotation Javadoc]
|
|
for a complete list of expiration configuration options.
|
|
|
|
More details on {data-store-name} expiration can be found
|
|
{x-data-store-docs}/developing/expiration/chapter_overview.html[here].
|
|
|
|
[[bootstrap-annotation-config-region-compression]]
|
|
=== Configuring Compression
|
|
|
|
In addition to <<bootstrap-annotation-config-region-expiration,eviction>>
|
|
and <<bootstrap-annotation-config-region-expiration,expiration>>, you can also configure your data Regions
|
|
with compression to reduce memory consumption.
|
|
|
|
{data-store-name} lets you compress in memory Region values by using pluggable
|
|
{x-data-store-javadoc}/org/apache/geode/compression/Compressor.html[`Compressors`], or different compression codecs.
|
|
{data-store-name} uses Google's https://google.github.io/snappy/[Snappy] compression library by default.
|
|
|
|
To enable compression, annotate the application class with `@EnableCompression`, as follows:
|
|
|
|
.Spring application with Region compression enabled
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@ClientCacheApplication
|
|
@EnableCompression(compressorBeanName = "MyCompressor", regionNames = { "Customers", "Orders" })
|
|
class ClientApplication { .. }
|
|
----
|
|
|
|
NOTE: Neither the `compressorBeanName` nor the `regionNames` attributes are required.
|
|
|
|
The `compressorBeanName` defaults to `SnappyCompressor`, enabling {data-store-name}'s
|
|
{x-data-store-javadoc}/org/apache/geode/compression/SnappyCompressor.html[`SnappyCompressor`].
|
|
|
|
The `regionNames` attribute is an array of Region names that specify the Regions that have compression enabled.
|
|
By default, all Regions compress values if the `regionNames` attribute is not explicitly set.
|
|
|
|
TIP: Alternatively, you can use the `spring.data.gemfire.cache.compression.compressor-bean-name`
|
|
and `spring.data.gemfire.cache.compression.region-names` properties in the `application.properties` file
|
|
to set and configure the values of these `@EnableCompression` annotation attributes.
|
|
|
|
WARNING: To use {data-store-name}'s Region compression feature, you must include the `org.iq80.snappy:snappy` dependency
|
|
in your application's `pom.xml` file (for Maven) or `build.gradle` file (for Gradle). This is necessary only if you use
|
|
{data-store-name}'s default support for Region compression, which uses the
|
|
{x-data-store-javadoc}/org/apache/geode/compression/SnappyCompressor.html[`SnappyCompressor`] by default.
|
|
Of course, if you use another compression library, you need to include dependencies for that compression library
|
|
on your application's classpath. Additionally, you need to implement {data-store-name}'s
|
|
{x-data-store-javadoc}/org/apache/geode/compression/Compressor.html[`Compressor`] interface to adapt your compression
|
|
library of choice, define it as a bean in the Spring compressor, and set the `compressorBeanName`
|
|
to this custom bean definition.
|
|
|
|
See the https://docs.spring.io/spring-data/gemfire/docs/current/api/org/springframework/data/gemfire/config/annotation/EnableCompression.html[`@EnableCompression` annotation Javadoc]
|
|
for more details.
|
|
|
|
More details on {data-store-name} compression can be found
|
|
https://gemfire91.docs.pivotal.io/geode/managing/region_compression.html[here].
|
|
|
|
[[bootstrap-annotation-config-region-off-heap]]
|
|
=== Configuring Off-Heap Memory
|
|
|
|
Another effective means for reducing pressure on the JVM's Heap memory and minimizing GC activity is to use
|
|
{data-store-name}'s off-heap memory support.
|
|
|
|
Rather than storing Region entries on the JVM Heap, entries are stored in the system's main memory. Off-heap memory
|
|
generally works best when the objects being stored are uniform in size, are mostly less than 128K, and do not need
|
|
to be deserialized frequently, as explained in the {data-store-name}
|
|
{x-data-store-docs}/managing/heap_use/off_heap_management.html[User Guide].
|
|
|
|
To enable off-heap, annotate the application class with `@EnableOffHeap`, as follows:
|
|
|
|
.Spring application with Off-Heap enabled
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@PeerCacheApplication
|
|
@EnableOffHeap(memorySize = 8192m regionNames = { "Customers", "Orders" })
|
|
class ServerApplication { .. }
|
|
----
|
|
|
|
The `memorySize` attribute is required. The value for the `memorySize` attribute specifies the amount of main memory
|
|
a Region can use in either megabytes (`m`) or gigabytes (`g`).
|
|
|
|
The `regionNames` attribute is an array of Region names that specifies the Regions that store entries in main memory.
|
|
By default, all Regions use main memory if the `regionNames` attribute is not explicitly set.
|
|
|
|
TIP: Alternatively, you can use the `spring.data.gemfire.cache.off-heap.memory-size`
|
|
and `spring.data.gemfire.cache.off-heap.region-names` properties in the `application.properties` file to set
|
|
and configure the values of these `@EnableOffHeap` annotation attributes.
|
|
|
|
See the https://docs.spring.io/spring-data/gemfire/docs/current/api/org/springframework/data/gemfire/config/annotation/EnableOffHeap.html[`@EnableOffHeap` annotation Javadoc]
|
|
for more details.
|
|
|
|
[[bootstrap-annotation-config-region-disk-stores]]
|
|
=== Configuring Disk Stores
|
|
|
|
Alternatively, you can configure Regions to persist data to disk. You can also configure Regions to overflow
|
|
data to disk when Region entries are evicted. In both cases, a `DiskStore` is required to persist and/or overflow
|
|
the data. When an explicit `DiskStore` has not been configured for a Region with persistence or overflow,
|
|
{data-store-name} uses the `DEFAULT` `DiskStore`.
|
|
|
|
We recommend defining Region-specific `DiskStores` when persisting and/or overflowing data to disk.
|
|
|
|
{sdg-name} provides annotation support for defining and creating application Region `DiskStores`
|
|
by annotating the application class with the `@EnableDiskStore` and `@EnableDiskStores` annotations.
|
|
|
|
TIP: `@EnableDiskStores` is a composite annotation for aggregating one or more `@EnableDiskStore` annotations.
|
|
|
|
For example, while `Book` information might mostly consist of reference data from some external data source
|
|
(such as Amazon), `Order` data is most likely going to be transactional in nature and something the application
|
|
is going to need to retain (and maybe even overflow to disk if the transaction volume is high enough) --
|
|
or so any book publisher and author hopes, anyway.
|
|
|
|
Using the `@EnableDiskStore` annotation, you can define and create a `DiskStore` as follows:
|
|
|
|
.Spring application defining a `DiskStore`
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@PeerCacheApplication
|
|
@EnableDiskStore(name = "OrdersDiskStore", autoCompact = true, compactionThreshold = 70,
|
|
maxOplogSize = 512, diskDirectories = @DiskDiretory(location = "/absolute/path/to/order/disk/files"))
|
|
class ServerApplication { .. }
|
|
----
|
|
|
|
Again, more than one `DiskStore` can be defined by using the composite, `@EnableDiskStores` annotation.
|
|
|
|
As with other annotations in {sdg-name}'s annotation-based configuration model, both `@EnableDiskStore`
|
|
and `@EnableDiskStores` have many attributes along with associated configuration properties to customize
|
|
the `DiskStores` created at runtime.
|
|
|
|
Additionally, the `@EnableDiskStores` annotation defines certain common `DiskStore` attributes that apply to all
|
|
`DiskStores` created from `@EnableDiskStore` annotations composed with the `@EnableDiskStores` annotation itself.
|
|
Individual `DiskStore` configuration override a particular global setting, but the `@EnableDiskStores` annotation
|
|
conveniently defines common configuration attributes that apply across all `DiskStores` aggregated by the annotation.
|
|
|
|
{sdg-name} also provides the `DiskStoreConfigurer` callback interface, which can be declared in Java configuration
|
|
and used instead of configuration properties to customize a `DiskStore` at runtime, as the following example shows:
|
|
|
|
.Spring application with custom DiskStore configuration
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@PeerCacheApplication
|
|
@EnableDiskStore(name = "OrdersDiskStore", autoCompact = true, compactionThreshold = 70,
|
|
maxOplogSize = 512, diskDirectories = @DiskDiretory(location = "/absolute/path/to/order/disk/files"))
|
|
class ServerApplication {
|
|
|
|
@Bean
|
|
DiskStoreConfigurer ordersDiskStoreDiretoryConfigurer(
|
|
@Value("${orders.disk.store.location}") String location) {
|
|
|
|
return (beanName, diskStoreFactoryBean) -> {
|
|
|
|
if ("OrdersDiskStore".equals(beanName) {
|
|
diskStoreFactoryBean.setDiskDirs(Collections.singletonList(new DiskDir(location));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
----
|
|
|
|
See the https://docs.spring.io/spring-data/gemfire/docs/current/api/org/springframework/data/gemfire/config/annotation/EnableDiskStore.html[`@EnableDiskStore`] and https://docs.spring.io/spring-data/gemfire/docs/current/api/org/springframework/data/gemfire/config/annotation/EnableDiskStores.html[`@EnableDiskStores`] annotation
|
|
Javadoc for more details on the available attributes as well as associated configuration properties.
|
|
|
|
More details on {data-store-name} Region persistence and overflow (using DiskStores) can be found
|
|
{x-data-store-docs}/developing/storing_data_on_disk/chapter_overview.html[here].
|
|
|
|
[[bootstrap-annotation-config-region-indexes]]
|
|
=== Configuring Indexes
|
|
|
|
There is not much use in storing data in Regions unless the data can be accessed.
|
|
|
|
In addition to `Region.get(key)` operations, particularly when the key is known in advance, data is commonly retrieved
|
|
by executing queries on the Regions that contain the data. With {data-store-name}, queries are written by using
|
|
the Object Query Language (OQL), and the specific data set that a client wishes to access is expressed
|
|
in the query's predicate (for example, `SELECT * FROM /Books b WHERE b.author.name = 'Jon Doe'`).
|
|
|
|
Generally, querying without indexes is inefficient. When executing queries without an index, {data-store-name}
|
|
performs the equivalent of a full table scan.
|
|
|
|
Indexes are created and maintained for fields on objects used in query predicates to match the data of interest, as
|
|
expressed by the query's projection. Different types of indexes, such as
|
|
{x-data-store-docs}/developing/query_index/creating_key_indexes.html[key]
|
|
and {x-data-store-docs}/developing/query_index/creating_hash_indexes.html[hash] indexes, can be created.
|
|
|
|
{sdg-name} makes it easy to create indexes on Regions where the data is stored and accessed. Rather than explicitly
|
|
declaring `Index` bean definitions by using Spring config as before, we can create an `Index` bean definition in Java,
|
|
as follows:
|
|
|
|
.Index bean definition using Java config
|
|
[source, java]
|
|
----
|
|
@Bean("BooksIsbnIndex")
|
|
IndexFactoryBean bookIsbnIndex(GemFireCache gemfireCache) {
|
|
|
|
IndexFactoryBean bookIsbnIndex = new IndexFactoryBean();
|
|
|
|
bookIsbnIndex.setCache(gemfireCache);
|
|
bookIsbnIndex.setName("BookIsbnIndex");
|
|
bookIsbnIndex.setExpression("isbn");
|
|
bookIsbnIndex.setFrom("/Books"));
|
|
bookIsbnIndex.setType(IndexType.KEY);
|
|
|
|
return bookIsbnIndex;
|
|
}
|
|
----
|
|
|
|
Alternatively, we can use <<bootstrap:indexing, XML>> to create an `Index` bean definition, as follows:
|
|
|
|
.Index bean definition using XML
|
|
[source, xml]
|
|
----
|
|
<gfe:index id="BooksIsbnIndex" expression="isbn" from="/Books" type="KEY"/>
|
|
----
|
|
|
|
However, now you can directly define indexes on the fields of your application domain object types for which you know
|
|
will be used in query predicates to speed up those queries. You can even apply indexes for OQL queries generated
|
|
from user-defined query methods on an application's repository interfaces.
|
|
|
|
Re-using the example `Book` entity class from earlier, we can annotate the fields on `Book` that we know are used
|
|
in queries that we define with query methods in the `BookRepository` interface, as follows:
|
|
|
|
.Application domain object type modeling a book using indexes
|
|
[source, java]
|
|
----
|
|
@Region("Books")
|
|
class Book {
|
|
|
|
@Id
|
|
private ISBN isbn;
|
|
|
|
@Indexed
|
|
private Author author;
|
|
|
|
private Category category;
|
|
|
|
private LocalDate releaseDate;
|
|
|
|
private Publisher publisher;
|
|
|
|
@LuceneIndexed
|
|
private String title;
|
|
|
|
}
|
|
----
|
|
|
|
In our new `Book` class definition, we annotated the `author` field with `@Indexed` and the `title` field
|
|
with `@LuceneIndexed`. Also, the `isbn` field had previously been annotated with Spring Data's `@Id` annotation,
|
|
which identifies the field containing the unique identifier for `Book` instances, and, in {sdg-name}, the `@Id`
|
|
annotated field or property is used as the key in the Region when storing the entry.
|
|
|
|
* `@Id` annotated fields or properties result in the creation of an {data-store-name} `KEY` Index.
|
|
* `@Indexed` annotated fields or properties result in the creation of an {data-store-name} `HASH` Index (the default).
|
|
* `@LuceneIndexed` annotated fields or properties result in the creation of an {data-store-name} Lucene Index, used in
|
|
text-based searches with {data-store-name}'s Lucene integration and support.
|
|
|
|
When the `@Indexed` annotation is used without setting any attributes, the index `name`, `expression`, and `fromClause`
|
|
are derived from the field or property of the class on which the `@Indexed` annotation has been added. The `expression`
|
|
is exactly the name of the field or property. The `fromClause` is derived from the `@Region` annotation on
|
|
the domain object's class, or the simple name of the domain object class if the `@Region` annotation was not specified.
|
|
|
|
Of course, you can explicitly set any of the `@Indexed` annotation attributes to override the default values
|
|
provided by {sdg-name}.
|
|
|
|
.Application domain object type modeling a Book with customized indexes
|
|
[source, java]
|
|
----
|
|
@Region("Books")
|
|
class Book {
|
|
|
|
@Id
|
|
private ISBN isbn;
|
|
|
|
@Indexed(name = "BookAuthorNameIndex", expression = "author.name", type = "FUNCTIONAL")
|
|
private Author author;
|
|
|
|
private Category category;
|
|
|
|
private LocalDate releaseDate;
|
|
|
|
private Publisher publisher;
|
|
|
|
@LuceneIndexed(name = "BookTitleIndex", destory = true)
|
|
private String title;
|
|
|
|
}
|
|
----
|
|
|
|
The `name` of the index, which is auto-generated when not explicitly set, is also used as the name of the bean
|
|
registered in the Spring container for the index. If necessary, this index bean can even be injected by name
|
|
into another application component.
|
|
|
|
The generated name of the index follows this pattern: `<Region Name><Field/Property Name><Index Type>Idx`.
|
|
For example, the name of the `author` index would be, `BooksAuthorHashIdx`.
|
|
|
|
To enable indexing, annotate the application class with `@EnableIndexing`, as follows:
|
|
|
|
.Spring application with Indexing enabled
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@PeerCacheApplication
|
|
@EnableEntityDefinedRegions
|
|
@EnableIndexing
|
|
class ServerApplication { .. }
|
|
----
|
|
|
|
NOTE: The `@EnablingIndexing` annotation has no effect unless the `@EnableEntityDefinedRegions` is also declared.
|
|
Essentially, indexes are defined from fields or properties on the entity class types, and entity classes must be scanned
|
|
to inspect the entity's fields and properties for the presence of index annotations. Without this scan, index annotations
|
|
cannot be found. We also strongly recommend that you limit the scope of the scan.
|
|
|
|
While Lucene queries are not (yet) supported on {sdg-name} repositories, {sdg-acronym} does provide comprehensive
|
|
https://docs.spring.io/spring-data-gemfire/docs/current/reference/html/#bootstrap:lucene[support] for {data-store-name}
|
|
Lucene queries by using the familiar Spring template design pattern.
|
|
|
|
Finally, we close this section with a few extra tips to keep in mind when using indexes:
|
|
|
|
* While OQL indexes are not required to execute OQL Queries, Lucene Indexes are required to execute Lucene
|
|
text-based searches.
|
|
* OQL indexes are not persisted to disk. They are only maintained in memory. So, when an {data-store-name}
|
|
node is restarted, the index must be rebuilt.
|
|
* You also need to be aware of the overhead associated in maintaining indexes, particularly since an index is stored
|
|
exclusively in memory and especially when Region entries are updated. Index "maintenance" can be
|
|
{x-data-store-javadoc}/org/apache/geode/cache/RegionFactory.html#setIndexMaintenanceSynchronous-boolean-[configured]
|
|
as an asynchronous task.
|
|
|
|
Another optimization that you can use when restarting your Spring application where indexes have to be rebuilt
|
|
is to first define all the indexes up front and then create them all at once, which, in {sdg-name}, happens
|
|
when the Spring container is refreshed.
|
|
|
|
You can define indexes up front and then create them all at once by setting the `define` attribute on
|
|
the `@EnableIndexing` annotation to `true`.
|
|
|
|
See {x-data-store-docs}/developing/query_index/create_multiple_indexes.html["`Creating Multiple Indexes at Once`"]
|
|
in {data-store-name}'s User Guide for more details.
|
|
|
|
Creating sensible indexes is an important task, since it is possible for a poorly designed index
|
|
to do more harm than good.
|
|
|
|
See both the https://docs.spring.io/spring-data/gemfire/docs/current/api/org/springframework/data/gemfire/mapping/annotation/Indexed.html[`@Indexed`] annotation
|
|
and https://docs.spring.io/spring-data/gemfire/docs/current/api/org/springframework/data/gemfire/mapping/annotation/LuceneIndexed.html[`@LuceneIndexed`] annotation
|
|
Javadoc for complete list of configuration options.
|
|
|
|
More details on {data-store-name} OQL queries can be found
|
|
{x-data-store-docs}/developing/querying_basics/chapter_overview.html[here].
|
|
|
|
More details on {data-store-name} indexes can be found
|
|
{x-data-store-docs}/developing/query_index/query_index.html[here].
|
|
|
|
More details on {data-store-name} Lucene queries can be found
|
|
{x-data-store-docs}/tools_modules/lucene_integration.html[here].
|
|
|
|
[[bootstrap-annotation-config-continuous-queries]]
|
|
== Configuring Continuous Queries
|
|
|
|
Another very important and useful feature of {data-store-name} is
|
|
{x-data-store-docs}/developing/continuous_querying/chapter_overview.html[Continuous Queries].
|
|
|
|
In a world of Internet-enabled things, events and streams of data come from everywhere. Being able to handle
|
|
and process a large stream of data and react to events in real time is an increasingly important requirement
|
|
for many applications. One example is self-driving vehicles. Being able to receive, filter, transform, analyze,
|
|
and act on data in real time is a key differentiator and characteristic of real time applications.
|
|
|
|
Fortunately, {data-store-name} was ahead of its time in this regard. By using Continuous Queries (CQ),
|
|
a client application can express the data or events it is interested in and register listeners to handle and process
|
|
the events as they occur. The data that a client application may be interested in is expressed as an OQL query,
|
|
where the query predicate is used to filter or identify the data of interest. When data is changed or added
|
|
and it matches the criteria defined in the query predicate of the registered CQ, the client application is notified.
|
|
|
|
{sdg-name} makes it easy to define and register CQs, along with an associated listener to handle and process CQ
|
|
events without all the cruft of {data-store-name}'s plumbing. {sdg-acronym}'s new annotation-based configuration
|
|
for CQs builds on the existing Continuous Query support in the <<apis:continuous-query, continuous query listener container>>.
|
|
|
|
For instance, say a banking application registers interest in every customers' checking acccount to detect overdraft
|
|
withdrawls and handle this event by either applying overdraft protection or notifyinfg the customer. Then, the
|
|
application might register the following CQ:
|
|
|
|
.Spring `ClientCache` application with registered CQ and listener.
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@ClientCacheApplication(subcriptionEnabled = true)
|
|
@EnableContinuousQueries
|
|
class PublisherPrintApplication {
|
|
|
|
@ContinuousQuery(name = "OverdraftProtection", query = "SELECT * FROM /CheckingAccount ca WHERE ca.balance < 0.0")
|
|
void handleOverdraft(CqEvent event) {
|
|
// Quick!!! Put more money into the checking account or notify the customer of the checking account!
|
|
}
|
|
}
|
|
----
|
|
|
|
To enable Continuous Queries, annotate your application class with `@EnableContinuousQueries`.
|
|
|
|
Defining Continuous Queries consists of annotating any Spring `@Component`-annotated POJO class methods
|
|
with the `@ContinuousQuery` annotation (in similar fashion to {sdg-acronym}'s Function-annotated POJO methods).
|
|
A POJO method defined with a CQ by using the `@ContinuousQuery` annotation is called any time data matching
|
|
the query predicate is added or changed.
|
|
|
|
Additionally, the POJO method signature should adhere to the requirements outlined in the section on
|
|
<<apis:continuous-query:adapter, the `ContinuousQueryListener` and the `ContinuousQueryListenerAdapter`>>.
|
|
|
|
See the https://docs.spring.io/spring-data/gemfire/docs/current/api/org/springframework/data/gemfire/config/annotation/EnableContinuousQueries.html[`@EnableContinuousQueries`] and https://docs.spring.io/spring-data/gemfire/docs/current/api/index.html?org/springframework/data/gemfire/config/annotation/EnableContinuousQueries.html[`@ContinuousQuery`] annotation
|
|
Javadoc for more details on available attributes and configuration settings.
|
|
|
|
More details on {sdg-name}'s continuous query support can be found
|
|
<<apis:continuous-query, here>>.
|
|
|
|
More details on {data-store-name}'s Continuous Queries can be found
|
|
{x-data-store-docs}/developing/continuous_querying/chapter_overview.html[here].
|
|
|
|
[[bootstrap-annotation-config-caching]]
|
|
== Configuring Spring's Cache Abstraction
|
|
|
|
With {sdg-name}, {data-store-name} can be used as a caching provider in Spring's
|
|
{spring-framework-docs}/integration.html#cache[cache abstraction].
|
|
|
|
In Spring's Cache Abstraction, the caching annotations (such as `@Cacheable`) identify the cache on which a cache lookup
|
|
is performed before invoking a potentially expensive operation. The results of an application service method are cached
|
|
after the operation is invoked.
|
|
|
|
In {sdg-name}, a Spring `Cache` corresponds directly to a {data-store-name} Region. The Region must exist before
|
|
any caching annotated application service methods are called. This is true for any of Spring's caching annotations
|
|
(that is, `@Cacheable`, `@CachePut` and `@CacheEvict`) that identify the cache to use in the service operation.
|
|
|
|
For instance, our publisher's Point-of-Sale (PoS) application might have a feature to determine or lookup the `Price`
|
|
of a `Book` during a sales transaction, as the following example shows:
|
|
|
|
[source, java]
|
|
----
|
|
@Service
|
|
class PointOfSaleService
|
|
|
|
@Cacheable("BookPrices")
|
|
Price runPriceCheckFor(Book book) {
|
|
...
|
|
}
|
|
|
|
@Transactional
|
|
Receipt checkout(Order order) {
|
|
...
|
|
}
|
|
|
|
...
|
|
}
|
|
----
|
|
|
|
To make your work easier when you use {sdg-name} with Spring's Cache Abstraction, two new features have been added
|
|
to the annotation-based configuration model.
|
|
|
|
Consider the following Spring caching configuration:
|
|
|
|
.Enabling Caching using {data-store-name} as the caching provider
|
|
[source, java]
|
|
----
|
|
@EnableCaching
|
|
class CachingConfiguration {
|
|
|
|
@Bean
|
|
GemfireCacheManager cacheManager(GemFireCache gemfireCache) {
|
|
|
|
GemfireCacheManager cacheManager = new GemfireCacheManager();
|
|
|
|
cacheManager.setCache(gemfireCache);
|
|
|
|
return cacheManager;
|
|
}
|
|
|
|
@Bean("BookPricesCache")
|
|
ReplicatedRegionFactoryBean<Book, Price> bookPricesRegion(GemFireCache gemfireCache) {
|
|
|
|
ReplicatedRegionFactoryBean<Book, Price> bookPricesRegion =
|
|
new ReplicatedRegionFactoryBean<>();
|
|
|
|
bookPricesRegion.setCache(gemfireCache);
|
|
bookPricesRegion.setClose(false);
|
|
bookPricesRegion.setPersistent(false);
|
|
|
|
return bookPricesRegion;
|
|
}
|
|
|
|
@Bean("PointOfSaleService")
|
|
PointOfSaleService pointOfSaleService(..) {
|
|
return new PointOfSaleService(..);
|
|
}
|
|
}
|
|
----
|
|
|
|
Using {sdg-name}'s new features, you can simplify the same caching configuration to the following:
|
|
|
|
.Enabling {data-store-name} Caching
|
|
[source, java]
|
|
----
|
|
@EnableGemfireCaching
|
|
@EnableCachingDefinedRegions
|
|
class CachingConfiguration {
|
|
|
|
@Bean("PointOfSaleService")
|
|
PointOfSaleService pointOfSaleService(..) {
|
|
return new PointOfSaleService(..);
|
|
}
|
|
}
|
|
----
|
|
|
|
First, the `@EnableGemfireCaching` annotation replaces both the Spring `@EnableCaching` annotation and the need
|
|
to declare an explicit `CacheManager` bean definition (named "cacheManager") in the Spring config.
|
|
|
|
Second, the `@EnableCachingDefinedRegions` annotation, like the `@EnableEntityDefinedRegions` annotation described in
|
|
"`<<bootstrap-annotation-config-regions, Configuring Regions>>`", inspects the entire Spring application, caching
|
|
annotated service components to identify all the caches that are needed by the application at runtime and creates
|
|
Regions in {data-store-name} for these caches on application startup.
|
|
|
|
The Regions created are local to the application process that created the Regions. If the application is a peer `Cache`,
|
|
the Regions exist only on the application node. If the application is a `ClientCache`, then {sdg-acronym} creates
|
|
client `PROXY` Regions and expects those Regions with the same name to already exist on the servers in the cluster.
|
|
|
|
NOTE: {sdg-acronym} cannot determine the cache required by a service method using a Spring `CacheResolver`
|
|
to resolve the cache used in the operation at runtime.
|
|
|
|
TIP: {sdg-acronym} also supports JCache (JSR-107) cache annotations on application service components.
|
|
See the core {spring-framework-docs}/integration.html#cache-jsr-107[_Spring Framework Reference Guide_]
|
|
for the equivalent Spring caching annotation to use in place of JCache caching annotations.
|
|
|
|
Refer to the <<apis:spring-cache-abstraction, "`Support for the Spring Cache Abstraction`">> section for more details
|
|
on using {data-store-name} as a caching provider in Spring's Cache Abstraction.
|
|
|
|
More details on Spring's Cache Abstraction can be found
|
|
{spring-framework-docs}/integration.html#cache[here].
|
|
|
|
[[bootstrap-annotation-config-cluster]]
|
|
== Configuring Cluster Configuration Push
|
|
|
|
This may be the most exciting new feature in {sdg-name}.
|
|
|
|
When a client application class is annotated with `@EnableClusterConfiguration`, any Regions or Indexes defined
|
|
and declared as beans in the Spring Container by the client application are "`pushed`" to the cluster of servers
|
|
to which the client is connected. Not only that, but this "`push`" is performed in such a way that {data-store-name}
|
|
remembers the configuration pushed by the client when using HTTP. If all the nodes in the cluster go down, they
|
|
come back up with the same configuration as before. If a new server is added to the cluster, it will acquire
|
|
identical configuration.
|
|
|
|
In a sense, this feature is not much different than if you were to use _Gfsh_ to manually create the Regions and Indexes
|
|
on all the servers in the cluster. Except that now, with {sdg-name}, you no longer need to use _Gfsh_ to create Regions
|
|
and Indexes. Your Spring Boot application, enabled with the power of {sdg-name}, already contains all the configuration
|
|
metadata needed to create Regions and Indexes for you.
|
|
|
|
When you use the Spring Data Repository abstraction, we know all the Regions (such as those defined by the `@Region`
|
|
annotated entity classes) and Indexes (such as those defined by the `@Indexed`-annotated entity fields and properties)
|
|
that your application will need.
|
|
|
|
When you use Spring's Cache Abstraction, we also know all the Regions for all the caches identified in the caching
|
|
annotations needed by your application's service components.
|
|
|
|
Essentially, you are already telling us everything we need to know simply by developing your application with the
|
|
Spring Framework simply by using all of its API and features, whether expressed in annotation metadata, Java, XML
|
|
or otherwise, and whether for configuration, mapping, or whatever the purpose.
|
|
|
|
The point is, you can focus on your application's business logic while using the framework's features and supporting
|
|
infrastructure (such as Spring's Cache Abstraction, Spring Data Repositories, Spring's Transaction Management,
|
|
and so on) and {sdg-name} takes care of all the {data-store-name} plumbing required by those framework features
|
|
on your behalf.
|
|
|
|
Pushing configuration from the client to the servers in the cluster and having the cluster remember it is made possible
|
|
in part by the use of {data-store-name}'s {x-data-store-docs}/configuring/cluster_config/gfsh_persist.html[Cluster Configuration]
|
|
service. {data-store-name}'s Cluster Configuration service is also the same service used by _Gfsh_ to record
|
|
schema-related changes (for example, `gfsh> create region --name=Example --type=PARTITION`) issued by the user
|
|
to the cluster from the shell.
|
|
|
|
Of course, since the cluster may "`remember`" the prior configuration pushed by a client from a previous run,
|
|
{sdg-name} is careful not to stomp on any existing Regions and Indexes already defined in the servers.
|
|
This is especially important, for instance, when Regions already contain data!
|
|
|
|
NOTE: Currently, there is no option to overwrite any existing Region or Index definitions. To re-create a Region
|
|
or Index, you must use _Gfsh_ to first destroy the Region or Index and then restart the client application
|
|
so that configuration is pushed up to the server again. Alternatively, you can use _Gfsh_ to (re-)define the Regions
|
|
and Indexes manually.
|
|
|
|
NOTE: Unlike _Gfsh_, {sdg-name} supports the creation of Regions and Indexes only on the servers from a client.
|
|
For advanced configuration and use cases, you should use _Gfsh_ to manage the (server-side) cluster.
|
|
|
|
WARNING: To use this feature you must explicitly declare the `org.springframework:spring-web` dependency on the
|
|
classpath of your Spring, {data-store-name} `ClientCache` application.
|
|
|
|
Consider the power expressed in the following configuration:
|
|
|
|
.Spring `ClientCache` application
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@ClientCacheApplication
|
|
@EnableCachingDefinedRegions
|
|
@EnableEntityDefinedRegions
|
|
@EnableIndexing
|
|
@EnableGemfireCaching
|
|
@EnableGemfireRepositories
|
|
@EnableClusterConfiguration
|
|
class ClientApplication { .. }
|
|
----
|
|
|
|
You instantly get a Spring Boot application with a {data-store-name} `ClientCache` instance, Spring Data Repositories,
|
|
Spring's Cache Abstraction with {data-store-name} as the caching provider (where Regions and Indexes
|
|
are not only created on the client but pushed to the servers in the cluster).
|
|
|
|
From there, you only need to do the following:
|
|
|
|
* Define the application's domain model objects annotated with mapping and index annotations.
|
|
* Define Repository interfaces to support basic data access operations and simple queries for each of your entity types.
|
|
* Define the service components containing the business logic transacting the entities.
|
|
* Declare the appropriate annotations on service methods that require caching, transactional behavior, and so on.
|
|
|
|
Nothing in this case pertains to the infrastructure and plumbing required in the application's back-end services
|
|
(such as {data-store-name}). Database users have similar features. Now Spring and {data-store-name} developers do too.
|
|
|
|
When combined with the following {sdg-name} annotations, this application really starts to take flight,
|
|
with very little effort:
|
|
|
|
* `@EnableContinuousQueries`
|
|
* `@EnableGemfireFunctionExecutions`
|
|
* `@EnableGemfireCacheTransactions`
|
|
|
|
See the https://docs.spring.io/spring-data/gemfire/docs/current/api/index.html?org/springframework/data/gemfire/config/annotation/EnableClusterConfiguration.html[`@EnableClusterConfiguration` annotation
|
|
Javadoc] for more details.
|
|
|
|
[[bootstrap-annotation-config-ssl]]
|
|
== Configuring SSL
|
|
|
|
Equally important to serializing data to be transferred over the wire is securing the data while in transit.
|
|
Of course, the common way to accomplish this in Java is by using the Secure Sockets Extension (SSE)
|
|
and Transport Layer Security (TLS).
|
|
|
|
To enable SSL, annotate your application class with `@EnableSsl`, as follows:
|
|
|
|
.Spring `ClientCache` application with SSL enabled
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@ClientCacheApplication
|
|
@EnableSsl
|
|
public class ClientApplication { .. }
|
|
----
|
|
|
|
Then you need to set the necessary SSL configuration attributes or properties: keystores, usernames/passwords, and so on.
|
|
|
|
You can individually configure different {data-store-name} components (`GATEWAY`, `HTTP`, `JMX`, `LOCATOR`, and `SERVER`)
|
|
with SSL, or you can collectively configure them to use SSL by using the `CLUSTER` enumerated value.
|
|
|
|
You can specify which {data-store-name} components the SSL configuration settings should applied by using
|
|
the nested `@EnableSsl` annotation, `components` attribute with enumerated values from the `Component` enum,
|
|
as follows:
|
|
|
|
.Spring `ClientCache` application with SSL enabled by component
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@ClientCacheApplication
|
|
@EnableSsl(components = { GATEWAY, LOCATOR, SERVER })
|
|
public class ClientApplication { .. }
|
|
----
|
|
|
|
In addition, you can also specify component-level SSL configuration (`ciphers`, `protocols` and `keystore`/`truststore`
|
|
information) by using the corresponding annotation attribute or associated configuration properties.
|
|
|
|
See the https://docs.spring.io/spring-data/gemfire/docs/current/api/org/springframework/data/gemfire/config/annotation/EnableSsl.html[`@EnableSsl` annotation Javadoc]
|
|
for more details.
|
|
|
|
More details on {data-store-name} SSL support can be found
|
|
{x-data-store-docs}/managing/security/ssl_overview.html[here].
|
|
|
|
[[bootstrap-annotation-config-security]]
|
|
== Configuring Security
|
|
|
|
Without a doubt, application security is extremely important, and {sdg-name} provides comprehensive support
|
|
for securing both {data-store-name} clients and servers.
|
|
|
|
Recently, {data-store-name} introduced a new {x-data-store-docs}/managing/security/implementing_security.html[Integrated Security] framework
|
|
(replacing its old authentication and authorization security model) for handling authentication and authorization.
|
|
One of the main features and benefits of this new security framework is that it integrates with
|
|
https://shiro.apache.org/[Apache Shiro] and can therefore delegate both authentication and authorization requests
|
|
to Apache Shiro to enforce security.
|
|
|
|
The remainder of this section demonstrates how {sdg-name} can simplify {data-store-name}'s security story even further.
|
|
|
|
[[bootstrap-annotation-config-security-server]]
|
|
=== Configuring Server Security
|
|
|
|
There are several different ways in which you can configure security for servers in a {data-store-name} cluster.
|
|
|
|
* Implement the {data-store-name} `org.apache.geode.security.SecurityManager` interface and set {data-store-name}'s
|
|
`security-manager` property to refer to your application `SecurityManager` implementation using the fully qualified
|
|
class name. Alternatively, users can construct and initialize an instance of their `SecurityManager` implementation
|
|
and set it with the {x-data-store-javadoc}/org/apache/geode/cache/CacheFactory.html#setSecurityManager-org.apache.geode.security.SecurityManager[CacheFactory.setSecurityManager(:SecurityManager)]
|
|
method when creating a {data-store-name} peer `Cache`.
|
|
|
|
* Create an Apache Shiro https://shiro.apache.org/configuration.html[`shiro.ini`] file with the users, roles,
|
|
and permissions defined for your application and then set the {data-store-name} `security-shiro-init` property
|
|
to refer to this `shiro.ini` file, which must be available in the `CLASSPATH`.
|
|
|
|
* Using only Apache Shiro, annotate your Spring Boot application class with {sdg-name}'s new
|
|
`@EnableSecurity` annotation and define one or more Apache Shiro https://shiro.apache.org/realm.html[`Realms`]
|
|
as beans in the Spring container for accessing your application's security metadata (that is, authorized users, roles,
|
|
and permissions).
|
|
|
|
The problem with the first approach is that you must implement your own `SecurityManager`, which can be quite tedious
|
|
and error-prone. Implementing a custom `SecurityManager` offers some flexibility in accessing security metadata from
|
|
whatever data source stores the metadata, such as LDAP or even a proprietary, internal
|
|
data source. However, that problem has already been solved by configuring and using Apache Shiro `Realms`,
|
|
which is more universally known and non-{data-store-name}-specific.
|
|
|
|
TIP: See {data-store-name}'s security examples for {x-data-store-docs}/managing/security/authentication_examples.html[Authentication]
|
|
and {x-data-store-docs}/managing/security/authorization_example.html[Authorization] as one possible way
|
|
to implement your own custom, application-specific `SecurityManager`. However, we strongly recommend *against* doing so.
|
|
|
|
The second approach, using an Apache Shiro INI file, is marginally better, but you still need to be familiar with
|
|
the INI file format in the first place. Additionally, an INI file is static and not easily updatable at runtime.
|
|
|
|
The third approach is the most ideal, since it adheres to widely known and industry-accepted concepts
|
|
(that is, Apache Shiro's Security framework) and is easy to setup, as the following example shows:
|
|
|
|
.Spring server application using Apache Shiro
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@CacheServerApplication
|
|
@EnableSecurity
|
|
class ServerApplication {
|
|
|
|
@Bean
|
|
PropertiesRealm shiroRealm() {
|
|
|
|
PropertiesRealm propertiesRealm = new PropertiesRealm();
|
|
|
|
propertiesRealm.setResourcePath("classpath:shiro.properties");
|
|
propertiesRealm.setPermissionResolver(new GemFirePermissionResolver());
|
|
|
|
return propertiesRealm;
|
|
}
|
|
}
|
|
----
|
|
|
|
TIP: The configured `Realm` shown in the preceding example could easily have been any of Apache Shiro's supported `Realms`:
|
|
|
|
* https://shiro.apache.org/static/1.3.2/apidocs/org/apache/shiro/realm/activedirectory/package-frame.html[ActiveDirectory]
|
|
* https://shiro.apache.org/static/1.3.2/apidocs/org/apache/shiro/realm/jdbc/package-frame.html[JDBC]
|
|
* https://shiro.apache.org/static/1.3.2/apidocs/org/apache/shiro/realm/jndi/package-frame.html[JNDI]
|
|
* https://shiro.apache.org/static/1.3.2/apidocs/org/apache/shiro/realm/ldap/package-frame.html[LDAP]
|
|
* A `Realm` supporting the https://shiro.apache.org/static/1.3.2/apidocs/org/apache/shiro/realm/text/IniRealm.html[INI format].
|
|
|
|
You could even create a custom implementation of an Apache Shiro `Realm`.
|
|
|
|
See Apache Shiro's https://shiro.apache.org/realm.html[documentation on Realms] for more details.
|
|
|
|
When Apache Shiro is on the `CLASSPATH` of the servers in the cluster and one or more Apache Shiro `Realms`
|
|
have been defined as beans in the Spring container, {sdg-name} detects this configuration and uses Apache Shiro
|
|
as the security provider to secure your {data-store-name} servers when the `@EnableSecurity` annotation is used.
|
|
|
|
TIP: You can find more information about {sdg-name}'s support for {data-store-name}'s new integrated security
|
|
framework using Apache Shiro in this
|
|
https://spring.io/blog/2016/11/10/spring-data-geode-1-0-0-incubating-release-released[spring.io blog post].
|
|
|
|
See the https://docs.spring.io/spring-data/gemfire/docs/current/api/index.html?org/springframework/data/gemfire/config/annotation/EnableSecurity.html[`@EnableSecurity`] annotation
|
|
Javadoc for more details on available attributes and associated configuration properties.
|
|
|
|
More details on {data-store-name} security can be found
|
|
{x-data-store-docs}/managing/security/chapter_overview.html[here].
|
|
|
|
[[bootstrap-annotation-config-security-client]]
|
|
=== Configuring Client Security
|
|
|
|
The security story would not be complete without discussing how to secure Spring-based, {data-store-name} cache client
|
|
applications as well.
|
|
|
|
{data-store-name}'s process for securing a client application is, honestly, rather involved. In a nutshell, you need to:
|
|
|
|
. Provide an implementation of the
|
|
{x-data-store-javadoc}/org/apache/geode/security/AuthInitialize.html[`org.apache.geode.security.AuthInitialize`] interface.
|
|
. Set the {data-store-name} `security-client-auth-init` (System) property to refer to the custom, application-provided
|
|
`AuthInitialize` interface.
|
|
. Specify the user credentials in a proprietary, {data-store-name} `gfsecurity.properties` file.
|
|
|
|
{sdg-name} simplifies all of those steps by using the same `@EnableSecurity` annotation that was used in the server
|
|
applications. In other words, the same `@EnableSecurity` annotation handles security for both client and server
|
|
applications. This feature makes it easier for users when they decide to switch their applications from an embedded,
|
|
peer `Cache` application to a `ClientCache` application, for instance. Simply change the {sdg-acronym} annotation
|
|
from `@PeerCacheApplication` or `@CacheServerApplication` to `@ClientCacheApplication`, and you are done.
|
|
|
|
Effectively, all you need to do on the client is the following:
|
|
|
|
.Spring client application using `@EnableSecurity`
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@ClientCacheApplication
|
|
@EnableSecurity
|
|
class ClientApplication { .. }
|
|
----
|
|
|
|
Then you can define the familiar Spring Boot `application.properties` file containing the required username and password,
|
|
as the following example shows, and you are all set:
|
|
|
|
.Spring Boot `application.properties` file with the required Security credentials
|
|
[source, java]
|
|
----
|
|
spring.data.gemfire.security.username=jackBlack
|
|
spring.data.gemfire.security.password=b@cK!nB1@cK
|
|
----
|
|
|
|
TIP: By default, Spring Boot can find your `application.properties` file when it is placed in the root of
|
|
the application's `CLASSPATH`. Of course, Spring supports many ways to locate resources by using its
|
|
{spring-framework-docs}/core.html#resources[Resource abstraction].
|
|
|
|
See the https://docs.spring.io/spring-data/gemfire/docs/current/api/index.html?org/springframework/data/gemfire/config/annotation/EnableSecurity.html[`@EnableSecurity`] annotation
|
|
Javadoc for more details on available attributes and associated configuration properties.
|
|
|
|
More details on {data-store-name} Security can be found
|
|
{x-data-store-docs}/managing/security/chapter_overview.html[here].
|
|
|
|
[[bootstrap-annotation-config-tips]]
|
|
== Configuration Tips
|
|
|
|
The following tips can help you get the most out of using the new annotation-based configuration model:
|
|
|
|
* <<bootstrap-annotation-config-tips-organization>>
|
|
* <<bootstrap-annotation-config-tips-undocumented-annotations>>
|
|
|
|
[[bootstrap-annotation-config-tips-organization]]
|
|
=== Configuration Organization
|
|
|
|
As we saw in the section on <<bootstrap-annotation-config-cluster, "`Configuring Cluster Configuration Push`">>,
|
|
when many {data-store-name} or {sdg-name} features are enabled by using annotations, we begin to stack a lot of
|
|
annotations on the Spring `@Configuration` or `@SpringBootApplication` class. In this situation, it makes sense
|
|
to start compartmentalizing the configuration a bit.
|
|
|
|
For instance, consider the following declaration:
|
|
|
|
.Spring `ClientCache` application with the kitchen sink
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@ClientCacheApplication
|
|
@EnableContinuousQueries
|
|
@EnableCachingDefinedRegions
|
|
@EnableEntityDefinedRegions
|
|
@EnableIndexing
|
|
@EnableGemfireCacheTransactions
|
|
@EnableGemfireCaching
|
|
@EnableGemfireFunctionExecutions
|
|
@EnableGemfireRepositories
|
|
@EnableClusterConfiguration
|
|
class ClientApplication { .. }
|
|
----
|
|
|
|
We could break this configuration down by concern, as follows:
|
|
|
|
.Spring `ClientCache` application with the kitcken sink to boot
|
|
[source, java]
|
|
----
|
|
@SpringBootApplication
|
|
@Import({ GemFireConfiguration.class, CachingConfiguration.class,
|
|
FunctionsConfiguration.class, QueriesConfiguration.class,
|
|
RepositoriesConfiguration.class })
|
|
class ClientApplication { .. }
|
|
|
|
@ClientCacheApplication
|
|
@EnableClusterConfiguration
|
|
@EnableGemfireCacheTransactions
|
|
class GemFireConfiguration { .. }
|
|
|
|
@EnableGemfireCaching
|
|
@EnableCachingDefinedRegions
|
|
class CachingConfiguration { .. }
|
|
|
|
@EnableGemfireFunctionExecutions
|
|
class FunctionsConfiguration { .. }
|
|
|
|
@EnableContinuousQueries
|
|
class QueriesConfiguration {
|
|
|
|
@ContinuousQuery(..)
|
|
void processCqEvent(CqEvent event) {
|
|
...
|
|
}
|
|
}
|
|
|
|
@EnableEntityDefinedRegions
|
|
@EnableGemfireRepositories
|
|
@EnableIndexing
|
|
class RepositoriesConfiguration { .. }
|
|
----
|
|
|
|
While it does not matter to the Spring Framework, we generally recommend aiming for readability, for the sake of
|
|
the next person who has to maintain the code (which might be you at some point in the future).
|
|
|
|
[[bootstrap-annotation-config-tips-undocumented-annotations]]
|
|
=== Additional Configuration-based Annotations
|
|
|
|
The following {sdg-acronym} Annotations were not discussed in this reference documentation, either because
|
|
the annotation supports a deprecated feature of {data-store-name} or because there are better, alternative ways
|
|
to accomplishing the function that the annotation provides:
|
|
|
|
* `@EnableAuth`: Enables {data-store-name}'s old authentication and authorization security model. (Deprecated.
|
|
{data-store-name}'s new integrated security framework can be enabled on both clients and servers by using {sdg-acronym}'s
|
|
`@EnableSecurity` annotation, as described in "`<<bootstrap-annotation-config-security>>`".)
|
|
* `@EnableAutoRegionLookup`: Not recommended. Essentially, this annotation supports finding Regions defined in external
|
|
configuration metadata (such as `cache.xml` or Cluster Configuration when applied to a server) and automatically
|
|
registers those Regions as beans in the Spring container. This annotation corresponds with the `<gfe:auto-region-lookup>`
|
|
element in SDG's XML namespace. More details can found <<bootstrap:region:lookup:auto, here>>. Users should generally
|
|
prefer Spring configuration when using Spring and {sdg-name}. See "`<<bootstrap-annotation-config-regions>>`"
|
|
and "`<<bootstrap-annotation-config-cluster>>`" instead.
|
|
* `@EnableBeanFactoryLocator`: Enables the {sdg-acronym} `GemfireBeanFactoryLocator` feature, which is only useful
|
|
when using external configuration metadata (for example, `cache.xml`). For example, if you define a `CacheLoader` on
|
|
a Region defined in `cache.xml`, you can still autowire this `CacheLoader` with, say, a relational database
|
|
`DataSource` bean defined in Spring configuration. This annotation takes advantage of this {sdg-acronym}
|
|
<<apis:declarable, feature>> and might be useful if you have a large amount of legacy configuration metadata,
|
|
such as `cache.xml` files.
|
|
* `@EnableGemFireAsLastResource`: Discussed in <<apis:global-transaction-management, Global - JTA Transaction Management>>
|
|
with {data-store-name}.
|
|
* `@EnableMcast`: Enables {data-store-name}'s old peer discovery mechanism that uses UDP-based multi-cast networking.
|
|
(_Deprecated_. Use {data-store-name} Locators instead. See "`<<bootstrap-annotation-config-embedded-services-locator>>`".
|
|
* `@EnableRegionDataAccessTracing`: Useful for debugging purposes. This annotation enables tracing for all data access
|
|
operations performed on a Region by registering an AOP Aspect that proxies all Regions declared as beans
|
|
in the Spring container, intercepting the Region operation and logging the event.
|
|
|
|
[[bootstrap-annotation-config-conclusion]]
|
|
== Conclusion
|
|
|
|
As we learned in the previous sections, {sdg-name}'s new annotation-based configuration model provides a tremendous
|
|
amount of power. Hopefully, it lives up to its goal of making it easier for you to _get started quickly_ and _easily_
|
|
when using {data-store-name} with Spring.
|
|
|
|
Keep in mind that, when you use the new annotations, you can still use Java configuration or XML configuration.
|
|
You can even combine all three approaches by using Spring's {spring-framework-javadoc}/org/springframework/context/annotation/Import.html[`@Import`]
|
|
and {spring-framework-javadoc}/org/springframework/context/annotation/ImportResource.html[`@ImportResource`]
|
|
annotations on a Spring `@Configuration` or `@SpringBootApplication` class. The moment you explicitly provide
|
|
a bean definition that would otherwise be provided by {sdg-name} using 1 of the annotations, the annotation-based
|
|
configuration backs away.
|
|
|
|
[NOTE]
|
|
====
|
|
In certain cases, you may even need to fall back to Java configuration, as in the `Configurers` case, to handle
|
|
more complex or conditional configuration logic that is not easily expressed in or cannot be accomplished by
|
|
using annotations alone. Do not be alarmed. This behavior is to be expected.
|
|
|
|
For example, another case where you need Java or XML configuration is when configuring {data-store-name} WAN components,
|
|
which currently do not have any annotation configuration support. However, defining and registering WAN components
|
|
requires only using the `org.springframework.data.gemfire.wan.GatewayReceiverFactoryBean`
|
|
and `org.springframework.data.gemfire.wan.GatewaySenderFactoryBean` API classes in the Java configuration of your Spring
|
|
`@Configuration` or `@SpringBootApplication` classes (recommended).
|
|
====
|
|
|
|
The annotations were not meant to handle every situation. The annotations were meant to help you _get up and running_
|
|
as _quickly_ and as _easily_ as possible, especially during development.
|
|
|
|
We hope you will enjoy these new capabilities!
|
|
|
|
include::{basedocdir}/reference/bootstrap-annotations-quickstart.adoc[leveloffset=+1]
|