Files
spring-data-geode/src/main/asciidoc/reference/bootstrap-annotations.adoc

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]