762 lines
30 KiB
Plaintext
762 lines
30 KiB
Plaintext
[[geode-configuration-declarative]]
|
|
== Declarative Configuration
|
|
:geode-name: {apache-geode-name}
|
|
|
|
|
|
The primary purpose of any software development framework is to help you be productive as quickly and as easily as
|
|
possible and to do so in a reliable manner.
|
|
|
|
As application developers, we want a framework to provide constructs that are both intuitive and familiar so that their
|
|
behaviors are predictable. This provided convenience not only helps you hit the ground running in the right direction
|
|
sooner but increases your focus on the application domain so that you can better understand the problem you are trying
|
|
to solve in the first place. Once the problem domain is well understood, you are more apt to make informed decisions
|
|
about the design, which leads to better outcomes, faster.
|
|
|
|
This is exactly what Spring Boot's auto-configuration provides for you. It enables features, functionality, services
|
|
and supporting infrastructure for Spring applications in a loosely integrated way by using conventions (such as the
|
|
classpath) that ultimately help you keep your attention and focus on solving the problem at hand and not on the plumbing.
|
|
|
|
For example, if you are building a web application, you can include the `org.springframework.boot:spring-boot-starter-web`
|
|
dependency on your application classpath. Not only does Spring Boot enable you to build Spring Web MVC Controllers
|
|
appropriate to your application UC (your responsibility), but it also bootstraps your web application in an embedded
|
|
Servlet container on startup (Spring Boot's responsibility).
|
|
|
|
This saves you from having to handle many low-level, repetitive, and tedious development tasks that are error-prone and
|
|
easy to get wrong when you are trying to solve problems. You need not care how the plumbing works until you need to
|
|
customize something. And, when you do, you are better informed and prepared to do so.
|
|
|
|
It is also equally essential that frameworks, such as Spring Boot, get out of the way quickly when application
|
|
requirements diverge from the provided defaults. This is the beautiful and powerful thing about Spring Boot and why
|
|
it is second to none in its class.
|
|
|
|
Still, auto-configuration does not solve every problem all the time. Therefore, you need to use declarative
|
|
configuration in some cases, whether expressed as bean definitions, in properties, or by some other means. This is so
|
|
that frameworks do not leave things to chance, especially when things are ambiguous. The framework gives you choice.
|
|
|
|
Keeping our goals in mind, this chapter:
|
|
|
|
* Refers you to the SDG annotations covered by SBDG's auto-configuration.
|
|
* Lists all SDG annotations not covered by SBDG's auto-configuration.
|
|
* Covers the SBDG, SSDG and SDG annotations that you must explicitly declare and that provide the most value
|
|
and productivity when getting started with {geode-name} in Spring [Boot] applications.
|
|
|
|
NOTE: SDG refers to {spring-data-geode-website}[Spring Data for {geode-name}]. SSDG refers to
|
|
{spring-session-data-gemfire-website}[Spring Session for {geode-name}]. SBDG refers to
|
|
Spring Boot for {geode-name} (this project).
|
|
|
|
TIP: The list of SDG annotations covered by SBDG's auto-configuration is discussed in detail in the <<appendix>>,
|
|
in the <<geode-auto-configuration-annotations,Auto-configuration vs. Annotation-based configuration>> section.
|
|
|
|
To be absolutely clear about which SDG annotations we are referring to, we mean the SDG annotations in the
|
|
{spring-data-geode-javadoc}/org/springframework/data/gemfire/config/annotation/package-summary.html[`org.springframework.data.gemfire.config.annotation`]
|
|
package.
|
|
|
|
In subsequent sections, we also cover which annotations are added by SBDG.
|
|
|
|
[[geode-configuration-declarative-auto-configuration]]
|
|
=== Auto-configuration
|
|
|
|
We explained auto-configuration in detail in the "<<geode-configuration-auto,Auto-configuration>>" chapter.
|
|
|
|
[[geode-configuration-declarative-annotations]]
|
|
=== Annotations Not Covered by Auto-configuration
|
|
|
|
The following SDG annotations are not implicitly applied by SBDG's auto-configuration:
|
|
|
|
* `@EnableAutoRegionLookup`
|
|
* `@EnableBeanFactoryLocator`
|
|
* `@EnableCacheServer(s)`
|
|
* `@EnableCachingDefinedRegions`
|
|
* `@EnableClusterConfiguration`
|
|
* `@EnableClusterDefinedRegions`
|
|
* `@EnableCompression`
|
|
* `@EnableDiskStore(s)`
|
|
* `@EnableEntityDefinedRegions`
|
|
* `@EnableEviction`
|
|
* `@EnableExpiration`
|
|
* `@EnableGatewayReceiver`
|
|
* `@EnableGatewaySender(s)`
|
|
* `@EnableGemFireAsLastResource`
|
|
* `@EnableGemFireMockObjects`
|
|
* `@EnableHttpService`
|
|
* `@EnableIndexing`
|
|
* `@EnableOffHeap`
|
|
* `@EnableLocator`
|
|
* `@EnableManager`
|
|
* `@EnableMemcachedServer`
|
|
* `@EnablePool(s)`
|
|
* `@EnableRedisServer`
|
|
* `@EnableStatistics`
|
|
* `@UseGemFireProperties`
|
|
|
|
NOTE: This content was also covered in <<geode-autoconfiguration-annotations-explicit>>.
|
|
|
|
One reason SBDG does not provide auto-configuration for several of the annotations is because the annotations
|
|
are server-specific:
|
|
|
|
* `@EnableCacheServer(s)`
|
|
* `@EnableGatewayReceiver`
|
|
* `@EnableGatewaySender(s)`.
|
|
* `@EnableHttpService`
|
|
* `@EnableLocator`
|
|
* `@EnableManager`
|
|
* `@EnableMemcachedServer`
|
|
* `@EnableRedisServer`
|
|
|
|
Also, we <<geode-clientcache-applications,already stated>> that SBDG is opinionated about providing a `ClientCache`
|
|
instance.
|
|
|
|
Other annotations are driven by need, including:
|
|
|
|
* `@EnableAutoRegionLookup` and `@EnableBeanFactoryLocator`: Really useful only when mixing configuration metadata
|
|
formats, such as Spring config with {geode-name} `cache.xml`. This is usually the case only if you have legacy
|
|
`cache.xml` config to begin with. Otherwise, you should not use these annotations.
|
|
* `@EnableCompression`: Requires the Snappy Compression Library to be on your application classpath.
|
|
* `@EnableDiskStore(s)` Used only for overflow and persistence.
|
|
* `@EnableOffHeap`: Enables data to be stored in main memory, which is useful only when your application data (that is,
|
|
objects stored in {geode-name}) are generally uniform in size.
|
|
* `@EnableGemFireAsLastResource`: Needed only in the context of JTA Transactions.
|
|
* `@EnableStatistics`: Useful if you need runtime metrics. However, enabling statistics gathering does consume
|
|
considerable system resources (CPU & Memory).
|
|
|
|
Still other annotations require more careful planning:
|
|
|
|
* `@EnableEviction`
|
|
* `@EnableExpiration`
|
|
* `@EnableIndexing`
|
|
|
|
One annotation is used exclusively for unit testing:
|
|
|
|
* `@EnableGemFireMockObjects`
|
|
|
|
The bottom-line is that a framework should not auto-configure every possible feature, especially when the features
|
|
consume additional system resources or require more careful planning (as determined by the use case).
|
|
|
|
However, all of these annotations are available for the application developer to use when needed.
|
|
|
|
[[geode-configuration-declarative-annotations-productivity]]
|
|
=== Productivity Annotations
|
|
|
|
This section calls out the annotations we believe to be most beneficial for your application development purposes when
|
|
using {geode-name} in Spring [Boot] applications.
|
|
|
|
[[geode-configuration-declarative-annotations-productivity-enableclusteraware]]
|
|
==== `@EnableClusterAware` (SBDG)
|
|
|
|
The `@EnableClusterAware` annotation is arguably the most powerful and valuable annotation.
|
|
|
|
.Declaring `@EnableClusterAware`
|
|
====
|
|
[source,java]
|
|
----
|
|
@SpringBootApplication
|
|
@EnableClusterAware
|
|
class SpringBootApacheGeodeClientCacheApplication { }
|
|
----
|
|
====
|
|
|
|
When you annotate your main `@SpringBootApplication` class with `@EnableClusterAware`, your Spring Boot, {geode-name}
|
|
`ClientCache` application is able to seamlessly switch between client/server and local-only topologies with no code
|
|
or configuration changes, regardless of the runtime environment (such as local/standalone versus cloud-managed
|
|
environments).
|
|
|
|
When a cluster of {geode-name} servers is detected, the client application sends and receives data to and from the
|
|
{geode-name} cluster. If a cluster is not available, the client automatically switches to storing data locally on the
|
|
client by using `LOCAL` Regions.
|
|
|
|
Additionally, the `@EnableClusterAware` annotation is meta-annotated with SDG's
|
|
{spring-data-geode-javadoc}/org/springframework/data/gemfire/config/annotation/EnableClusterConfiguration.html[`@EnableClusterConfiguration`]
|
|
annotation.
|
|
|
|
The `@EnableClusterConfiguration` annotation lets configuration metadata defined on the client (such as Region and Index
|
|
definitions, as needed by the application based on requirements and use cases) be sent to the cluster of servers. If
|
|
those schema objects are not already present, they are created by the servers in the cluster in such a way that the
|
|
servers remember the configuration on restart as well as provide the configuration to new servers that join the cluster
|
|
when it is scaled out. This feature is careful not to stomp on any existing Region or Index objects already defined on
|
|
the servers, particularly since you may already have critical data stored in the Regions.
|
|
|
|
The primary motivation for the `@EnableClusterAware` annotation is to let you switch environments with minimal effort.
|
|
It is a common development practice to debug and test your application locally (in your IDE) and then push up to a
|
|
production-like (staging) environment for more rigorous integration testing.
|
|
|
|
By default, the configuration metadata is sent to the cluster by using a non-secure HTTP connection. However, you can
|
|
configure HTTPS, change the host and port, and configure the data management policy used by the servers when creating
|
|
Regions.
|
|
|
|
TIP: See the section in the SDG reference documentation on
|
|
{spring-data-geode-docs-html}/#bootstrap-annotation-config-cluster[Configuring Cluster Configuration Push]
|
|
for more details.
|
|
|
|
[[geode-configuration-declarative-annotations-productivity-enableclusteraware-strictmatch]]
|
|
===== @EnableClusterAware, strictMatch
|
|
|
|
The `strictMatch` attribute has been added to the `@EnableClusterAware` annotation to enable fail-fast behavior.
|
|
`strictMatch` is set to `false` by default.
|
|
|
|
Essentially, when you set `strictMatch` to `true`, your Spring Boot, {geode-name} `ClientCache` application requires
|
|
an {geode-name} cluster to exist. That is, the application requires a client/server topology to operate, and the
|
|
application should fail to start if a cluster is not present. The application should not startup in a local-only
|
|
capacity.
|
|
|
|
When `strictMatch` is set to `true` and an {geode-name} cluster is not available, your Spring Boot, {geode-name}
|
|
`ClientCache` application fails to start with a `ClusterNotFoundException`. The application does not attempt to
|
|
start in a local-only capacity.
|
|
|
|
You can explicitly set the `strictMatch` attribute programmatically by using the `@EnableClusterAware` annotation:
|
|
|
|
.Set `@EnableClusterAware.strictMatch`
|
|
====
|
|
[source,java]
|
|
----
|
|
@SpringBootApplication
|
|
@EnableClusterAware(strictMatch = true)
|
|
class SpringBootApacheGeodeClientCacheApplication { }
|
|
----
|
|
====
|
|
|
|
Alternatively, you can set `strictMatch` attribute by using the corresponding property
|
|
in Spring Boot `application.properties`:
|
|
|
|
.Set `strictMatch` using a property
|
|
====
|
|
[source,properties]
|
|
----
|
|
# Spring Boot application.properties
|
|
|
|
spring.boot.data.gemfire.cluster.condition.match.strict=true
|
|
----
|
|
====
|
|
|
|
This is convenient when you need to apply this configuration setting conditionally, based on a Spring profile.
|
|
|
|
When you adjust the log level of the `org.springframework.geode.config.annotation.ClusterAwareConfiguration` logger
|
|
to `INFO`, you get more details from the `@EnableClusterAware` functionality when applying the logic to determine
|
|
the presence of an {geode-name} cluster, such as which explicitly or implicitly configured connections were successful.
|
|
|
|
The following example shows typical output:
|
|
|
|
.`@EnableClusterAware` INFO log output
|
|
====
|
|
[source,txt]
|
|
----
|
|
2021-01-20 14:02:28,740 INFO fig.annotation.ClusterAwareConfiguration: 476 - Failed to connect to localhost[40404]
|
|
2021-01-20 14:02:28,745 INFO fig.annotation.ClusterAwareConfiguration: 476 - Failed to connect to localhost[10334]
|
|
2021-01-20 14:02:28,746 INFO fig.annotation.ClusterAwareConfiguration: 470 - Successfully connected to localhost[57649]
|
|
2021-01-20 14:02:28,746 INFO fig.annotation.ClusterAwareConfiguration: 576 - Cluster was found; Auto-configuration made [1] successful connection(s);
|
|
2021-01-20 14:02:28,746 INFO fig.annotation.ClusterAwareConfiguration: 586 - Spring Boot application is running in a client/server topology, using a standalone Apache Geode-based cluster
|
|
----
|
|
====
|
|
|
|
NOTE: An attempt is always made to connect to `localhost` on the default `Locator` port, `10334`, and the default
|
|
`CacheServer` port, `40404`.
|
|
|
|
TIP: You can force a successful match by setting the `spring.boot.data.gemfire.cluster.condition.match` property
|
|
to `true` in Spring Boot `application.properties`. This is sometimes useful for testing purposes.
|
|
|
|
[[geode-configuration-declarative-annotations-productivity-regions]]
|
|
==== `@EnableCachingDefinedRegions`, `@EnableClusterDefinedRegions` and `@EnableEntityDefinedRegions` (SDG)
|
|
|
|
These annotations are used to create Regions in the cache to manage your application data.
|
|
|
|
You can create Regions by using Java configuration and the Spring API as follows:
|
|
|
|
.Creating a Region with Spring JavaConfig
|
|
====
|
|
[source,java]
|
|
----
|
|
@Configuration
|
|
class GeodeConfiguration {
|
|
|
|
@Bean("Customers")
|
|
ClientRegionFactoryBean<Long, Customer> customersRegion(GemFireCache cache) {
|
|
|
|
ClientRegionFactoryBean<Long, Customer> customers =
|
|
new ClientRegionFactoryBean<>();
|
|
|
|
customers.setCache(cache);
|
|
customers.setShortcut(ClientRegionShortcut.PROXY);
|
|
|
|
return customers;
|
|
}
|
|
}
|
|
----
|
|
====
|
|
|
|
You can do the same in XML:
|
|
|
|
.Creating a client Region using Spring XML
|
|
====
|
|
[source,xml]
|
|
----
|
|
<gfe:client-region id="Customers" shorcut="PROXY"/>
|
|
----
|
|
====
|
|
|
|
However, using the provided annotations is far easier, especially during development, when the complete Region
|
|
configuration may be unknown and you want only to create a Region to persist your application data and move on.
|
|
|
|
[[geode-configuration-declarative-annotations-productivity-regions-enablecachingdefined]]
|
|
===== `@EnableCachingDefinedRegions`
|
|
|
|
The `@EnableCachingDefinedRegions` annotation is used when you have application components registered in the Spring
|
|
container that are annotated with Spring or JSR-107 JCache
|
|
{spring-framework-docs}/integration.html#cache-jsr-107[annotations].
|
|
|
|
Caches that are identified by name in the caching annotations are used to create Regions that hold the data
|
|
you want cached.
|
|
|
|
Consider the following example:
|
|
|
|
.Defining Regions based on Spring or JSR-107 JCache Annotations
|
|
====
|
|
[source,java]
|
|
----
|
|
@Service
|
|
class CustomerService {
|
|
|
|
@Cacheable(cacheNames = "CustomersByAccountNumber", key = "#account.number")
|
|
Customer findBy(Account account) {
|
|
// ...
|
|
}
|
|
}
|
|
----
|
|
====
|
|
|
|
Further consider the following example, in which the main `@SpringBootApplication` class is annotated with
|
|
`@EnableCachingDefinedRegions`:
|
|
|
|
.Using `@EnableCachingDefinedRegions`
|
|
====
|
|
[source,java]
|
|
----
|
|
@SpringBootApplication
|
|
@EnableCachingDefineRegions
|
|
class SpringBootApacheGeodeClientCacheApplication { }
|
|
----
|
|
====
|
|
|
|
With this setup, SBDG would create a client `PROXY` Region (or `PARTITION_REGION` if your application were a peer member
|
|
of the {geode-name} cluster) with a name of "`CustomersByAccountNumber`", as though you created the Region by using
|
|
either the Java configuration or XML approaches shown earlier.
|
|
|
|
You can use the `clientRegionShortcut` or `serverRegionShortcut` attribute to change the data management policy of
|
|
the Regions created on the client or servers, respectively.
|
|
|
|
For client Regions, you can also set the `poolName` attribute to assign a specific `Pool` of connections to be used by
|
|
the client `*PROXY` Regions to send data to the cluster.
|
|
|
|
[[geode-configuration-declarative-annotations-productivity-regions-enableentitydefined]]
|
|
===== `@EnableEntityDefinedRegions`
|
|
|
|
As with `@EnableCachingDefinedRegions`, `@EnableEntityDefinedRegions` lets you create Regions based on the entity
|
|
classes you have defined in your application domain model.
|
|
|
|
For instance, consider an entity class annotated with SDG's
|
|
{spring-data-geode-javadoc}/org/springframework/data/gemfire/mapping/annotation/Region.html[`@Region`]
|
|
mapping annotation:
|
|
|
|
.Customer entity class annotated with `@Region`
|
|
====
|
|
[source,java]
|
|
----
|
|
@Region("Customers")
|
|
class Customer {
|
|
|
|
@Id
|
|
private Long id;
|
|
|
|
@Indexed
|
|
private String name;
|
|
|
|
}
|
|
----
|
|
====
|
|
|
|
For this class, SBDG creates Regions from the name specified in the `@Region` mapping annotation on the entity class.
|
|
In this case, the `Customer` application-defined entity class results in the creation of a Region named "`Customers`"
|
|
when the main `@SpringBootApplication` class is annotated with `@EnableEntityDefinedRegions`:
|
|
|
|
.Using `@EnableEntityDefinedRegions`
|
|
====
|
|
[source,java]
|
|
----
|
|
@SpringBootApplication
|
|
@EnableEntityDefinedRegions(basePackageClasses = Customer.class,
|
|
clientRegionShortcut = ClientRegionShortcut.CACHING_PROXY)
|
|
class SpringBootApacheGeodeClientCacheApplication { }
|
|
----
|
|
====
|
|
|
|
As with the `@EnableCachingDefinedRegions` annotation, you can set the client and server Region data management policy
|
|
by using the `clientRegionShortcut` and `serverRegionShortcut` attributes, respectively, and set a dedicated `Pool`
|
|
of connections used by client Regions with the `poolName` attribute.
|
|
|
|
However, unlike the `@EnableCachingDefinedRegions` annotation, you must specify either the `basePackage` attribute
|
|
or the type-safe `basePackageClasses` attribute (recommended) when you use the `@EnableEntityDefinedRegions` annotation.
|
|
|
|
Part of the reason for this is that `@EnableEntityDefinedRegions` performs a component scan for the entity classes
|
|
defined by your application. The component scan loads each class to inspect the annotation metadata for that class.
|
|
This is not unlike the JPA entity scan when working with JPA providers, such as Hibernate.
|
|
|
|
Therefore, it is customary to limit the scope of the scan. Otherwise, you end up potentially loading many classes
|
|
unnecessarily. After all, the JVM uses dynamic linking to load classes only when needed.
|
|
|
|
Both the `basePackages` and `basePackageClasses` attributes accept an array of values. With `basePackageClasses`, you
|
|
need only refer to a single class type in that package and every class in that package as well as classes in the
|
|
sub-packages are scanned to determine if the class type represents an entity. A class type is an entity if it is
|
|
annotated with the `@Region` mapping annotation. Otherwise, it is not considered to be an entity.
|
|
|
|
For example, suppose you had the following structure:
|
|
|
|
.Entity Scan
|
|
====
|
|
[source,txt]
|
|
----
|
|
- example.app.crm.model
|
|
|- Customer.class
|
|
|- NonEntity.class
|
|
|- contact
|
|
|- Address.class
|
|
|- PhoneNumber.class
|
|
|- AnotherNonEntity.class
|
|
- example.app.accounts.model
|
|
|- Account.class
|
|
...
|
|
..
|
|
.
|
|
----
|
|
====
|
|
|
|
Then you could configure the `@EnableEntityDefinedRegions` as follows:
|
|
|
|
.Targeting with `@EnableEntityDefinedRegions`
|
|
====
|
|
[source,java]
|
|
----
|
|
@SpringBootApplication
|
|
@EnableEntityDefinedRegions(basePackageClasses = { NonEntity.class, Account.class } )
|
|
class SpringBootApacheGeodeClientCacheApplication { }
|
|
----
|
|
====
|
|
|
|
If `Customer`, `Address`, `PhoneNumber` and `Account` were all entity classes properly annotated with `@Region`,
|
|
the component scan would pick up all these classes and create Regions for them. The `NonEntity` class serves only as
|
|
a marker in this case, to point to where (that is, which package) the scan should begin.
|
|
|
|
Additionally, the `@EnableEntityDefinedRegions` annotation provides include and exclude filters, the same as
|
|
the core Spring Frameworks `@ComponentScan` annotation.
|
|
|
|
TIP: See the SDG reference documentation on
|
|
{spring-data-geode-docs-html}/#bootstrap-annotation-config-regions[Configuring Regions]
|
|
for more details.
|
|
|
|
[[geode-configuration-declarative-annotations-productivity-regions-enableclusterdefined]]
|
|
===== `@EnableClusterDefinedRegions`
|
|
|
|
Sometimes, it is ideal or even necessary to pull configuration from the cluster (rather than push configuration to the
|
|
cluster). That is, you want the Regions defined on the servers to be created on the client and used by your application.
|
|
|
|
To do so, annotate your main `@SpringBootApplication` class with `@EnableClusterDefinedRegions`:
|
|
|
|
.Using `@EnableClusterDefinedRegions`
|
|
====
|
|
[source,java]
|
|
----
|
|
@SpringBootApplication
|
|
@EnableClusterDefinedRegions
|
|
class SpringBootApacheGeodeClientCacheApplication { }
|
|
----
|
|
====
|
|
|
|
Every Region that exists on the servers in the {geode-name} cluster will have a corresponding `PROXY` Region defined
|
|
and created on the client as a bean in your Spring Boot application.
|
|
|
|
If the cluster of servers defines a Region called "`ServerRegion`", you can inject a client `PROXY` Region with
|
|
the same name ("`ServerRegion`") into your Spring Boot application:
|
|
|
|
.Using a server-side Region on the client
|
|
====
|
|
[source,java]
|
|
----
|
|
@Component
|
|
class SomeApplicationComponent {
|
|
|
|
@Resource(name = "ServerRegion")
|
|
private Region<Integer, EntityType> serverRegion;
|
|
|
|
public void someMethod() {
|
|
|
|
EntityType entity = new EntityType();
|
|
|
|
this.serverRegion.put(1, entity);
|
|
|
|
// ...
|
|
}
|
|
}
|
|
----
|
|
====
|
|
|
|
SBDG auto-configures a `GemfireTemplate` for the "`ServerRegion`" Region
|
|
(see <<geode-configuration-declarative-auto-configuration-regiontemplates>>),
|
|
so a better way to interact with the client `PROXY` Region that corresponds to the "`ServerRegion`" Region on the server
|
|
is to inject the template:
|
|
|
|
.Using a server-side Region on the client with a template
|
|
====
|
|
[source,java]
|
|
----
|
|
@Component
|
|
class SomeApplicationComponent {
|
|
|
|
@Autowired
|
|
@Qualifier("serverRegionTemplate")
|
|
private GemfireTemplate serverRegionTemplate;
|
|
|
|
public void someMethod() {
|
|
|
|
EntityType entity = new EntityType();
|
|
|
|
this.serverRegionTemplate.put(1, entity);
|
|
|
|
//...
|
|
}
|
|
}
|
|
----
|
|
====
|
|
|
|
TIP: See the SDG reference documentation on
|
|
{spring-data-geode-docs-html}/#bootstrap-annotation-config-region-cluster-defined[Configuring Cluster-defined Regions]
|
|
for more details.
|
|
|
|
[[geode-configuration-declarative-annotations-productivity-enableindexing]]
|
|
==== `@EnableIndexing` (SDG)
|
|
|
|
You can also use the `@EnableIndexing` annotation -- but only when you use `@EnableEntityDefinedRegions`. This is
|
|
because `@EnableIndexing` requires the entities to be scanned and analyzed for mapping metadata (defined on the class
|
|
type of the entity). This includes annotations such as the Spring Data Commons `@Id` annotation and the annotations
|
|
provided by SDG, such as `@Indexed` and `@LuceneIndexed`.
|
|
|
|
The `@Id` annotation identifies the (primary) key of the entity. The `@Indexed` annotation defines OQL indexes on object
|
|
fields, which can be used in the predicates of your OQL queries. The `@LuceneIndexed` annotation is used to define the
|
|
Apache Lucene Indexes required for searches.
|
|
|
|
NOTE: Lucene Indexes can only be created on `PARTITION` Regions, and `PARTITION` Regions can only be defined on
|
|
the server side.
|
|
|
|
You may have noticed that the `Customer` entity class's `name` field was annotated with `@Indexed`.
|
|
Consider the following listing:
|
|
|
|
Consider the following listing:
|
|
|
|
.Customer entity class with `@Indexed` annotated `name` field
|
|
====
|
|
[source,java]
|
|
----
|
|
@Region("Customers")
|
|
class Customer {
|
|
|
|
@Id
|
|
private Long id;
|
|
|
|
@Indexed
|
|
private String name;
|
|
|
|
}
|
|
----
|
|
====
|
|
|
|
As a result, when our main `@SpringBootApplication` class is annotated with `@EnableIndexing`, an {geode-name} OQL Index
|
|
for the `Customer.name` field is created, allowing OQL queries on customers by name to use this Index:
|
|
|
|
.Using `@EnableIndexing`
|
|
====
|
|
[source,java]
|
|
----
|
|
@SpringBootApplication
|
|
@EnableEntityDefinedRegions(basePackageClasses = Customer.class)
|
|
@EnableIndexing
|
|
class SpringBootApacheGeodeClientCacheApplication { }
|
|
----
|
|
====
|
|
|
|
NOTE: Keep in mind that OQL Indexes are not persistent between restarts (that is, {geode-name} maintains Indexes
|
|
in memory only). An OQL Index is always rebuilt when the node is restarted.
|
|
|
|
When you combine `@EnableIndexing` with either `@EnableClusterConfiguration` or `@EnableClusterAware`, the Index
|
|
definitions are pushed to the server-side Regions where OQL queries are generally executed.
|
|
|
|
TIP: See the SDG reference documentation on
|
|
{spring-data-geode-docs-html}/#bootstrap-annotation-config-region-indexes[Configuring Indexes]
|
|
for more details.
|
|
|
|
[[geode-configuration-declarative-annotations-productivity-enableexpiration]]
|
|
==== `@EnableExpiration` (SDG)
|
|
|
|
It is often useful to define both eviction and expiration policies, particularly with a system like {geode-name},
|
|
because it primarily keeps data in memory (on the JVM Heap). Your data volume size may far exceed the amount of
|
|
available JVM Heap memory, and keeping too much data on the JVM Heap can cause Garbage Collection (GC) issues.
|
|
|
|
TIP: You can enable off-heap (or main memory usage) capabilities by declaring SDG's `@EnableOffHeap` annotation.
|
|
See the SDG reference documentation on
|
|
{spring-data-geode-docs-html}/#bootstrap-annotation-config-region-off-heap[Configuring Off-Heap Memory]
|
|
for more details.
|
|
|
|
Defining eviction and expiration policies lets you limit what is kept in memory and for how long.
|
|
|
|
While {spring-data-geode-docs-html}/#bootstrap-annotation-config-region-eviction[configuring eviction] is easy with SDG,
|
|
we particularly want to call out expiration since
|
|
{spring-data-geode-docs-html}/#bootstrap-annotation-config-region-expiration[configuring expiration] has special support
|
|
in SDG.
|
|
|
|
With SDG, you can define the expiration policies associated with a particular application class type on the class type
|
|
itself, by using the {spring-data-geode-javadoc}/org/springframework/data/gemfire/expiration/Expiration.html[`@Expiration`],
|
|
{spring-data-geode-javadoc}/org/springframework/data/gemfire/expiration/IdleTimeoutExpiration.html[`@IdleTimeoutExpiration`]
|
|
and {spring-data-geode-javadoc}/org/springframework/data/gemfire/expiration/TimeToLiveExpiration.html[`@TimeToLiveExpiration`]
|
|
annotations.
|
|
|
|
TIP: See the {geode-name} {apache-geode-docs}/developing/expiration/how_expiration_works.html[User Guide]
|
|
for more details on the different expiration types -- that is _Idle Timeout_ (TTI) versus _Time-to-Live_ (TTL).
|
|
|
|
For example, suppose we want to limit the number of `Customers` maintained in memory for a period of time (measured in
|
|
seconds) based on the last time a `Customer` was accessed (for example, the last time a `Customer` was read). To do so,
|
|
we can define an idle timeout expiration (TTI) policy on our `Customer` class type:
|
|
|
|
.Customer entity class with Idle Timeout Expiration (TTI)
|
|
====
|
|
[source,java]
|
|
----
|
|
@Region("Customers")
|
|
@IdleTimeoutExpiration(action = "INVALIDATE", timeout = "300")
|
|
class Customer {
|
|
|
|
@Id
|
|
private Long id;
|
|
|
|
@Indexed
|
|
private String name;
|
|
|
|
}
|
|
----
|
|
====
|
|
|
|
The `Customer` entry in the `Customers` Region is `invalidated` after 300 seconds (5 minutes).
|
|
|
|
To enable annotation-based expiration policies, we need to annotate our main `@SpringBootApplication` class
|
|
with `@EnableExpiration`:
|
|
|
|
.Enabling Expiration
|
|
====
|
|
[source,java]
|
|
----
|
|
@SpringBootApplication
|
|
@EnableExpiration
|
|
class SpringBootApacheGeodeApplication { }
|
|
----
|
|
====
|
|
|
|
NOTE: Technically, this entity-class-specific annotation-based expiration policy is implemented by using {geode-name}'s
|
|
{apache-geode-javadoc}/org/apache/geode/cache/CustomExpiry.html[`CustomExpiry`] interface.
|
|
|
|
TIP: See the SDG reference doccumentation for more details on
|
|
{spring-data-geode-docs-html}/#bootstrap-annotation-config-region-expiration[configuring expiration], along with
|
|
{spring-data-geode-docs-html}/#bootstrap:region:expiration:annotation[annotation-based data expiration] in particular.
|
|
|
|
[[geode-configuration-declarative-annotations-productivity-enablemockobjects]]
|
|
==== `@EnableGemFireMockObjects` (STDG)
|
|
|
|
Software testing in general and unit testing in particular are a very important development tasks to ensure the quality
|
|
of your Spring Boot applications.
|
|
|
|
{geode-name} can make testing difficult in some cases, especially when tests have to be written as integration tests
|
|
to assert the correct behavior. This can be very costly and lengthens the feedback cycle. Fortunately, you can write
|
|
unit tests as well.
|
|
|
|
Spring provides a framework for testing Spring Boot applications that use {geode-name}. This is where the
|
|
{spring-test-data-gemfire-website}[Spring Test for {geode-name} (STDG)] project can help, particularly with
|
|
unit testing.
|
|
|
|
For example, if you do not care what {geode-name} would actually do in certain cases and only care about the "`contract`",
|
|
which is what mocking a collaborator is all about, you could effectively mock {geode-name} objects to isolate the SUT,
|
|
or "`Subject Under Test`", and focus on the interactions or outcomes you expect to happen.
|
|
|
|
With STDG, you need not change a bit of configuration to enable mock objects in the unit tests for your Spring Boot
|
|
applications. You need only annotate the test class with `@EnableGemFireMockObjects`:
|
|
|
|
.Using Mock {geode-name} Objects
|
|
====
|
|
[source,java]
|
|
----
|
|
@RunWith(SpringRunner.class)
|
|
@SpringBootTest
|
|
class MyApplicationTestClass {
|
|
|
|
@Test
|
|
public void someTestCase() {
|
|
// ...
|
|
}
|
|
|
|
@Configuration
|
|
@EnableGemFireMockObjects
|
|
static class GeodeConfiguration { }
|
|
|
|
}
|
|
----
|
|
====
|
|
|
|
Your Spring Boot configuration of {geode-name} returns mock objects for all {geode-name} objects, such as Regions.
|
|
|
|
Mocking {geode-name} objects even works for objects created from the productivity annotations discussed in the previous
|
|
sections.
|
|
|
|
For example, consider the following Spring Boot, {geode-name} `ClientCache` application class:
|
|
|
|
.Main `@SpringBootApplication` class under test
|
|
====
|
|
[source,java]
|
|
----
|
|
@SpringBootApplication
|
|
@EnableEntityDefinedRegions(basePackageClasses = Customer.class)
|
|
class SpringBootApacheGeodeClientCacheApplication { }
|
|
----
|
|
====
|
|
|
|
In the preceding example, the `"Customers`" Region defined by the `Customer` entity class and created by
|
|
the `@EnableEntityDefinedRegions` annotation would be a mock Region and not an actual Region. You can still inject
|
|
the Region in your test and assert interactions on the Region based on your application workflows:
|
|
|
|
.Using Mock {geode-name} Objects
|
|
====
|
|
[source,java]
|
|
----
|
|
@RunWith(SpringRunner.class)
|
|
@SpringBootTest
|
|
class MyApplicationTestClass {
|
|
|
|
@Resource(name = "Customers")
|
|
private Region<Long, Customer> customers;
|
|
|
|
@Test
|
|
public void someTestCase() {
|
|
|
|
Customer jonDoe = new Customer(1, "Jon Doe");
|
|
|
|
// Use the application in some way and test the interaction on the "Customers" Region
|
|
|
|
assertThat(this.customers).containsValue(jonDoe);
|
|
|
|
// ...
|
|
}
|
|
}
|
|
----
|
|
====
|
|
|
|
There are many more things that STDG can do for you in both unit testing and integration testing.
|
|
|
|
See the https://github.com/spring-projects/spring-test-data-geode#unit-testing-with-stdg[documentation on unit testing]
|
|
for more details.
|
|
|
|
You can https://github.com/spring-projects/spring-test-data-geode#integration-testing-with-stdg[write integration tests]
|
|
that use STDG as well. Writing integration tests is an essential concern when you need to assert whether your
|
|
application OQL queries are well-formed, for instance. There are many other valid cases where integration testing
|
|
is also applicable.
|