diff --git a/src/main/asciidoc/reference/bootstrap-annotations.adoc b/src/main/asciidoc/reference/bootstrap-annotations.adoc index fca8d79d..8187beeb 100644 --- a/src/main/asciidoc/reference/bootstrap-annotations.adoc +++ b/src/main/asciidoc/reference/bootstrap-annotations.adoc @@ -1,24 +1,25 @@ [[bootstrap-annotation-config]] = Bootstrapping Apache Geode using Spring Annotations -_Spring Data Geode_ (SDG) 2.0 introduces a **new** Annotation-based configuration model to bootstrap Apache Geode -with the Spring container. +_Spring Data for Apache Geode_ (SDG) 2.0 introduces a **new** Annotation-based configuration model +to bootstrap and configure Apache Geode using the Spring container. The primary motivation for introducing an Annotation-based approach to the configuration of Apache Geode in -a Spring context is to enable application developers to _**get up and running** as **quickly** and as **easily** -as possible_. +a Spring context is to enable application developers to _**get up and running** as **quickly** +and as **easily** as possible_. [[bootstrap-annotation-config-introduction]] == Introduction -Apache Geode can be very difficult to setup and use successfully given all the +Apache Geode can be very difficult to setup and use correctly given all the http://geode.apache.org/docs/guide/12/reference/topics/gemfire_properties.html[configuration properties], configuration options -(http://geode.apache.org/docs/guide/12/reference/topics/chapter_overview_cache_xml.html[cache.xml], +(http://geode.apache.org/releases/latest/javadoc/index.html[Java API] +http://geode.apache.org/docs/guide/12/reference/topics/chapter_overview_cache_xml.html[cache.xml], http://geode.apache.org/docs/guide/12/tools_modules/gfsh/chapter_overview.html[_Gfsh_] -+ http://geode.apache.org/docs/guide/12/configuring/chapter_overview.html[_Cluster Configuration_], +with http://geode.apache.org/docs/guide/12/configuring/chapter_overview.html[_Cluster Configuration_], <>) -along with different supported _topologies_ +in addition to different supported _topologies_ (http://geode.apache.org/docs/guide/12/topologies_and_comm/cs_configuration/chapter_overview.html[client/server], http://geode.apache.org/docs/guide/12/topologies_and_comm/p2p_configuration/chapter_overview.html[P2P], http://geode.apache.org/docs/guide/12/topologies_and_comm/multi_site_configuration/chapter_overview.html[WAN]) @@ -27,22 +28,22 @@ and https://cwiki.apache.org/confluence/display/GEODE/Geode+Internal+Architectur The Annotation-based configuration model is an alternative to XML-based configuration using _Spring Data Geode's_ XML Namespace. With XML, an application developer would use the `spring-gemfire` (`gfe`) schema for configuration -and the `spring-data-gemfire` (`gfe-data`) schema for data access related concerns. See <> for more details. NOTE: As of SDG 2.0, the new Annotation-based configuration model does not yet have configuration support for Apache Geode's WAN components and topology. Like _Spring Boot_, _Spring Data Geode's_ Annotation-based configuration model was designed as an opinionated, -_convention over configuration_ approach for using Apache Geode. Indeed, the Annotation-based configuration model +_convention over configuration_ approach for using Apache Geode. Indeed, this Annotation-based configuration model was inspired by _Spring Boot_ as well as several other Spring and _Spring Data_ projects. -By following convention, all Annotations provide reasonable and sensible defaults for all the attributes out-of-the-box. -The default value for a given Annotation attribute directly corresponds to the default value provided in Apache Geode -for the same configuration property or setting. +By following convention, all Annotations provide reasonable and sensible defaults for all configuration attributes +out-of-the-box. The default value for a given Annotation attribute directly corresponds to the default value +provided in Apache Geode for the same configuration property or setting. The intention is to let an application developer enable an Apache Geode feature or an embedded service by simply -declaring the Annotation on his/her Spring `@Configuration` or `@SpringBootApplcation` class without needing to +declaring the Annotation on his/her Spring `@Configuration` or `@SpringBootApplication` class without needing to unnecessarily configure a large number of attributes or properties just to use the feature. Again, _getting up and running as quickly and as easily as possible_ is the primary objective. @@ -76,20 +77,20 @@ The _client/server_ topology is the most typical system architecture employed wh can make her _Spring Boot_ application a cache client simply by annotating it with `@ClientCacheApplication`. Alternatively, a _Spring Boot_ application may be a peer member of an Apache Geode cluster. That is, the application -itself is just another server in the cluster of servers that will manage data. The application creates an "embedded" -peer `Cache` instance when a developer annotates his/her application class with `@PeerCacheApplication`. +itself is just another server in the cluster of servers that will manage data. The _Spring Boot_ application creates +an "embedded" peer `Cache` instance when a developer annotates his/her application class with `@PeerCacheApplication`. -By extension, the application may also serve as a `CacheServer` for cache clients, allowing clients to connect +By extension, the application may also serve as a `CacheServer` serving cache clients, allowing 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 will create a peer `Cache` instance along with +`@CacheServerApplication` instead of `@PeerCacheApplication`, which will create a peer `Cache` instance along with the `CacheServer`. -NOTE: An Apache Geode Server is not necessarily a "_Cache Server_" by default. That is, it is not necessarily setup to -service cache clients just because it is a "server". A Geode Server can just be a peer member/data node of the cluster -that manages data without servicing any clients while other peer members in the cluster are setup to service -clients in addition to managing data. It also possible to setup certain peer members as non-data node, +NOTE: An Apache Geode Server is not necessarily a "_Cache Server_" by default. That is, a server is not necessarily +setup to serve cache clients just because it is a "server". An Apache Geode Server can just be a peer member/data node +of the cluster that manages data without serving any clients while other peer members in the cluster are indeed setup +to serve clients in addition to managing data. It also possible to setup certain peer members as non-data node, http://geode.apache.org/docs/guide/12/developing/region_options/data_hosts_and_accessors.html[data accessors] -that can service clients as `CacheServers` as well, but is well beyond the scope of this document. +that can service clients as `CacheServers` as well, but is beyond the scope of this document. By way of example, if I wanted to create a _Spring Boot_, Apache Geode cache client application, I would start with... @@ -112,9 +113,9 @@ will be a server and peer member of a cluster, or distributed system formed by A class ServerApplication { .. } ---- -Alternatively, a user may use the `@CacheServerApplication` annotation in place of `@PeerCacheApplication`, -which will create both an "embedded" peer `Cache` instance along with a `CacheServer` running on "_localhost_", -listening on the default cache server port, *40404*... +Alternatively, a user may use the `@CacheServerApplication` annotation instead 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*... .Spring-based Apache Geode embedded `CacheServer` Application [source, java] @@ -127,31 +128,31 @@ class ServerApplication { .. } [[bootstrap-annotation-config-client-server-applications]] == Going in-detail on _client/server_ applications -There are multiple ways that a client can connect to and communicate with servers in a Geode cluster. The most common -and recommended approach is to use Apache Geode Locators. +There are multiple ways that a client can connect to and communicate with servers in an Apache Geode cluster. +The most common and recommended approach is to use Apache Geode Locators. -NOTE: A cache client can connect to 1 or more Locators in the Geode cluster instead of directly to a +NOTE: A cache client can connect to 1 or more Locators in the Apache Geode cluster instead of directly to a `CacheServer`. The advantage of using Locators over direct `CacheServer` connections is that Locators provide meta-data -about the cluster to which clients are connected. This meta-data includes information like which servers have the least -amount of load, or which servers contain the data of interests to the client. A Locator also provides fail-over +about the cluster to which clients are connected. This meta-data includes information like which servers contain +the data of interests to the client, or which servers have the least amount of load. A Locator also provides fail-over capabilities in case a `CacheServer` goes down. By enabling the PR single-hop capability in the client `Pool`, -the client is routed directly to the server containing the data the client needs access to. +the client is routed directly to the server containing the data the client needs access to, to obtain the data requested. -NOTE: Locators are also peer members in a cluster. Locators actually constitute what makes up a cluster of Geode nodes; -i.e. all nodes connected by a Locator make up a cluster of peers and new members use Locators to join a cluster +NOTE: Locators are also peer members in a cluster. Locators actually constitute what makes up a cluster of Apache Geode +nodes; i.e. all nodes connected by a Locator make up a cluster of peers and new members use Locators to join a cluster and find other members. Since Apache Geode sets up a "DEFAULT" `Pool` connected to a `CacheServer` running on "_localhost_", listening on port -**40404** by default when a `ClientCache` instance is created, there is nothing special a user need do to utilize -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 all set. +**40404** by default when a `ClientCache` instance is created, and a `CacheServer` listens on port **40404** accepting +connections on all system NICs, there is nothing special a user needs to do to utilize 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. You can even start your servers using _Gfsh's_ `start server` command if you prefer. Your _Spring Boot_ `@ClientCacheApplication` will still connect to the server regardless of how it is started. Although, we think you will prefer to configure and start your servers using the _Spring Data Geode_ approach, with Annotations. -However, as an application developer, you will no doubt want to customize the "DEFAULT" `Pool` setup by Apache Geode +As an application developer, you will no doubt want to customize the "DEFAULT" `Pool` setup by Apache Geode to possibly connect to 1 or more Locators, for instance... .Spring-based Apache Geode `ClientCache` application using Locators @@ -166,7 +167,8 @@ class ClientApplication { .. } ---- Along with the `locators` attribute, the `@ClientCacheApplication` annotation has a `servers` attribute that can be used -to specify 1 or more nested `@Server` annotations that enable the cache client to connect directly to 1 or more servers. +to specify 1 or more nested `@Server` annotations that enable the cache client to connect directly to 1 or more servers, +if necessary. NOTE: You can only use either the `locators` or `servers` attribute, but not both, which is enforced by Apache Geode. @@ -175,7 +177,8 @@ a `ClientCache` instance is created with the `@ClientCacheApplication` annotatio 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 1 annotation of the same type to be declared on a class. +a single class. Java 8 and earlier does not allow more than 1 annotation of the same type to be declared +on a class. .Spring-based Apache Geode `ClientCache` application using multiple named `Pools` [source, java] @@ -219,7 +222,8 @@ class ServerApplication { .. } ---- NOTE: Like `@EnablePools`, `@EnableCacheServers` is a composite annotation for aggregating multiple `@EnableCacheServer` -annotations on a single class. +annotations on a single class. Again, Java 8 and earlier does not allow more than 1 annotation of the same type +to be declared on a class. One thing an observant reader may have noticed is, in all cases, the user is specifying hard-coded values for hostnames, ports as well other configuration-oriented Annotation attributes. This is not ideal when a user's application gets @@ -231,26 +235,26 @@ How does an application developer handle dynamic configuration determined at run == 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 an attribute could be expressed as an `int`, like a port number, then the attribute's type -should be an `int`. +attributes. For example, if the configuration attribute could be expressed as an `int`, like 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_ or _SpEL expressions_ in properties -or attributes of the configuration meta-data when configuring beans in a Spring context. Although, this would require -all Annotation attributes be `Strings` thereby giving up _Type-Safety_; not acceptable! +One of the finer features of Spring is the ability to use _property placeholders_ and/or _SpEL expressions_ +in properties or attributes of the configuration meta-data when configuring beans in a Spring context. +Although, this would require all Annotation attributes to be `Strings` thereby giving up _Type-Safety_; not acceptable! So, _Spring Data Geode_ borrows from another commonly used pattern in Spring, `Configurers`. Many different `Configurer` interfaces are provided out-of-the-box in Spring Web MVC, such as the https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/config/annotation/ContentNegotiationConfigurer.html[`org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer`]. -`Configurers` are a way to allow application developers to receive a callback and customize the configuration of a -component 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. +The `Configurers` design pattern are a way to allow 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. _Spring Data Geode_ provides several `Configurer` callback interfaces to customize different aspects of Annotation-based -configuration meta-data at runtime, before the _Sring_ managed beans that the Annotations create are initialized: +configuration meta-data at runtime, before the _Spring_ managed beans that the Annotations create are initialized: * `ClientCacheConfigurer` * `PeerCacheConfigurer` @@ -262,7 +266,7 @@ configuration meta-data at runtime, before the _Sring_ managed beans that the An * `RegionConfigurer` For example, we can use the `CacheServerConfigurer` and `ClientCacheConfigurer` to customize the port numbers -used by our `CacheServer` and `ClientCache` applications, respectively. +used by our _Spring Boot_ `CacheServer` and `ClientCache` applications, respectively. First, in our server application... @@ -270,7 +274,7 @@ First, in our server application... [source, java] ---- @SpringBootApplication -@CacheServerApplication(name = "SpringApplication", logLevel = "info") +@CacheServerApplication(name = "SpringServerApplication", logLevel = "info") class ServerApplication { @Bean @@ -309,24 +313,27 @@ class ClientApplication { ---- By using the provided `Configurers`, a user is able to receive a callback in order to further customize -the configuration that is enabled by the associated Annotation. +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 context, the bean definition can take advantage -of other Spring container features, such as _property placeholders_, or _SpEL expressions_ in `@Value` annotations +of other Spring container features, such as _property placeholders_, or _SpEL expressions_ using the `@Value` annotation on factory method parameters, and so on. All _Spring Data Geode_-provided `Configurers` take 2 bits of information in the callback: the name of the bean created in the Spring context by the Annotation along with a reference to the `FactoryBean` used by the Annotation to -configure the Geode component (e.g. a `ClientCache` instance with SDG's `ClientCacheFactoryBean`). +create and configure the Geode component (e.g. a `ClientCache` instance is created and configured with +SDG's `ClientCacheFactoryBean`). NOTE: SDG `FactoryBeans` are part of the SDG public API and are what an application developer would use in Spring's https://docs.spring.io/spring/docs/current/spring-framework-reference/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 very same `FactoryBeans` for their configuration. +these very same `FactoryBeans` for their configuration. So, in essence, the Annotations are a facade +and provide an extra layer of abstraction for convenience. -Given a `Configurer` can be declared as a regular bean definition like any other, it is not difficult to imagine +Given a `Configurer` can be declared as a regular bean definition like any other POJO, it is not difficult to imagine a user combining different Spring configuration options, such as the use of _Spring Profiles_ with `Conditions` -as well as other features to create even more sophisticated and flexible configuration. +using both Property Placeholders and SpEL expressions as well as other nifty features to create +even more sophisticated and flexible configuration. However, `Configurers` are not the only option. @@ -334,8 +341,8 @@ However, `Configurers` are not the only option. == 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.`, that can be declared in -_Spring Boot_ `application.properties`. +with a corresponding configuration _property_, prefixed with `spring.data.gemfire.`, that can be declared in a +_Spring Boot_ `application.properties` file. Building on our examples above, the client's `application.properties` would define... @@ -343,17 +350,17 @@ Building on our examples above, the client's `application.properties` would defi [source, java] ---- spring.data.gemfire.cache.log-level=info -spring.data.gemfire.cache.pool.venus.servers=venus[48484] -spring.data.gemfire.cache.pool.venus.max-connections=200 -spring.data.gemfire.cache.pool.venus.min-connections=50 -spring.data.gemfire.cache.pool.venus.ping-interval=15000 -spring.data.gemfire.cache.pool.venus.pr-single-hop-enabled=true -spring.data.gemfire.cache.pool.venus.read-timeout=20000 -spring.data.gemfire.cache.pool.venus.subscription-enabled=true -spring.data.gemfire.cache.pool.saturn.locators=skullbox[20668] -spring.data.gemfire.cache.pool.saturn.subscription-enabled=true -spring.data.gemfire.cache.pool.neptune.servers=saturn[41414],neptune[42424] -spring.data.gemfire.cache.pool.neptune.min-connections=25 +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 ---- And, the server's application.properties would define... @@ -398,7 +405,7 @@ And, the `@CacheServerApplication` class as... class ServerApplication { .. } ---- -The example above illustrates why it is import to "name" your Annotation-based beans (other than it is required +The example above illustrates why it is import to "name" your Annotation-based beans (other than, it is required in certain cases). Doing so makes it possible to reference the bean in a Spring context from XML, properties and even Java. It is even possible to inject Annotation-defined beans into an application class, for whatever purpose; for example... @@ -415,7 +422,7 @@ class MyApplicationComponent { } ---- -Likewise, naming a Annotated-defined bean allows you to code a `Configurer` to customize a specific, "named" bean +Likewise, naming a Annotation-defined bean allows you to code a `Configurer` to customize a specific, "named" bean since the `beanName` is 1 of 2 arguments passed to the callback. Often times, an associated Annotation attribute property takes 2 forms: a "named" property along with @@ -437,7 +444,7 @@ and overrides its own `bind-address`, "Saturn" and "Neptune" inherit from the un `spring.data.gemfire.cache.server.bind-address` property. Refer to an Annotation's _Javadoc_ for which Annotation attributes support property-based configuration, and whether -they support "named" properties over just "default", unnamed properties. +they support "named" properties over just default, "unnamed" properties. [[bootstrap-annotation-config-properties-of-properties]] === `Properties` of `Properties` @@ -480,7 +487,7 @@ as well as by new members joining an existing cluster to find other peers. It is often convenient for application developers as they are developing their _Spring Boot_, _Spring Data Geode_ applications to startup up a small cluster of 2 or 3 Apache Geode servers. Rather than starting a separate Locator -process, a user can simply annotate her `@CacheServerApplication` class with `@EnableLocator`. +process, a user can simply annotate her _Spring Boot_ `@CacheServerApplication` class with `@EnableLocator`. .Spring, Apache Geode `CacheServer` application running an embedded Locator [source, java] @@ -491,11 +498,14 @@ process, a user can simply annotate her `@CacheServerApplication` class with `@E class ServerApplication { .. } ---- -The `@EnableLocator` annotation starts and embedded Locator in the Spring, Apache Geode `CacheServer` application +The `@EnableLocator` annotation starts an embedded Locator in the Spring, Apache Geode `CacheServer` application process running on "_localhost_", listening on the default Locator port **10334**. It is possible to customize the `host` (a.k.a bind address) and `port` that the embedded Locator binds to using the corresponding Annotation attributes. +Additionally, the `@EnableLocator` attributes may be set with the `spring.data.gemfire.locator.host` +and `spring.data.gemfire.locator.port` properties in `application.properties` as well. + Then, it is possible to start other _Spring Boot_, `@CacheServerApplication` enabled applications connecting to this Locator with... @@ -507,9 +517,9 @@ Locator with... class ServerApplication { .. } ---- -You may even combine both application classes shown above into a single class and use your IDE feature to create -different run profile configurations to create and run different instances of the same class with slightly modified -configuration using Java System Properties... +You can even combine both application classes shown above into a single class and use your IDE to create different +run profile configurations to run different instances of the same class with slightly modified configuration using +Java System Properties... .Spring `CacheServer` application running an embedded Locator and connecting to the Locator [source, java] @@ -534,18 +544,19 @@ Then, for each run profile, a user simply sets and changes the following System .IDE run profile configuration [source, java] ---- -spring.data.gemfire.cache.name=SpringCacheServerOne +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 be set with the -`-Dspring.profiles.active=embedded-locator` Java System Property. Then, simply change the `name` and `cache.server.port` -for each of the other run profiles and you'll have yourself a small cluster/distributed system of Geode Servers. +`-Dspring.profiles.active=embedded-locator` Java System Property. Then, simply change the `..name` +and `..cache.server.port` for each of the other run profiles and you'll have yourself a small cluster/distributed system +of Apache Geode Servers running on your local system. Pretty slick! NOTE: The `@EnableLocator` annotation was meant to be a development-time annotation only and not something -an application developer should use in production. It is recommended that Locators be stand-alone, independent -processes in the cluster. +an application developer should use in production. It is strongly recommended that Locators be stand-alone, +independent processes in the cluster. More details on how Apache Geode Locators work can be found http://geode.apache.org/docs/guide/12/topologies_and_comm/topology_concepts/how_member_discovery_works.html[here]. @@ -554,14 +565,18 @@ http://geode.apache.org/docs/guide/12/topologies_and_comm/topology_concepts/how_ === Configuring an embedded Manager An Apache Geode Manager is another peer member/node in the cluster that is responsible for "management" activities. -Management activities include things like creating Regions, Indexes, DiskStores, etc. The Manager allows a JMX-enabled -client (e.g. _Gfsh_ shell tool) to connect to the manager to manage the cluster. It is also possible to connect to -a Manager with JDK provided tools like _JConsole_ or _JVisualVM_, given these are both JMX-enabled clients as well. +Management activities include things like creating Regions, Indexes, DiskStores, etc along with monitoring the runtime +operations and behavior of these components. + +The Manager allows a JMX-enabled client (e.g. the _Gfsh_ shell tool) to connect to the Manager to manage the cluster. +It is also possible to connect to a Manager with JDK provided tools like _JConsole_ or _JVisualVM_, given these are +both JMX-enabled clients as well. Perhaps we would also like to make our Spring `@CacheServerApplication` shown above a Manager as well. Simply annotate -your Spring `@Configurtion` or `@SpringBootApplication` class with `@EnableManager` and you are done. By default, -the Manager binds to "_localhost_" listening on the default Apache Geode Manager port **1099**. Several aspects of -the Manager can be configured with the Annotation attributes or corresponding properties. +your Spring `@Configuration` or `@SpringBootApplication` class with `@EnableManager` and you are in business. + +By default, the Manager binds to "_localhost_" listening on the default Apache Geode Manager port **1099**. +Several aspects of the Manager can be configured with the Annotation attributes or corresponding properties. .Spring `CacheServer` application running an embedded Manager [source, java] @@ -582,7 +597,7 @@ public class ServerApplication { } ---- -With the above class, you can even use _Gfsh_ to connect to this server and manage it. +With the above class, you can even use _Gfsh_ to connect to this server and manage it! [source, java] ---- @@ -608,10 +623,10 @@ SpringCacheServerTwo | 10.99.199.5(SpringCacheServerTwo:14844):1025 SpringCacheServerThree | 10.99.199.5(SpringCacheServerThree:14846):1026 ---- -Because we also have the embedded Locator enabled, we were able to connect indirectly to the Manager through -the Locator. The Locator allows JMX clients to connect and find a Manager node in the cluster. If none exist, -the Locator will assume the role of the Manager. However, if no Locator existed, then we would need to connect -directly to the Manager using... +Because we also have the embedded Locator enabled, we are able to connect indirectly to the Manager through +the Locator. A Locator allows JMX clients to connect and find a Manager node in the cluster. If none exist, +the Locator will assume the role of a Manager. However, if no existing Locator is present, then we would need to +connect directly to the Manager using... .Gfsh `connect` command connecting directly to the Manager [source, java] @@ -620,8 +635,8 @@ gfsh>connect --jmx-manager=localhost[1099] ---- NOTE: Like the `@EnableLocator` annotation, the `@EnableManager` annotation was also meant to be a development-time -only and not something an application developer should use in production. It is recommended that Managers, -like Locators, be stand-alone, independent processes in the cluster. +only annotation and not something an application developer should use in production. It is strongly recommended +that Managers, like Locators, be stand-alone, independent and dedicated processes in the cluster. More details on Apache Geode Management and Monitoring can be found http://geode.apache.org/docs/guide/12/managing/book_intro.html[here]. @@ -637,7 +652,7 @@ the http://geode.apache.org/docs/guide/12/rest_apps/book_intro.html[Developer RE and the http://geode.apache.org/docs/guide/12/tools_modules/pulse/pulse-overview.html[Pulse Monitoring Web Application]. However, to use any of these Apache Geode provided Web applications, you must have a full installation of Apache Geode -installed on your system, and you must set the `GEMFIRE` environment variable to your installation directory. +installed on your system, and you must set the `GEODE_HOME` environment variable to your installation directory. To enable the embedded HTTP server, simply add the `@EnableHttpService` annotation to any `@PeerCacheApplication` or `@CacheServerApplication` annotated class... @@ -654,7 +669,7 @@ 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 configuration as needed. -Follow the links above for more details on HTTP support. +Follow the links above for more details on HTTP support and the services provided. [[bootstrap-annotation-config-embedded-services-memcached]] === Configuring the embedded Memcached Server (Gemcached) @@ -681,9 +696,9 @@ http://geode.apache.org/docs/guide/12/tools_modules/gemcached/chapter_overview.h [[bootstrap-annotation-config-embedded-services-redis]] === Configuring the embedded Redis Server -Apache Geode also implements the Redis Server protocol, which enables Redis clients to communicate with a cluster -of Apache Geode Servers and issue Redis commands. As of this writing, the Redis Server protocol support in Apache Geode -is still experimental. +Apache Geode also implements the Redis Server protocol, which enables Redis clients to connect to and communicate with +a cluster of Apache Geode Servers to issue Redis commands. As of this writing, the Redis Server protocol support +in Apache Geode is still experimental. To enable the embedded Redis Service, simply add the `@EnableRedisServer` annotation to any `@PeerCacheApplication` or `@CacheServerApplication` annotated class... @@ -721,14 +736,17 @@ While the `logLevel` attribute can be specified with all the cache-based applica (e.g. `@ClientCacheApplication(logLevel="info")`), it is easier to customize logging behavior with the `@EnableLogging` annotation. +Additionally, you can specify the `log-level` using the `spring.data.gemfire.logging.level` property +in `application.properties`. + See the `@EnableLogging` annotation _Javadoc_ for more details. [[bootstrap-annotation-config-statistics]] == Configuring Statistics -To gain even deeper insight into Apache Geode during runtime, an application developer can enable _Statistics_. -Gathering statistical data facilitates system analysis and troubleshooting when complex problems occur, which -are often distributed in nature and timing is a factor. +To gain even deeper insight into Apache Geode at runtime, an application developer can enable _Statistics_. +Gathering statistical data facilitates system analysis and troubleshooting when complex problems occur, +which are often distributed in nature and where timing is a crucial factor. When _Statistics_ are enabled, a user can use Apache Geode's http://gemfire.docs.pivotal.io/gemfire/tools_modules/vsd/chapter_overview.html[VSD (_Visual Statistics Display_)] tool @@ -761,13 +779,14 @@ http://gemfire.docs.pivotal.io/gemfire/managing/statistics/chapter_overview.html One of the more powerful features of Apache Geode is http://geode.apache.org/docs/guide/12/developing/data_serialization/gemfire_pdx_serialization.html[PDX Serialization]. -While a complete discussion on PDX is well beyond the scope of this document, serialization using PDX is a much better +While a complete discussion on PDX is beyond the scope of this document, serialization using PDX is a much better alternative to _Java Serialization_, with the following benefits... 1. PDX uses a centralized _Type Registry_ to keep the serialized bytes of an object more compact. 2. PDX is a neutral serialization format allowing both Java and Native Clients to operate on the same data set. -3. PDX supports versioning and allows object fields to be added or removed with affecting applications using either -older or newer versions of the PDX serialized, application domain object types that change, without data loss. +3. PDX supports versioning and allows object fields to be added or removed with affecting existing applications +using either older or newer versions of the PDX serialized, application domain objects that have changed, +and without data loss. 4. PDX allows object fields to be accessed individually or in OQL query projections and predicates without the object needing to be de-serialized first. @@ -775,6 +794,9 @@ In general, serialization in Apache Geode is needed anytime data is transferred peers in a cluster for 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 be +`java.io.Serializable`, which maybe undesirable to impose such restrictions on your application domain model. + To enable PDX, simply annotate your application class with `@EnablePdx`... .Spring `ClientCache` application with PDX enabled @@ -788,20 +810,20 @@ public class ClientApplication { .. } Typically, an application's domain object types will either implement the http://geode.apache.org/releases/latest/javadoc/org/apache/geode/pdx/PdxSerializable.html[`org.apache.geode.pdx.PdxSerializable`] -interface, or an application developer will choose to implement and register the non-invasive +interface, or an application developer will choose to implement and register a non-invasive implementation of the http://geode.apache.org/releases/latest/javadoc/org/apache/geode/pdx/PdxSerializer.html[`org.apache.geode.pdx.PdxSerializer`] -interface to handle the application domain object types that need to be serialized. +interface to handle all the application domain object types that need to be serialized. Unfortunately, Apache Geode only allows one `PdxSerializer` to be registered, which suggests that all application domain object types should be handled by a "single" `PdxSerializer` instance. But, that is a serious anti-pattern -and foolish practice to be sure. +and unmaintainable practice to be sure. Even though only a single `PdxSerializer` instance can be registered with Apache Geode, it makes sense to create a -`PdxSerializer` implementation per application domain object type. +single `PdxSerializer` implementation per application domain object type. By using the https://en.wikipedia.org/wiki/Composite_pattern[Composite Software Design Pattern], the application -developer can provide an implementation of the `PdxSerializer` interface that aggregates of all application -domain object type-specific `PdxSerializer` instances but acts as a single `PdxSerializer` instance, and register it. +developer 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 context and refer to this _Composite_ `PdxSerializer` by bean name in the `@EnablePdx` annotation using the `serializerBeanName` attribute. @@ -824,7 +846,7 @@ public class ClientApplication { It is also possible to declare Apache Geode's http://geode.apache.org/releases/latest/javadoc/org/apache/geode/pdx/ReflectionBasedAutoSerializer.html[`org.apache.geode.pdx.ReflectionBasedAutoSerializer`] -as a bean definition in a Spring context. Alternatively, you can use _Spring Data Geode's_ more robust, +as a bean definition in a Spring context. Alternatively, you should use _Spring Data Geode's_ more robust, https://docs.spring.io/spring-data-gemfire/docs/current/api/org/springframework/data/gemfire/mapping/MappingPdxSerializer.html[`org.springframework.data.gemfire.mapping.MappingPdxSerializer`], which uses _Spring Data_ mapping meta-data and infrastructure applied to the serialization process for more efficient handling than reflection alone. @@ -842,7 +864,7 @@ Of course, the common way to accomplish this in _Java_ is using the _Secure Sock and _Transport Layer Security_ (TLS). To enable SSL, simply annotate your application class with `@EnableSsl` and set the necessary SSL configuration -attributes (e.g. keystores, usernames/passwords, etc)... +attributes or properties (e.g. keystores, usernames/passwords, etc)... .Spring `ClientCache` application with SSL enabled [source, java] @@ -854,7 +876,7 @@ public class ClientApplication { .. } ---- Different Apache Geode components: `GATEWAY`, `HTTP`, `JMX`, `LOCATOR`, `SERVER` can be individually configured -with SSL, or they can all be collectively configured all at once to use SSL using the `CLUSTER` enumerated value. +with SSL, or they can all be collectively configured at once to use SSL using the `CLUSTER` enumerated value. It is easy to specify which Apache Geode components that the SSL configuration settings should applied to using the nested `@EnableSsl` annotation `Component` enum... @@ -889,7 +911,7 @@ when launching your application. TIP: It is recommended that these _Geode Properties_ be set in a `gemfire.properties` file when deploying your application to production. But, at development-time, it can be convenient to set these properties individually, -as needed, for prototyping and/or testing purposes. +as needed, for prototyping and testing purposes. A few examples of some of the less common _Geode Properties_ that a user usually need not worry about include, but are not limited to: `ack-wait-threshold`, `disable-tcp`, `socket-buffer-size`, etc. @@ -908,7 +930,7 @@ public class ClientApplication { .. } Keep in mind, some of the _Geode Properties_ are client specific (e.g. `conflateEvents`) while others are server specific (e.g. `distributedSystemId`, `enableNetworkPartitionDetection`, `enforceUniqueHost`, `memberTimeout`, -`redundancyZone`). +`redundancyZone`, etc). More details on Apache Geode properties can be found http://geode.apache.org/docs/guide/12/reference/topics/gemfire_properties.html[here]. @@ -925,7 +947,7 @@ generally available and accessible. Apache Geode organizes data in a cache into http://geode.apache.org/docs/guide/12/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 making it -more conducive for building effective `Indexes`. We will talk about Indexing +more conducive for building effective `Indexes` and writing queries. We will talk about Indexing <>. Previously, _Spring Data Geode_ users needed to explicitly define and declare the Regions used in their applications @@ -966,23 +988,23 @@ Or, using <>... ---- -While neither Java nor XML configuration is hard to do, it is cumbersome, especially if an application has a large -number of Regions that need to be defined. Many relational database-based applications can literally have 100s -or even 1000s of tables. +While neither Java nor XML configuration is all that difficult to do, it is cumbersome, especially if an application +has a large number of Regions that need to be defined. Many relational database-based applications can literally +have hundreds or even thousands of tables. Ugh! -Now users can define and configure Regions based on their application domain objects (entities). No longer does +Now users can define and configure Regions based on their application domain objects (i.e. entities). No longer will a user need to explicitly define `Region` bean definitions in Spring configuration meta-data, unless finer-grained control is required. -To simplify Region creation, _Spring Data Geode_ combines the use of SD _Repositories_ with the expressive power of -Annotation-based configuration using the **new** `@EnableEntityDefinedRegions` annotation. +To simplify Region creation, _Spring Data Geode_ 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 https://docs.spring.io/spring-data/commons/docs/current/reference/html/#repositories[_Spring Data Repository_ abstraction] -and _Spring Data Geode's_ <> of the SD _Repository abstraction_, which -has been specifically customized to optimize data access operations for Apache Geode. +and _Spring Data Geode's_ <> of _Spring Data's_ _Repository abstraction_, +which has been specifically customized to optimize data access operations for Apache Geode. First, an application developer starts by defining the application domain objects... @@ -1008,7 +1030,7 @@ class Book { } ---- -Next, the application developer would define a basic _Repository_ for `Books` by extending _Spring Data Commons_ +Next, an application developer would define a basic _Repository_ for `Books` by extending _Spring Data Commons_ `org.springframework.data.repository.CrudRepository` interface... .Repository for Books @@ -1022,9 +1044,9 @@ operations (CRUD) along with support for simple queries (e.g. `findById(..)`). more sophisticated queries simply by declaring query methods on the _Repository_ interface (e.g. `List findByAuthor(Author author);`). -Under-the-hood, _Spring Data Geode_ provides an implementation of this interface when the Spring container is -bootstrapped. SDG will even implement the query methods defined by the user so long as the user follows simple -<>. +Under-the-hood, _Spring Data Geode_ provides an implementation of the applications _Repository_ interface when +the Spring container is bootstrapped. SDG will even implement the query methods defined by the user so long as +the user follows simple <>. Now, when a user defined the `Book` class, she also specified the Region in which instances of `Book` will be mapped and stored by declaring the _Spring Data Geode_ mapping annotation, `@Region` on the entity's type. Of course, if @@ -1041,16 +1063,17 @@ To enable and use this feature, simply annotate the application class with `@Ena ---- @SpringBootApplication @ClientCacheApplication -@EnableGemfireRepositories @EnableEntityDefinedRegions(basePackages = "example.app.domain") +@EnableGemfireRepositories(basePackages = "example.app.repo") class ClientApplication { .. } ---- TIP: Creating Regions from entity classes is the most useful when using _Spring Data Repositories_ in your application. -_Spring Data Geode's_ _Repository_ support is enabled with the `@EnableGemfireRepositories` annotation. +_Spring Data Geode's_ _Repository_ support is enabled with the `@EnableGemfireRepositories` annotation, as shown +in the example above. By default, the `@EnableEntityDefinedRegions` annotation will scan for entity classes recursively starting from -the package of the configuration class on which the `@EnableEntityDefinedRegions` annotation is defined. +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. @@ -1073,7 +1096,7 @@ class ClientApplication { .. } ---- In addition to specifying the location where to begin the scan, like Spring's `@ComponentScan` annotation, a user can -specify include and exclude filters with all the same semantics of the +specify `include` and `exclude` filters with all the same semantics of the `org.springframework.context.annotation.ComponentScan.Filter` annotation. See the `@EnableEntityDefinedRegion` annotation _Javadoc_ for more details. @@ -1091,9 +1114,9 @@ NOTE: Other configuration settings also can affect how data is managed like the See http://geode.apache.org/docs/guide/12/developing/region_options/storage_distribution_options.html[Storage and Distribution Options] in the Apache Geode User Guide for more details. -When user annotate their application domain object types with the generic `@Region` mapping annotation, -_Spring Data Geode_ will decide which type of `Region` to create. SDG's default strategy takes the cache type into -consideration when determining the type of `Region` to create. +When the user annotates her application domain object types with the generic `@Region` mapping annotation, +_Spring Data Geode_ will decide which type of `Region` to create. SDG's default strategy takes the cache type +into consideration when determining the type of `Region` to create. For example, if the application was declared as a `ClientCache` using the `@ClientCacheApplication` annotation, then SDG would create a client `PROXY` `Region`. Or, if the application was declared as a peer `Cache` using either the @@ -1108,18 +1131,20 @@ applied by _Spring Data Geode_, 4 new Region mapping annotations have been intro * `ReplicateRegion` The `ClientRegion` mapping annotation is specific to client applications. All other Region mapping annotations -listed above can only be used in server applications. +listed above can only be used in server applications with 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 -for the user. In this case, the data may not necessarily need to be distributed back to the server, not 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 (NOTE: this is -independent of and different from "entry" TTI/TTL expiration policies). +for the user. In this case, the data may not need to be distributed back to the server, not 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. + +NOTE: Region-level Idle-Timeout (TTI) and Time-To-Live (TTL) expiration policies are independent of and different from +entry-level TTI/TTL expiration policies. In any case, if a user wanted to create a local-only, client Region where the data is not gong to be distributed to -a corresponding Region with the same name on the server, the user would specify the `@ClientRegion` mapping annotation -and set the `shortcut` attribute to `ClientRegionShortcut.LOCAL`... +a corresponding Region with the same name on the server, the user would simply declare the `@ClientRegion` +mapping annotation and set the `shortcut` attribute to `ClientRegionShortcut.LOCAL`... .Spring `ClientCache` application with a local-only, client Region [source, java] @@ -1139,7 +1164,7 @@ http://geode.apache.org/docs/guide/12/developing/region_options/region_types.htm === Configuring Eviction Managing data with Apache Geode is an active task. More than likely, tuning will be required and a combination -of features (e.g. both Eviction and <>) will need to +of features (e.g. both _Eviction_ and <>) will need to be employed to effectively manage your data in memory with Apache Geode. Given that Apache Geode is an _In-Memory Data Grid_ (IMDG), data is managed in "memory" and distributed to other nodes @@ -1150,8 +1175,8 @@ as linear scale-out (rather than scaling up, which means to add more memory, mor 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 nearing full capacity, is rarely, if ever, a good thing. Stop-the-world GCs or worse, -`OutOfMemoryErrors`, will bring your application to a screaming halt. +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, Apache Geode supports LRU-based _Eviction_. That is, Apache Geode evicts Region entries based on when those entries were last accessed by using @@ -1179,11 +1204,11 @@ As shown above, the `policies` attribute can specify 1 or more nested `@Eviction catered to 1 or more Regions where the Eviction policy needs to be applied. Additionally, a user can reference a custom implementation of Apache Geode's -http://geode.apache.org/releases/latest/javadoc/org/apache/geode/cache/util/ObjectSizer.html[`org.apache.geode.cache.util.ObjectSizer`] interface -defined as a bean in the Spring context and referenced by name using the `objectSizerName` attribute. +http://geode.apache.org/releases/latest/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 context and referenced by name using the `objectSizerName` attribute. -An `ObjectSizer` allows the user to define the criteria used to evaluate and determine the the size of objects stored -in the Region. +An `ObjectSizer` allows the user to define the criteria used to evaluate and determine the the size of objects +stored in a Region. See the `@EnableEviction` annotation _Javadoc_ for a complete list of Eviction configuration options. @@ -1193,17 +1218,18 @@ http://geode.apache.org/docs/guide/12/developing/eviction/chapter_overview.html[ [[bootstrap-annotation-config-region-expiration]] === Configuring Expiration -Along with _Eviction_, _Expiration_ can also be used to manage memory by allowing entries stored in a Region to expire. -Both _Time-to-Live_ (TTL) and _Idle-Timeout_ (TTI) based entry expiration policies are supported in Apache Geode. +Along with <>, _Expiration_ can also be used to manage memory +by allowing entries stored in a Region to expire. Both _Time-to-Live_ (TTL) and _Idle-Timeout_ (TTI) based entry +expiration policies are supported in Apache Geode. _Spring Data Geode's_ Annotation-based Expiration configuration is based on -<> that was added in -_Spring Data Geode_ many releases ago. +<> added in +_Spring Data Geode_ version 1.5. Essentially, _Spring Data Geode's_ Expiration annotation support is based on a provided, custom implementation of Apache Geode's http://geode.apache.org/releases/latest/javadoc/org/apache/geode/cache/CustomExpiry.html[`org.apache.geode.cache.CustomExpiry`] interface. -This custom implementation inspects the user's application domain objects stored in a Region for the presence of -type-level Expiration annotations. +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. _Spring Data Geode_ provides the following Expiration annotations used on application domain object types, out-of-the-box... @@ -1260,13 +1286,13 @@ http://geode.apache.org/docs/guide/12/developing/expiration/chapter_overview.htm === Configuring Compression In addition to <> -and <> a user may also configure his or her data Regions +and <>, a user may also configure his or her data Regions to use Compression in order to reduce memory consumption. Apache Geode allows users to compress in-memory Region values using pluggable http://geode.apache.org/releases/latest/javadoc/org/apache/geode/compression/Compressor.html[`Compressors`], or different compression codecs. Out-of-the-box, Apache Geode uses Google's http://google.github.io/snappy/[Snappy] -library. +compression library. To enable Compression support, simply annotate the application class with `@EnableCompression`... @@ -1279,7 +1305,7 @@ To enable Compression support, simply annotate the application class with `@Enab class ClientApplication { .. } ---- -NOTE: Neither the `compressorBeanName` nor the `regionNames` attributes are required. +NOTE: Neither the `compressorBeanName` nor the `regionNames` attribute are required. The `compressorBeanName` defaults to "`SnappyCompressor`" enabling Apache Geode's provided http://geode.apache.org/releases/latest/javadoc/org/apache/geode/compression/SnappyCompressor.html[`SnappyCompressor`] @@ -1293,14 +1319,14 @@ and `spring.data.gemfire.cache.compression.region-names` properties in the `appl to set and configure the values of these `@EnableCompression` annotation attributes. WARNING: To use Apache Geode's Region Compression feature, you must include the `org.iq80.snappy:snappy` dependency -in your _Maven_ `pom.xml` or `build.gradle` file when using _Gradle_. This is only necessary if you use Apache Geode's -default, out-of-the-box support for Region Compression, which uses the +in your application _Maven_ `pom.xml` file, or `build.gradle` file when using _Gradle_. This is only necessary +if you use Apache Geode's default, out-of-the-box support for Region Compression, which uses the http://geode.apache.org/releases/latest/javadoc/org/apache/geode/compression/SnappyCompressor.html[`SnappyCompressor`] by default. Of course, if you are using another compression library, you will need to include dependencies for that compression library on your application's classpath. Additionally, you will need to implement Apache Geode's -http://geode.apache.org/releases/latest/javadoc/org/apache/geode/compression/Compressor.html[`Compressors`] to adapt -your compression library of choice, define it as a bean in the _Spring_ context, and then set the `compressorBeanName` -to this custom bean definition. +http://geode.apache.org/releases/latest/javadoc/org/apache/geode/compression/Compressor.html[`Compressor`] interface +to adapt your compression library of choice, define it as a bean in the _Spring_ context, and then set +the `compressorBeanName` to this custom bean definition. See the `@EnableCompression` annotation _Javadoc_ for more details. @@ -1312,7 +1338,9 @@ http://gemfire91.docs.pivotal.io/geode/managing/region_compression.html[here]. Another effective means for reducing pressure on the JVM's Heap memory and minimize GC activity is to use Apache Geode's _Off-Heap_ memory support. Rather than storing Region entries on the JVM Heap, entries are stored -in the system's main memory. +in the system's main memory. Off-Heap 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 Apache Geode +http://geode.apache.org/docs/guide/14/managing/heap_use/off_heap_management.html[User Guide]. To enable _Off-Heap_ support, simple annotate the application class with `@EnableOffHeap`... @@ -1340,7 +1368,7 @@ See the `@EnableOffHeap` annotation _Javadoc_ for more details. [[bootstrap-annotation-config-region-indexes]] === Configuring Indexes -There is not much use in storing data in Regions unless the data can be retrieved. +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 of the value of interest is known in advance, data is commonly retrieved by executing queries on the Regions containing the data. With Apache Geode, queries are @@ -1361,7 +1389,7 @@ Rather than explicitly declaring `Index` bean definitions using Spring config as .Index bean definition using Java config [source, java] ---- -@Bean("BookIsbnIndex") +@Bean("BooksIsbnIndex") IndexFactoryBean bookIsbnIndex(GemFireCache gemfireCache) { IndexFactoryBean bookIsbnIndex = new IndexFactoryBean(); @@ -1384,8 +1412,8 @@ Or, in <>... ---- -Indexes can now be created directly from the fields defined on application domain object types that a user knows -will be used in query predicates to speedup those queries. Indexes will be applied even for OQL queries generated +Indexes can now be defined directly on the fields declared in application domain object types that a user knows +will be used in query predicates to speedup those queries. Indexes will even be applied for OQL queries generated from user-defined query methods on an application's _Repository_ interfaces. Re-using the example `Book` class from above, we can annotate the fields on `Book` that we know will be used in queries @@ -1426,12 +1454,12 @@ the `@Id` annotated field or property is used as the key in the Region when stor text-based searches with Apache Geode'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/property of the object 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 object's -class (or the simple name of the domain object class if the `@Region` annotation was not specified). +are derived from the field/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, any of the `@Indexed` annotation attributes may be explicitly set to override the default values provided by -_Spring Data Geode_. +Of course, any of the `@Indexed` annotation attributes may be explicitly set to override the default values +provided by _Spring Data Geode_. .Application domain object type modeling a Book using cutomized Indexes [source, java] @@ -1477,15 +1505,15 @@ class ServerApplication { .. } ---- NOTE: The `@EnablingIndexing` annotation has no effect unless the `@EnableEntityDefinedRegions` is also declared. -Essentially, Indexes are defined from entity class types, and entity classes must be scanned in order to inspect -the entity's fields and properties for the presence of Index annotations. Without this scan, Index annotations -would not be found. It is also imperative that you limit the scope of the scan. +Essentially, Indexes are defined from fields/properties on the entity class types, and entity classes must be scanned +in order to inspect the entity's fields and properties for the presence of Index annotations. Without this scan, +Index annotations would not be found. It is also strongly recommended that you limit the scope of the scan. While Lucene queries are not supported on _Spring Data Geode_ _Repositories_ (yet), SDG does provide comprehensive https://docs.spring.io/spring-data-gemfire/docs/current/reference/html/#bootstrap:lucene[support] for Apache Geode -Lucene queries using the familiar Spring _Template_ pattern. +Lucene queries using the familiar Spring _Template_ design pattern. -Finally, we close with a few extra things to keep in mind when using Indexes: +Finally, we close with a few extra tips to keep in mind when using Indexes: 1. While OQL Indexes are not required to execute OQL Queries, Lucene Indexes are required to execute Lucene, text-based searches. @@ -1496,7 +1524,7 @@ exclusively in memory, and especially when Region entries are updated. Index "m http://geode.apache.org/releases/latest/javadoc/org/apache/geode/cache/RegionFactory.html#setIndexMaintenanceSynchronous-boolean-[configured] as an asynchronous task. -Another optimization that can be utilized when re-starting your Spring application where Indexes have to be rebuilt +Another optimization that may be utilized when re-starting your Spring application where Indexes have to be rebuilt is to first define all the Indexes upfront and then create them all at once, which, in _Spring Data Geode_, happens when the Spring context is refreshed. @@ -1525,21 +1553,21 @@ http://geode.apache.org/docs/guide/12/tools_modules/lucene_integration.html[here Regions can be configured to persist data to disk. Regions can also be configured to overflow data to disk when Region entries are evicted. In both cases, a `DiskStore` is required to persist or overflow the data. When an -explicit `DiskStore` has not been set on a Region with persistence or overflow configured, then Apache Geode will -use the "DEFAULT" `DiskStore`. +explicit `DiskStore` has not been set on a Region with persistence or overflow configured, then Apache Geode +will use the "DEFAULT" `DiskStore`. However, it is possible and recommended to define Region-specific `DiskStores` when persisting or overflowing data to disk. -_Spring Data Geode_ provides Annotation support for defining and creating application Region `DiskStores` by -annotating the application class with the `@EnableDiskStore` and `@EnableDiskStores` annotations. +_Spring Data Geode_ 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 1 or more `@EnableDiskStore` annotations. For example, while `Book` product information might mostly consist of reference data, from some external data source (e.g. Amazon), `Order` data is most likely going to be transactional in nature and something the application is going to -need to retain, maybe even overflow to disk if the transaction volume is high enough, or so any Book publisher hopes, -anyway. +need to retain, 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, I can define and create a `DiskStore` as follows... @@ -1556,15 +1584,16 @@ class ServerApplication { .. } Again, more than 1 `DiskStore` can be defined using the composite, `@EnableDiskStores` annotation. Like other Annotations in _Spring Data Geode's_ Annotation-based configuration model, both `@EnableDiskStore` -and `@EnableDiskStores` have many attributes along with associated configuration properties to apply additional -configuration to `DiskStores` created at runtime. +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 +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 can override a particular global setting, but the `@EnableDiskStores` -annotation defines common configuration attributes for all `DiskStores` out of convenience. +Individual `DiskStore` configuration will override a particular global setting, but the `@EnableDiskStores` +annotation conveniently defines common configuration attributes applied across all `DiskStores` aggregated by +the annotation. -_Spring Data Geode_ also provides the `DiskStoreConfigurer` callback interface that can be declared in Java config +_Spring Data Geode_ also provides the `DiskStoreConfigurer` callback interface, which can be declared in Java config and used instead of configuration properties to customize a `DiskStore` at runtime... .Spring application with custom DiskStore configuration @@ -1608,16 +1637,16 @@ important requirement for many applications. One example is self-driving vehicl transform, analyze and act on data in real-time is a key differentiator and characteristic of real-time enabled applications. -Fortunately, Apache Geode was ahead of its time in this regard. Using _Continuous Queries_ (CQ) a client application +Fortunately, Apache Geode was ahead of its time in this regard. 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 -arrive. The data that a client application may be interested in is expressed in a OQL query, where the query predicate -is used to filter, or identify the data of interests. 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. +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 interests. 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. -_Spring Data Geode_ makes defining and registering CQs along with an associated listener to handler and process CQ +_Spring Data Geode_ makes defining and registering CQs along with an associated listener to handle and process CQ events without all the cruft of Apache Geode's plumbing, a non-event (no pun intended). SDG's new Annotation-based -configuration for CQs builds on the already existing _Continuous Query_ support in the -<>. +configuration for CQs builds on the existing _Continuous Query_ support in the +<>. For instance, say a Book publisher wants to register interests in and receive notification anytime orders (demand) for a `Book` exceeds the current inventory (supply), then the publisher's print application might register @@ -1651,15 +1680,15 @@ class PublisherPrintApplication { To enable _Continuous Queries_, simply annotate your application class with `@EnableContinuousQueries`. Defining _Continuous Queries_ is as simple as annotating any Spring `@Component` annotated POJO class methods -with the `@ContinuousQuery` annotation, in similar fashion to SDG's Function annotated POJO methods. A method +with the `@ContinuousQuery` annotation, in similar fashion to SDG's Function annotated POJO methods. A POJO method defined with a CQ using the `@ContinuousQuery` annotation will be called anytime data matching the query predicate is added or changed. Additionally, the POJO method signature should adhere to the requirements outlined in the section on <>. -See the `@EnableContinuousQueries` and `@ContinuousQuery` annotation _Javadoc_ for more details on available attributes -and configuration settings. +See the `@EnableContinuousQueries` and `@ContinuousQuery` annotation _Javadoc_ for more details on +available attributes and configuration settings. More details on _Spring Data Geode's_ Continuous Query support can be found <>. @@ -1678,7 +1707,7 @@ is performed before invoking a potentially expensive operation, or where the res are cached after the operation is invoked. In _Spring Data Geode_, a Spring `Cache` corresponds directly to a Region. The Region must exist before any -`@Cacheable` application service method is called. This is true for any of Spring's caching annotations +`@Cacheable` application service methods are called. This is true for any of Spring's caching annotations (i.e. `@Cacheable`, `@CachePut` and `@CacheEvict`) that identify the cache to use in the operation. For instance, our publisher's Point-of-Sale (POS) application might have a feature to determine, or lookup @@ -1725,10 +1754,10 @@ class CachingConfiguration { } @Bean("BookPricesCache") - PartitionedRegionFactoryBean bookPricesRegion(GemFireCache gemfireCache) { + ReplicatedRegionFactoryBean bookPricesRegion(GemFireCache gemfireCache) { - PartitionedRegionFactoryBean bookPricesRegion = - new PartitionedRegionFactoryBean<>(); + ReplicatedRegionFactoryBean bookPricesRegion = + new ReplicatedRegionFactoryBean<>(); bookPricesRegion.setCache(gemfireCache); bookPricesRegion.setClose(false); @@ -1764,7 +1793,7 @@ First, the `@EnableGemfireCaching` annotation replaces both the Spring `EnableCa the need to declare an explicit `cacheManager` bean definition in the Spring config. Second, the `@EnableCachingDefinedRegions` annotation, like the `@EnableEntityDefinedRegions` annotation described in -<>, inspects all the Spring caching annotated application +<>, inspects all the Spring application, caching annotated service components to identify all the caches that will be needed by the application at runtime and creates Regions in Apache Geode for these caches on application startup. @@ -1775,7 +1804,7 @@ a client `PROXY` Region and expects that a Region with the same name already exi NOTE: SDG cannot determine the cache required by a service method using a Spring `CacheResolver` to resolve the cache used in the operation at runtime. -NOTE: SDG does not currently identify _JCache_, JSR-107 cache annotations used on application service components. +TIP: SDG also supports _JCache_, JSR-107 cache annotations on application service components as well. Refer to the core https://docs.spring.io/spring/docs/current/spring-framework-reference/integration.html#cache-jsr-107[_Spring Framework Reference Guide_] for the equivalent Spring caching annotation to use in place of _JCache_, JSR-107 caching annotations. @@ -1791,37 +1820,39 @@ https://docs.spring.io/spring/docs/current/spring-framework-reference/integratio This may be the most exciting **new** feature in _Spring Data Geode_. When a client application class is annotated with `@EnableClusterConfiguration`, any Regions or Indexes defined -and declared as beans in the Spring context 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 Apache Geode will remember -the configuration pushed by the client. If all the nodes in the cluster go down, they will come back up with -the same configuration as before. +and declared as beans in the Spring context 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 Apache Geode will +remember the configuration pushed by the client, when using HTTP. If all the nodes in the cluster go down, they +will come back up with the same configuration as before. In a sense, this feature is not much different than if a user were to use _Gfsh_ to create the Regions and Indexes -on all the servers in the cluster. Except now, with _Spring Data Geode_, users does **not** need to use _Gfsh_ -to create Regions and Indexes. The user's _Spring Boot_ application, enabled with the power of _Spring Data Geode_, -already contains all the configuration meta-data SDG needs to create Regions and Indexes for the user. +on all the servers in the cluster, manually. Except now, with _Spring Data Geode_, users does **not** need to use +_Gfsh_ to create Regions and Indexes. The user's _Spring Boot_ application, enabled with the power of +_Spring Data Geode_, already contains all the configuration meta-data needed to create Regions and Indexes +for the user. When users are using the _Spring Data Repository_ abstraction, we know all the Regions (e.g. `@Region` annotated entity types) and Indexes (e.g. `@Indexed` annotated entity fields and properties) that the users' application will need. When users are using _Spring's Cache Abstraction_, we also know all the Regions for all the caches -identified in the caching annotations that the application is going to need. Essentially, the user is already -telling us everything we need to know just by developing her application with the entire _Spring Framework_ and all -of its provided services, infrastructure, etc, whether expressed in Annotation meta-data, Java, XML or otherwise, -and whether for configuration, for mapping, or whatever purpose. +identified in the caching annotations needed by the application's service components. Essentially, the user is +already telling us everything we need to know just by developing her application with the entire _Spring Framework_ +and all of its provided services, infrastructure, etc, whether expressed in Annotation meta-data, Java, XML +or otherwise, and whether for configuration, for mapping, or whatever purpose. -The user can focus on her application business logic along with using the framework provided services and -supporting infrastructure (e.g. _Spring Data Repositories_, _Spring's Transaction Management_, _Spring Caching_, etc) -and _Spring Data Geode_ will take care of all the Apache Geode plumbing required by those framework services +The point is, users can focus on their application business logic along with using the framework provided services +and supporting infrastructure (e.g. _Spring Data Repositories_, _Spring's Transaction Management_, _Spring Caching_, +and so on) and _Spring Data Geode_ will take care of all the Apache Geode plumbing required by those framework services on the user's 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 Apache Geode's http://geode.apache.org/docs/guide/12/configuring/cluster_config/gfsh_persist.html[_Cluster Configuration_] service. Apache Geode's _Cluster Configuration_ service is also the same service used by _Gfsh_ to record -schema-related changes issued by the user to the cluster from the shell. +schema-related changes (e.g. `gfsh> create region --name=Example --type=PARTITION`) issued by the user to the cluster +from the shell. -Of course, since the cluster "remembers" the prior configuration pushed by a client from a previous run perhaps, +Of course, since the cluster "remembers" the prior configuration pushed by a client from a previous run, perhaps, _Spring Data Geode_ is careful not to stomp on any existing Regions and Indexes already defined in the servers. -This is especially important when Regions already contain data. +This is especially important when Regions already contain data, for instance. NOTE: Currently there is no option to overwrite any existing Region or Index definitions. To recreate a Region or Index, the user must use _Gfsh_ to destroy the Region or Index first and then restart the client application @@ -1831,7 +1862,7 @@ so that configuration will be pushed up to the server again. Alternatively a us NOTE: Unlike _Gfsh_, _Spring Data Geode_ only supports the creation of Regions and Indexes on the servers from a client. For advanced configuration and use cases, _Gfsh_ should be used to manage the cluster. -For a moment, imagine the power expressed in the following configuration... +For a moment, just imagine the power expressed in the following configuration... .Spring `ClientCache` application [source, java] @@ -1852,12 +1883,12 @@ _Spring Data Repositories_ with _Spring's Cache Abstraction_, using Apache Geode where Regions and Indexes are not only created on the client, but pushed to the servers in the cluster. All the application developer need do is define the application's domain model objects annotated with mapping -and Index annotations, define Repository interfaces supporting basic data access operations and querying -for each of the entity types, define the service components containing the business logic manipulating +and Index annotations, define Repository interfaces supporting basic data access operations and simple queryies +for each of the 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, etc, and the developer is in business. Nothing the user did in this case pertains to infrastructure and plumbing required in the application's back-end services (e.g. Apache Geode). Database users have similar -features, no Spring, Apache Geode developers can too. +features. Now Spring, Apache Geode developers can too! When combined with a couple more _Spring Data Geode_ Annotations... @@ -1865,7 +1896,7 @@ When combined with a couple more _Spring Data Geode_ Annotations... * `@EnableGemfireFunctionExecutions` * `@EnableGemfireCacheTransactions` -Then, the application is really going to start to take flight. +Then, this application is really going to start to take flight, with very minimal effort. See the `@EnableClusterConfiguration` annotation _Javadoc_ for more details. @@ -1911,7 +1942,7 @@ universally known and non-Apache Geode specific. TIP: See Apache Geode's Security examples for http://geode.apache.org/docs/guide/12/managing/security/authentication_examples.html[Authentication] and http://geode.apache.org/docs/guide/12/managing/security/authorization_example.html[Authorization] as 1 possible way -to implement your own custom, application specific `SecurityManager`. +to implement your own custom, application specific `SecurityManager`. However, this is strongly **not** recommended. The second approach using an Apache Shiro INI file is marginally better, but a user still needs to be familiar with the INI file format in the first place. Additionally, an INI file is static and not easily updatable at runtime. @@ -1929,23 +1960,25 @@ class ServerApplication { @Bean PropertiesRealm shiroRealm() { + PropertiesRealm propertiesRealm = new PropertiesRealm(); + propertiesRealm.setResourcePath("classpath:shiro.properties"); propertiesRealm.setPermissionResolver(new GeodePermissionResolver()); + return propertiesRealm; } } ---- -NOTE: The configured `Realm` shown in the example above could have easily been any of Apache Shiro's supported -`Realms` out-of-the-box (https://shiro.apache.org/static/1.3.2/apidocs/org/apache/shiro/realm/activedirectory/package-frame.html[ActiveDirectory], +TIP: The configured `Realm` shown in the example above could have easily been any of Apache Shiro's supported `Realms` +out-of-the-box: (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], or even a `Realm` supporting the https://shiro.apache.org/static/1.3.2/apidocs/org/apache/shiro/realm/text/IniRealm.html[INI format]) -or even a custom implementation of an Apache Shiro `Realm` implemented by the user. See Apache Shiro's -https://shiro.apache.org/realm.html[documentation on Realms] -for more details. +or perhaps a custom implementation of an Apache Shiro `Realm` implemented by the user. 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 1 or more Apache Shiro `Realms` have been defined as beans in the Spring context, _Spring Data Geode_ will detect this configuration and use Apache Shiro @@ -1955,8 +1988,8 @@ TIP: Earlier, information was posted on _Spring Data Geode's_ support for Apache framework using Apache Shiro in this https://spring.io/blog/2016/11/10/spring-data-geode-1-0-0-incubating-release-released[spring.io blob post]. -See the `@EnableSecurity` annotation _Javadoc_ for more details on available attributes and associated -configuration properties. +See the `@EnableSecurity` annotation _Javadoc_ for more details on available attributes +and associated configuration properties. More details on Apache Geode Security can be found http://geode.apache.org/docs/guide/12/managing/security/chapter_overview.html[here]. @@ -1967,7 +2000,7 @@ http://geode.apache.org/docs/guide/12/managing/security/chapter_overview.html[he The Security story would not be complete without discussing how to secure Spring-based, Apache Geode cache client applications. -Apache Geode's process to securing a client application is, well, rather involved. In a nutshell, a user essentially +Apache Geode's process of securing a client application is, well, rather involved. In a nutshell, a user essentially needs to... 1. Provide an implementation of the @@ -2007,11 +2040,11 @@ spring.data.gemfire.security.password=b@cK!nB1@cK That was easy! TIP: By default, _Spring Boot_ can find an `application.properties` file when placed in the root of -the application's `CLASSPATH`. Of course, Spring supports may ways to to locate resources using its +the application's `CLASSPATH`. Of course, Spring supports may ways to locate resources using its https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#resources[Resource abstraction]. -See the `@EnableSecurity` annotation _Javadoc_ for more details on available attributes and associated -configuration properties. +See the `@EnableSecurity` annotation _Javadoc_ for more details on available attributes +and associated configuration properties. More details on Apache Geode Security can be found http://geode.apache.org/docs/guide/12/managing/security/chapter_overview.html[here]. @@ -2040,10 +2073,10 @@ For instance, given... @EnableCachingDefinedRegions @EnableEntityDefinedRegions @EnableIndexing +@EnableGemfireCacheTransactions @EnableGemfireCaching @EnableGemfireFunctionExecutions @EnableGemfireRepositories -@EnableGemfireCacheTransactions @EnableClusterConfiguration class ClientApplication { .. } ---- @@ -2054,22 +2087,25 @@ We could break this configuration down by concern. For example... [source, java] ---- @SpringBootApplication -@Import({ CachingConfiguration.class, GeodeConfiguration.class, - QueriesAndFunctionsConfiguration.class, RepositoryConfiguration.class }) +@Import({ GeodeConfiguration.class, CachingConfiguration.class, + FunctionsConfiguration.class, QueriesConfiguration.class, + RepositoriesConfiguration.class }) class ClientApplication { .. } -@EnableGemfireCaching -@EnableCachingDefinedRegions -class CachingConfiguration { .. } - @ClientCacheApplication @EnableClusterConfiguration @EnableGemfireCacheTransactions class GeodeConfiguration { .. } -@EnableContinuousQueries +@EnableGemfireCaching +@EnableCachingDefinedRegions +class CachingConfiguration { .. } + @EnableGemfireFunctionExecutions -class QueriesAndFunctionsConfiguration { +class FunctionsConfiguration { .. } + +@EnableContinuousQueries +class QueriesConfiguration { @ContinuousQuery(..) void processCqEvent(CqEvent event) { @@ -2077,10 +2113,10 @@ class QueriesAndFunctionsConfiguration { } } -@EnableGemfireRepositories @EnableEntityDefinedRegions +@EnableGemfireRepositories @EnableIndexing -class RepositoryConfiguration { .. } +class RepositoriesConfiguration { .. } ---- Spring does not care. Organize your application configuration as you see fit. @@ -2096,19 +2132,21 @@ the Annotation provides. * `@EnableAuth` - enable Apache Geode's old Authentication/Authorization Security model. (_Deprecated_; use Apache Geode's new _Integrated Security_ framework discussed <>). +Again, Apache Geode's new _Integrated Security_ framework can be enabled on both clients and servers using SDG's +`@EnableSecurity` annotation as described in <>. * `@EnableAutoRegionLookup` - Not recommended. Essentially, this Annotation supports finding Regions defined in external configuration meta-data (e.g. `cache.xml`, or _Cluster Configuration_ when applied to a server) and registers those Regions as beans in the Spring context automatically. Users should generally prefer Spring config when -using Spring and _Spring Data Geode_. See <> +using Spring and _Spring Data Geode_. See <> and <> instead. -* `@EnableBeanFactoryLocator` - enables the SDG `GemfireBeanFactoryLocator` feature, which is only useful, again, when -using external configuration meta-data (e.g. `cache.xml`). For example, if a user defines a `CacheLoader` on a -Region defined in `cache.xml`, the user can still auto-wire this `CacheLoader` with say, a relational database -`DataSource` bean defined in Spring confif. This Annotation takes advantage of this SDG <> +* `@EnableBeanFactoryLocator` - enables the SDG `GemfireBeanFactoryLocator` feature, which is only useful, again, +when using external configuration meta-data (e.g. `cache.xml`). For example, if a user defines a `CacheLoader` on +a Region defined in `cache.xml`, the user can still auto-wire this `CacheLoader` with say, a relational database +`DataSource` bean defined in Spring config. This Annotation takes advantage of this SDG <> and might be useful for users who have a large amount of legacy configuration meta-data, like `cache.xml` files. * `@EnableGemFireAsLastResource` - is actually discussed in <> with Apache Geode. -* `@EnableMcast` - enables Apache Geode's old peer discovery mechanism using UDP-based Multi-cast Networking. +* `@EnableMcast` - enables Apache Geode's old peer discovery mechanism using UDP-based Multi-cast Networking. (_Deprecated_; users should be using Apache Geode Locators instead; see <>. * `@EnableRegionDataAccessTracing` - is useful for debugging purposes; the Annotation enables tracing for all @@ -2136,9 +2174,11 @@ Do not be alarmed; this is to be expected. For example, another case where Java config or XML will be needed is when configuring Apache Geode WAN components, which currently do not have any Annotation configuration support. However, defining and registering WAN components -is as simple as using the `org.springframework.data.gemfire.wan.GatewaReceiverFactoryBean` -and `org.springframework.data.gemfire.wan.GatewaySenderFactoryBean` API classes in Java configuration on your Spring +is as simple as using the `org.springframework.data.gemfire.wan.GatewayReceiverFactoryBean` +and `org.springframework.data.gemfire.wan.GatewaySenderFactoryBean` API classes in 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 application developers **get up and running** as **quickly** and as **easily** as possible, especially during development. + +We hope you will enjoy these new capabilities! diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/AbstractCacheConfiguration.java b/src/main/java/org/springframework/data/gemfire/config/annotation/AbstractCacheConfiguration.java index 1e0f05b4..f43830da 100644 --- a/src/main/java/org/springframework/data/gemfire/config/annotation/AbstractCacheConfiguration.java +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/AbstractCacheConfiguration.java @@ -23,7 +23,6 @@ import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeList import java.lang.annotation.Annotation; import java.util.List; -import java.util.Map; import java.util.Optional; import java.util.Properties; import java.util.concurrent.atomic.AtomicBoolean; @@ -43,6 +42,7 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportAware; +import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.convert.ConversionService; import org.springframework.core.io.Resource; import org.springframework.core.type.AnnotationMetadata; @@ -126,7 +126,9 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi private Integer mcastPort = 0; private Float criticalHeapPercentage; + private Float criticalOffHeapPercentage; private Float evictionHeapPercentage; + private Float evictionOffHeapPercentage; private GatewayConflictResolver gatewayConflictResolver; @@ -165,7 +167,7 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi * * @return a {@link Properties} object containing Pivotal GemFire/Apache Geode properties used to configure * the Pivotal GemFire/Apache Geode cache instance. - * @see GemFire Properties + * @see GemFire Properties * @see java.util.Properties * @see #locators() * @see #logLevel() @@ -192,6 +194,7 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi */ @Override public void setImportMetadata(AnnotationMetadata importMetadata) { + configureInfrastructure(importMetadata); configureCache(importMetadata); configurePdx(importMetadata); @@ -207,6 +210,7 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi * @see org.springframework.core.type.AnnotationMetadata */ protected void configureInfrastructure(AnnotationMetadata importMetadata) { + registerCustomEditorBeanFactoryPostProcessor(importMetadata); registerDefinedIndexesApplicationListener(importMetadata); registerDiskStoreDirectoryBeanPostProcessor(importMetadata); @@ -249,8 +253,7 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi if (isClientPeerOrServerCacheApplication(importMetadata)) { - Map cacheMetadataAttributes = - importMetadata.getAnnotationAttributes(getAnnotationTypeName()); + AnnotationAttributes cacheMetadataAttributes = getAnnotationAttributes(importMetadata); setCopyOnRead(resolveProperty(cacheProperty("copy-on-read"), Boolean.TRUE.equals(cacheMetadataAttributes.get("copyOnRead")))); @@ -263,6 +266,14 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi .filter(AbstractAnnotationConfigSupport::hasValue) .ifPresent(this::setCriticalHeapPercentage); + Optional.ofNullable(resolveProperty(cacheProperty("critical-off-heap-percentage"), (Float) null)) + .ifPresent(this::setCriticalOffHeapPercentage); + + Optional.ofNullable((Float) cacheMetadataAttributes.get("criticalOffHeapPercentage")) + .filter(it -> getCriticalOffHeapPercentage() == null) + .filter(AbstractAnnotationConfigSupport::hasValue) + .ifPresent(this::setCriticalOffHeapPercentage); + Optional.ofNullable(resolveProperty(cacheProperty("eviction-heap-percentage"), (Float) null)) .ifPresent(this::setEvictionHeapPercentage); @@ -271,6 +282,14 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi .filter(AbstractAnnotationConfigSupport::hasValue) .ifPresent(this::setEvictionHeapPercentage); + Optional.ofNullable(resolveProperty(cacheProperty("eviction-off-heap-percentage"), (Float) null)) + .ifPresent(this::setEvictionOffHeapPercentage); + + Optional.ofNullable((Float) cacheMetadataAttributes.get("evictionOffHeapPercentage")) + .filter(it -> getEvictionOffHeapPercentage() == null) + .filter(AbstractAnnotationConfigSupport::hasValue) + .ifPresent(this::setEvictionOffHeapPercentage); + setLogLevel(resolveProperty(cacheProperty("log-level"), (String) cacheMetadataAttributes.get("logLevel"))); @@ -297,7 +316,7 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi if (importMetadata.hasAnnotation(enablePdxTypeName)) { - Map enablePdxAttributes = importMetadata.getAnnotationAttributes(enablePdxTypeName); + AnnotationAttributes enablePdxAttributes = getAnnotationAttributes(importMetadata, enablePdxTypeName); setPdxDiskStoreName(resolveProperty(pdxProperty("disk-store-name"), (String) enablePdxAttributes.get("diskStoreName"))); @@ -377,6 +396,7 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi Optional.ofNullable(getPdxDiskStoreName()) .filter(StringUtils::hasText) .ifPresent(pdxDiskStoreName -> { + if (PDX_DISK_STORE_AWARE_BEAN_FACTORY_POST_PROCESSOR_REGISTERED.compareAndSet(false, true)) { register(BeanDefinitionBuilder.rootBeanDefinition(PdxDiskStoreAwareBeanFactoryPostProcessor.class) .setRole(BeanDefinition.ROLE_INFRASTRUCTURE) @@ -450,8 +470,10 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi gemfireCache.setClose(isClose()); gemfireCache.setCopyOnRead(getCopyOnRead()); gemfireCache.setCriticalHeapPercentage(getCriticalHeapPercentage()); + gemfireCache.setCriticalOffHeapPercentage(getCriticalOffHeapPercentage()); gemfireCache.setDynamicRegionSupport(getDynamicRegionSupport()); gemfireCache.setEvictionHeapPercentage(getEvictionHeapPercentage()); + gemfireCache.setEvictionOffHeapPercentage(getEvictionOffHeapPercentage()); gemfireCache.setGatewayConflictResolver(getGatewayConflictResolver()); gemfireCache.setJndiDataSources(getJndiDataSources()); gemfireCache.setProperties(gemfireProperties()); @@ -466,42 +488,6 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi return gemfireCache; } - /** - * Returns the cache application {@link java.lang.annotation.Annotation} type pertaining to this configuration. - * - * @return the cache application {@link java.lang.annotation.Annotation} type used by this application. - * @see org.springframework.data.gemfire.config.annotation.CacheServerApplication - * @see org.springframework.data.gemfire.config.annotation.ClientCacheApplication - * @see org.springframework.data.gemfire.config.annotation.PeerCacheApplication - */ - protected abstract Class getAnnotationType(); - - /** - * Returns the fully-qualified {@link Class#getName() class name} of the cache application - * {@link java.lang.annotation.Annotation} type. - * - * @return the fully-qualified {@link Class#getName() class name} of the cache application - * {@link java.lang.annotation.Annotation} type. - * @see java.lang.Class#getName() - * @see #getAnnotationType() - */ - protected String getAnnotationTypeName() { - return getAnnotationType().getName(); - } - - /** - * Returns the simple {@link Class#getName() class name} of the cache application - * {@link java.lang.annotation.Annotation} type. - * - * @return the simple {@link Class#getName() class name} of the cache application - * {@link java.lang.annotation.Annotation} type. - * @see java.lang.Class#getSimpleName() - * @see #getAnnotationType() - */ - protected String getAnnotationTypeSimpleName() { - return getAnnotationType().getSimpleName(); - } - // REVIEW JAVADOC FROM HERE /** @@ -604,7 +590,6 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi || isPeerCacheApplication(importMetadata)); } - /* (non-Javadoc) */ void setCacheXml(Resource cacheXml) { this.cacheXml = cacheXml; } @@ -613,7 +598,6 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi return this.cacheXml; } - /* (non-Javadoc) */ void setClose(boolean close) { this.close = close; } @@ -622,7 +606,6 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi return this.close; } - /* (non-Javadoc) */ void setCopyOnRead(boolean copyOnRead) { this.copyOnRead = copyOnRead; } @@ -631,7 +614,6 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi return this.copyOnRead; } - /* (non-Javadoc) */ void setCriticalHeapPercentage(Float criticalHeapPercentage) { this.criticalHeapPercentage = criticalHeapPercentage; } @@ -640,7 +622,14 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi return this.criticalHeapPercentage; } - /* (non-Javadoc) */ + void setCriticalOffHeapPercentage(Float criticalOffHeapPercentage) { + this.criticalOffHeapPercentage = criticalOffHeapPercentage; + } + + protected Float getCriticalOffHeapPercentage() { + return this.criticalOffHeapPercentage; + } + void setDynamicRegionSupport(DynamicRegionSupport dynamicRegionSupport) { this.dynamicRegionSupport = dynamicRegionSupport; } @@ -649,7 +638,6 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi return this.dynamicRegionSupport; } - /* (non-Javadoc) */ void setEvictionHeapPercentage(Float evictionHeapPercentage) { this.evictionHeapPercentage = evictionHeapPercentage; } @@ -658,7 +646,14 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi return this.evictionHeapPercentage; } - /* (non-Javadoc) */ + void setEvictionOffHeapPercentage(Float evictionOffHeapPercentage) { + this.evictionOffHeapPercentage = evictionOffHeapPercentage; + } + + protected Float getEvictionOffHeapPercentage() { + return this.evictionOffHeapPercentage; + } + void setGatewayConflictResolver(GatewayConflictResolver gatewayConflictResolver) { this.gatewayConflictResolver = gatewayConflictResolver; } @@ -667,7 +662,6 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi return this.gatewayConflictResolver; } - /* (non-Javadoc) */ void setJndiDataSources(List jndiDataSources) { this.jndiDataSources = jndiDataSources; } @@ -676,7 +670,6 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi return nullSafeList(this.jndiDataSources); } - /* (non-Javadoc) */ void setLocators(String locators) { this.locators = locators; this.mcastPort = DEFAULT_MCAST_PORT; @@ -686,7 +679,6 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi return this.locators; } - /* (non-Javadoc) */ void setLogLevel(String logLevel) { this.logLevel = logLevel; } @@ -695,7 +687,6 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi return Optional.ofNullable(this.logLevel).orElse(DEFAULT_LOG_LEVEL); } - /* (non-Javadoc) */ void setMappingContext(GemfireMappingContext mappingContext) { this.mappingContext = mappingContext; } @@ -713,7 +704,6 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi return Optional.ofNullable(mcastPort).orElse(DEFAULT_MCAST_PORT); } - /* (non-Javadoc) */ void setName(String name) { this.name = name; } @@ -722,7 +712,6 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi return Optional.ofNullable(this.name).filter(StringUtils::hasText).orElseGet(this::toString); } - /* (non-Javadoc) */ void setPdxDiskStoreName(String pdxDiskStoreName) { this.pdxDiskStoreName = pdxDiskStoreName; } @@ -731,7 +720,6 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi return this.pdxDiskStoreName; } - /* (non-Javadoc) */ void setPdxIgnoreUnreadFields(Boolean pdxIgnoreUnreadFields) { this.pdxIgnoreUnreadFields = pdxIgnoreUnreadFields; } @@ -740,7 +728,6 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi return this.pdxIgnoreUnreadFields; } - /* (non-Javadoc) */ void setPdxPersistent(Boolean pdxPersistent) { this.pdxPersistent = pdxPersistent; } @@ -749,7 +736,6 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi return this.pdxPersistent; } - /* (non-Javadoc) */ void setPdxReadSerialized(Boolean pdxReadSerialized) { this.pdxReadSerialized = pdxReadSerialized; } @@ -758,7 +744,6 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi return this.pdxReadSerialized; } - /* (non-Javadoc) */ void setPdxSerializer(PdxSerializer pdxSerializer) { this.pdxSerializer = pdxSerializer; } @@ -767,7 +752,6 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi return this.pdxSerializer; } - /* (non-Javadoc) */ void setStartLocator(String startLocator) { this.startLocator = startLocator; } @@ -776,7 +760,6 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi return this.startLocator; } - /* (non-Javadoc) */ void setTransactionListeners(List transactionListeners) { this.transactionListeners = transactionListeners; } @@ -785,7 +768,6 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi return nullSafeList(this.transactionListeners); } - /* (non-Javadoc) */ void setTransactionWriter(TransactionWriter transactionWriter) { this.transactionWriter = transactionWriter; } @@ -794,7 +776,6 @@ public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfi return this.transactionWriter; } - /* (non-Javadoc) */ void setUseBeanFactoryLocator(boolean useBeanFactoryLocator) { this.useBeanFactoryLocator = useBeanFactoryLocator; } diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/CacheServerApplication.java b/src/main/java/org/springframework/data/gemfire/config/annotation/CacheServerApplication.java index 0ac08fad..47ecf33c 100644 --- a/src/main/java/org/springframework/data/gemfire/config/annotation/CacheServerApplication.java +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/CacheServerApplication.java @@ -96,6 +96,16 @@ public @interface CacheServerApplication { */ float criticalHeapPercentage() default ResourceManager.DEFAULT_CRITICAL_PERCENTAGE; + /** + * Configures the percentage of off-heap at or above which the cache is considered in danger of becoming inoperable. + * + * Defaults to {@literal 0.0}. + * + * Use {@literal spring.data.gemfire.cache.critical-off-heap-percentage} property + * in {@literal application.properties}. + */ + float criticalOffHeapPercentage() default 0.0f; + /** * By default, a GemFire member (both locators and servers) will attempt to reconnect and reinitialize the cache * after it has been forced out of the distributed system by a network partition event or has otherwise been @@ -118,6 +128,17 @@ public @interface CacheServerApplication { */ float evictionHeapPercentage() default ResourceManager.DEFAULT_EVICTION_PERCENTAGE; + /** + * Configures the percentage of off-heap at or above which the eviction should begin on Regions configured + * for HeapLRU eviction. + * + * Defaults to {@literal 0.0}. + * + * Use {@literal spring.data.gemfire.cache.eviction-off-heap-percentage} property + * in {@literal application.properties}. + */ + float evictionOffHeapPercentage() default 0.0f; + /** * Configures the ip address or host name that server locators will tell clients that this cache server * is listening on. @@ -141,7 +162,7 @@ public @interface CacheServerApplication { /** * Configures the list of Locators defining the cluster to which this Spring cache application will connect. * - * Use {@literal spring.data.gemfire.cache.copy-on-read} property in {@literal application.properties}. + * Use {@literal spring.data.gemfire.locators} property in {@literal application.properties}. */ String locators() default ""; diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/CacheServerConfiguration.java b/src/main/java/org/springframework/data/gemfire/config/annotation/CacheServerConfiguration.java index 902c1efc..fb44afee 100644 --- a/src/main/java/org/springframework/data/gemfire/config/annotation/CacheServerConfiguration.java +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/CacheServerConfiguration.java @@ -20,6 +20,7 @@ package org.springframework.data.gemfire.config.annotation; import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeMap; import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeSet; +import java.lang.annotation.Annotation; import java.util.Collections; import java.util.List; import java.util.Map; @@ -39,6 +40,7 @@ import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.type.AnnotationMetadata; import org.springframework.data.gemfire.server.CacheServerFactoryBean; import org.springframework.data.gemfire.server.SubscriptionEvictionPolicy; @@ -145,6 +147,7 @@ public class CacheServerConfiguration extends PeerCacheConfiguration { return Optional.ofNullable(this.cacheServerConfigurers) .filter(cacheServerConfigurers -> !cacheServerConfigurers.isEmpty()) .orElseGet(() -> + Optional.of(this.getBeanFactory()) .filter(beanFactory -> beanFactory instanceof ListableBeanFactory) .map(beanFactory -> { @@ -175,8 +178,7 @@ public class CacheServerConfiguration extends PeerCacheConfiguration { if (isCacheServerApplication(importMetadata)) { - Map cacheServerApplicationAttributes = - importMetadata.getAnnotationAttributes(getAnnotationTypeName()); + AnnotationAttributes cacheServerApplicationAttributes = getAnnotationAttributes(importMetadata); setAutoStartup(resolveProperty(cacheServerProperty("auto-startup"), Boolean.TRUE.equals(cacheServerApplicationAttributes.get("autoStartup")))); @@ -229,11 +231,10 @@ public class CacheServerConfiguration extends PeerCacheConfiguration { * {@inheritDoc} */ @Override - protected Class getAnnotationType() { + protected Class getAnnotationType() { return CacheServerApplication.class; } - /* (non-Javadoc) */ void setAutoStartup(boolean autoStartup) { this.autoStartup = autoStartup; } @@ -242,7 +243,6 @@ public class CacheServerConfiguration extends PeerCacheConfiguration { return this.autoStartup; } - /* (non-Javadoc) */ void setBindAddress(String bindAddress) { this.bindAddress = bindAddress; } @@ -252,7 +252,6 @@ public class CacheServerConfiguration extends PeerCacheConfiguration { .orElse(CacheServer.DEFAULT_BIND_ADDRESS); } - /* (non-Javadoc) */ void setHostnameForClients(String hostnameForClients) { this.hostnameForClients = hostnameForClients; } @@ -262,7 +261,6 @@ public class CacheServerConfiguration extends PeerCacheConfiguration { .orElse(CacheServer.DEFAULT_HOSTNAME_FOR_CLIENTS); } - /* (non-Javadoc) */ void setInterestRegistrationListeners(Set interestRegistrationListeners) { this.interestRegistrationListeners = interestRegistrationListeners; } @@ -271,7 +269,6 @@ public class CacheServerConfiguration extends PeerCacheConfiguration { return nullSafeSet(this.interestRegistrationListeners); } - /* (non-Javadoc) */ void setLoadPollInterval(Long loadPollInterval) { this.loadPollInterval = loadPollInterval; } @@ -280,7 +277,6 @@ public class CacheServerConfiguration extends PeerCacheConfiguration { return Optional.ofNullable(this.loadPollInterval).orElse(CacheServer.DEFAULT_LOAD_POLL_INTERVAL); } - /* (non-Javadoc) */ void setMaxConnections(Integer maxConnections) { this.maxConnections = maxConnections; } @@ -289,7 +285,6 @@ public class CacheServerConfiguration extends PeerCacheConfiguration { return Optional.ofNullable(this.maxConnections).orElse(CacheServer.DEFAULT_MAX_CONNECTIONS); } - /* (non-Javadoc) */ void setMaxMessageCount(Integer maxMessageCount) { this.maxMessageCount = maxMessageCount; } @@ -298,7 +293,6 @@ public class CacheServerConfiguration extends PeerCacheConfiguration { return Optional.ofNullable(this.maxMessageCount).orElse(CacheServer.DEFAULT_MAXIMUM_MESSAGE_COUNT); } - /* (non-Javadoc) */ void setMaxThreads(Integer maxThreads) { this.maxThreads = maxThreads; } @@ -307,7 +301,6 @@ public class CacheServerConfiguration extends PeerCacheConfiguration { return Optional.ofNullable(this.maxThreads).orElse(CacheServer.DEFAULT_MAX_THREADS); } - /* (non-Javadoc) */ void setMaxTimeBetweenPings(Integer maxTimeBetweenPings) { this.maxTimeBetweenPings = maxTimeBetweenPings; } @@ -316,7 +309,6 @@ public class CacheServerConfiguration extends PeerCacheConfiguration { return Optional.ofNullable(this.maxTimeBetweenPings).orElse(CacheServer.DEFAULT_MAXIMUM_TIME_BETWEEN_PINGS); } - /* (non-Javadoc) */ void setMessageTimeToLive(Integer messageTimeToLive) { this.messageTimeToLive = messageTimeToLive; } @@ -325,7 +317,6 @@ public class CacheServerConfiguration extends PeerCacheConfiguration { return Optional.ofNullable(this.messageTimeToLive).orElse(CacheServer.DEFAULT_MESSAGE_TIME_TO_LIVE); } - /* (non-Javadoc) */ void setPort(Integer port) { this.port = port; } @@ -334,7 +325,6 @@ public class CacheServerConfiguration extends PeerCacheConfiguration { return Optional.ofNullable(this.port).orElse(CacheServer.DEFAULT_PORT); } - /* (non-Javadoc) */ void setServerLoadProbe(ServerLoadProbe serverLoadProbe) { this.serverLoadProbe = serverLoadProbe; } @@ -343,7 +333,6 @@ public class CacheServerConfiguration extends PeerCacheConfiguration { return Optional.ofNullable(this.serverLoadProbe).orElse(CacheServer.DEFAULT_LOAD_PROBE); } - /* (non-Javadoc) */ void setSocketBufferSize(Integer socketBufferSize) { this.socketBufferSize = socketBufferSize; } @@ -352,7 +341,6 @@ public class CacheServerConfiguration extends PeerCacheConfiguration { return Optional.ofNullable(this.socketBufferSize).orElse(CacheServer.DEFAULT_SOCKET_BUFFER_SIZE); } - /* (non-Javadoc) */ void setSubscriptionCapacity(Integer subscriptionCapacity) { this.subscriptionCapacity = subscriptionCapacity; } @@ -361,7 +349,6 @@ public class CacheServerConfiguration extends PeerCacheConfiguration { return Optional.ofNullable(this.subscriptionCapacity).orElse(ClientSubscriptionConfig.DEFAULT_CAPACITY); } - /* (non-Javadoc) */ void setSubscriptionDiskStoreName(String subscriptionDiskStoreName) { this.subscriptionDiskStoreName = subscriptionDiskStoreName; } @@ -370,7 +357,6 @@ public class CacheServerConfiguration extends PeerCacheConfiguration { return this.subscriptionDiskStoreName; } - /* (non-Javadoc) */ void setSubscriptionEvictionPolicy(SubscriptionEvictionPolicy subscriptionEvictionPolicy) { this.subscriptionEvictionPolicy = subscriptionEvictionPolicy; } @@ -379,7 +365,6 @@ public class CacheServerConfiguration extends PeerCacheConfiguration { return Optional.ofNullable(this.subscriptionEvictionPolicy).orElse(SubscriptionEvictionPolicy.DEFAULT); } - /* (non-Javadoc) */ void setTcpNoDelay(Boolean tcpNoDelay) { this.tcpNoDelay = tcpNoDelay; } @@ -388,6 +373,14 @@ public class CacheServerConfiguration extends PeerCacheConfiguration { return Optional.ofNullable(this.tcpNoDelay).orElse(CacheServer.DEFAULT_TCP_NO_DELAY); } + /** + * Returns a {@link String} containing the name of the Spring-configured Apache Geode {@link CacheServer} application + * and data node in the cluster. + * + * @return a {@link String} containing the name of the Spring-configured Apache Geode {@link CacheServer} application + * and data node in the cluster. + * @see java.lang.Object#toString() + */ @Override public String toString() { return DEFAULT_NAME; diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/ClientCacheApplication.java b/src/main/java/org/springframework/data/gemfire/config/annotation/ClientCacheApplication.java index 973e0c01..ef3e86b9 100644 --- a/src/main/java/org/springframework/data/gemfire/config/annotation/ClientCacheApplication.java +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/ClientCacheApplication.java @@ -71,6 +71,16 @@ public @interface ClientCacheApplication { */ float criticalHeapPercentage() default ResourceManager.DEFAULT_CRITICAL_PERCENTAGE; + /** + * Configures the percentage of off-heap at or above which the cache is considered in danger of becoming inoperable. + * + * Defaults to {@literal 0.0}. + * + * Use {@literal spring.data.gemfire.cache.critical-off-heap-percentage} property + * in {@literal application.properties}. + */ + float criticalOffHeapPercentage() default 0.0f; + /** * Used only for clients in a client/server installation. If set, this indicates that the client is durable * and identifies the client. The ID is used by servers to reestablish any messaging that was interrupted @@ -101,6 +111,17 @@ public @interface ClientCacheApplication { */ float evictionHeapPercentage() default ResourceManager.DEFAULT_EVICTION_PERCENTAGE; + /** + * Configures the percentage of off-heap at or above which the eviction should begin on Regions configured + * for HeapLRU eviction. + * + * Defaults to {@literal 0.0}. + * + * Use {@literal spring.data.gemfire.cache.eviction-off-heap-percentage} property + * in {@literal application.properties}. + */ + float evictionOffHeapPercentage() default 0.0f; + /** * Configures the free connection timeout for this pool. * diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/ClientCacheConfiguration.java b/src/main/java/org/springframework/data/gemfire/config/annotation/ClientCacheConfiguration.java index 88c10ebd..1cc1d5ff 100644 --- a/src/main/java/org/springframework/data/gemfire/config/annotation/ClientCacheConfiguration.java +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/ClientCacheConfiguration.java @@ -19,6 +19,7 @@ package org.springframework.data.gemfire.config.annotation; import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeMap; +import java.lang.annotation.Annotation; import java.util.Collections; import java.util.List; import java.util.Map; @@ -160,6 +161,7 @@ public class ClientCacheConfiguration extends AbstractCacheConfiguration { return Optional.ofNullable(this.clientCacheConfigurers) .filter(clientCacheConfigurers -> !clientCacheConfigurers.isEmpty()) .orElseGet(() -> + Optional.of(this.getBeanFactory()) .filter(beanFactory -> beanFactory instanceof ListableBeanFactory) .map(beanFactory -> { @@ -230,8 +232,7 @@ public class ClientCacheConfiguration extends AbstractCacheConfiguration { if (isClientCacheApplication(importMetadata)) { - Map clientCacheApplicationAttributes = - importMetadata.getAnnotationAttributes(getAnnotationTypeName()); + AnnotationAttributes clientCacheApplicationAttributes = getAnnotationAttributes(importMetadata); setDurableClientId(resolveProperty(cacheClientProperty("durable-client-id"), (String) clientCacheApplicationAttributes.get("durableClientId"))); @@ -358,10 +359,13 @@ public class ClientCacheConfiguration extends AbstractCacheConfiguration { resolveProperty(poolProperty("locators"), (String) null)); if (StringUtils.hasText(locatorsFromProperty)) { + String[] locatorHostsPorts = locatorsFromProperty.split(","); + poolLocators = ConnectionEndpointList.parse(GemfireUtils.DEFAULT_LOCATOR_PORT, locatorHostsPorts); } else { + poolLocators = new ConnectionEndpointList(); AnnotationAttributes[] locators = (AnnotationAttributes[]) clientCacheApplicationAttributes.get("locators"); @@ -395,7 +399,6 @@ public class ClientCacheConfiguration extends AbstractCacheConfiguration { setPoolServers(poolServers); } - /* (non-Javadoc) */ protected ConnectionEndpoint newConnectionEndpoint(String host, Integer port) { return new ConnectionEndpoint(host, port); } @@ -404,11 +407,10 @@ public class ClientCacheConfiguration extends AbstractCacheConfiguration { * {@inheritDoc} */ @Override - protected Class getAnnotationType() { + protected Class getAnnotationType() { return ClientCacheApplication.class; } - /* (non-Javadoc) */ void setDurableClientId(String durableClientId) { this.durableClientId = durableClientId; } @@ -417,7 +419,6 @@ public class ClientCacheConfiguration extends AbstractCacheConfiguration { return this.durableClientId; } - /* (non-Javadoc) */ void setDurableClientTimeout(Integer durableClientTimeout) { this.durableClientTimeout = durableClientTimeout; } @@ -426,7 +427,6 @@ public class ClientCacheConfiguration extends AbstractCacheConfiguration { return this.durableClientTimeout; } - /* (non-Javadoc) */ void setFreeConnectionTimeout(Integer freeConnectionTimeout) { this.freeConnectionTimeout = freeConnectionTimeout; } @@ -435,7 +435,6 @@ public class ClientCacheConfiguration extends AbstractCacheConfiguration { return this.freeConnectionTimeout; } - /* (non-Javadoc) */ void setIdleTimeout(Long idleTimeout) { this.idleTimeout = idleTimeout; } @@ -444,7 +443,6 @@ public class ClientCacheConfiguration extends AbstractCacheConfiguration { return this.idleTimeout; } - /* (non-Javadoc) */ void setKeepAlive(Boolean keepAlive) { this.keepAlive = keepAlive; } @@ -453,7 +451,6 @@ public class ClientCacheConfiguration extends AbstractCacheConfiguration { return this.keepAlive; } - /* (non-Javadoc) */ void setLoadConditioningInterval(Integer loadConditioningInterval) { this.loadConditioningInterval = loadConditioningInterval; } @@ -462,7 +459,6 @@ public class ClientCacheConfiguration extends AbstractCacheConfiguration { return this.loadConditioningInterval; } - /* (non-Javadoc) */ void setMaxConnections(Integer maxConnections) { this.maxConnections = maxConnections; } @@ -471,7 +467,6 @@ public class ClientCacheConfiguration extends AbstractCacheConfiguration { return this.maxConnections; } - /* (non-Javadoc) */ void setMinConnections(Integer minConnections) { this.minConnections = minConnections; } @@ -480,7 +475,6 @@ public class ClientCacheConfiguration extends AbstractCacheConfiguration { return this.minConnections; } - /* (non-Javadoc) */ void setMultiUserAuthentication(Boolean multiUserAuthentication) { this.multiUserAuthentication = multiUserAuthentication; } @@ -489,7 +483,6 @@ public class ClientCacheConfiguration extends AbstractCacheConfiguration { return this.multiUserAuthentication; } - /* (non-Javadoc) */ void setPingInterval(Long pingInterval) { this.pingInterval = pingInterval; } @@ -498,7 +491,6 @@ public class ClientCacheConfiguration extends AbstractCacheConfiguration { return this.pingInterval; } - /* (non-Javadoc) */ void setPoolLocators(Iterable locators) { this.locators = locators; } @@ -507,7 +499,6 @@ public class ClientCacheConfiguration extends AbstractCacheConfiguration { return this.locators; } - /* (non-Javadoc) */ void setPoolServers(Iterable servers) { this.servers = servers; } @@ -516,7 +507,6 @@ public class ClientCacheConfiguration extends AbstractCacheConfiguration { return this.servers; } - /* (non-Javadoc) */ void setPrSingleHopEnabled(Boolean prSingleHopEnabled) { this.prSingleHopEnabled = prSingleHopEnabled; } @@ -525,7 +515,6 @@ public class ClientCacheConfiguration extends AbstractCacheConfiguration { return this.prSingleHopEnabled; } - /* (non-Javadoc) */ void setReadTimeout(Integer readTimeout) { this.readTimeout = readTimeout; } @@ -534,7 +523,6 @@ public class ClientCacheConfiguration extends AbstractCacheConfiguration { return this.readTimeout; } - /* (non-Javadoc) */ void setReadyForEvents(boolean readyForEvents) { this.readyForEvents = readyForEvents; } @@ -543,7 +531,6 @@ public class ClientCacheConfiguration extends AbstractCacheConfiguration { return this.readyForEvents; } - /* (non-Javadoc) */ void setRetryAttempts(Integer retryAttempts) { this.retryAttempts = retryAttempts; } @@ -552,7 +539,6 @@ public class ClientCacheConfiguration extends AbstractCacheConfiguration { return this.retryAttempts; } - /* (non-Javadoc) */ void setServerGroup(String serverGroup) { this.serverGroup = serverGroup; } @@ -561,7 +547,6 @@ public class ClientCacheConfiguration extends AbstractCacheConfiguration { return this.serverGroup; } - /* (non-Javadoc) */ void setSocketBufferSize(Integer socketBufferSize) { this.socketBufferSize = socketBufferSize; } @@ -570,7 +555,6 @@ public class ClientCacheConfiguration extends AbstractCacheConfiguration { return this.socketBufferSize; } - /* (non-Javadoc) */ void setStatisticsInterval(Integer statisticsInterval) { this.statisticsInterval = statisticsInterval; } @@ -579,7 +563,6 @@ public class ClientCacheConfiguration extends AbstractCacheConfiguration { return this.statisticsInterval; } - /* (non-Javadoc) */ void setSubscriptionAckInterval(Integer subscriptionAckInterval) { this.subscriptionAckInterval = subscriptionAckInterval; } @@ -588,7 +571,6 @@ public class ClientCacheConfiguration extends AbstractCacheConfiguration { return this.subscriptionAckInterval; } - /* (non-Javadoc) */ void setSubscriptionEnabled(Boolean subscriptionEnabled) { this.subscriptionEnabled = subscriptionEnabled; } @@ -597,7 +579,6 @@ public class ClientCacheConfiguration extends AbstractCacheConfiguration { return this.subscriptionEnabled; } - /* (non-Javadoc) */ void setSubscriptionMessageTrackingTimeout(Integer subscriptionMessageTrackingTimeout) { this.subscriptionMessageTrackingTimeout = subscriptionMessageTrackingTimeout; } @@ -606,7 +587,6 @@ public class ClientCacheConfiguration extends AbstractCacheConfiguration { return this.subscriptionMessageTrackingTimeout; } - /* (non-Javadoc) */ void setSubscriptionRedundancy(Integer subscriptionRedundancy) { this.subscriptionRedundancy = subscriptionRedundancy; } @@ -615,7 +595,6 @@ public class ClientCacheConfiguration extends AbstractCacheConfiguration { return this.subscriptionRedundancy; } - /* (non-Javadoc) */ void setThreadLocalConnections(Boolean threadLocalConnections) { this.threadLocalConnections = threadLocalConnections; } @@ -624,6 +603,14 @@ public class ClientCacheConfiguration extends AbstractCacheConfiguration { return this.threadLocalConnections; } + /** + * Returns a {@link String} containing the name of the Spring-configured Apache Geode + * {@link ClientCache} application. + * + * @return a {@link String} containing the name of the Spring-configured Apache Geode + * {@link ClientCache} application. + * @see java.lang.Object#toString() + */ @Override public String toString() { return DEFAULT_NAME; diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/PeerCacheApplication.java b/src/main/java/org/springframework/data/gemfire/config/annotation/PeerCacheApplication.java index 67e83a5d..fab9e55f 100644 --- a/src/main/java/org/springframework/data/gemfire/config/annotation/PeerCacheApplication.java +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/PeerCacheApplication.java @@ -70,6 +70,16 @@ public @interface PeerCacheApplication { */ float criticalHeapPercentage() default ResourceManager.DEFAULT_CRITICAL_PERCENTAGE; + /** + * Configures the percentage of off-heap at or above which the cache is considered in danger of becoming inoperable. + * + * Defaults to {@literal 0.0}. + * + * Use {@literal spring.data.gemfire.cache.critical-off-heap-percentage} property + * in {@literal application.properties}. + */ + float criticalOffHeapPercentage() default 0.0f; + /** * By default, a GemFire member (both locators and servers) will attempt to reconnect and reinitialize the cache * after it has been forced out of the distributed system by a network partition event or has otherwise been @@ -92,6 +102,17 @@ public @interface PeerCacheApplication { */ float evictionHeapPercentage() default ResourceManager.DEFAULT_EVICTION_PERCENTAGE; + /** + * Configures the percentage of off-heap at or above which the eviction should begin on Regions configured + * for HeapLRU eviction. + * + * Defaults to {@literal 0.0}. + * + * Use {@literal spring.data.gemfire.cache.eviction-off-heap-percentage} property + * in {@literal application.properties}. + */ + float evictionOffHeapPercentage() default 0.0f; + /** * Configures the list of Locators defining the cluster to which this Spring cache application will connect. * diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/PeerCacheConfiguration.java b/src/main/java/org/springframework/data/gemfire/config/annotation/PeerCacheConfiguration.java index c9c7eaad..c9f57ffc 100644 --- a/src/main/java/org/springframework/data/gemfire/config/annotation/PeerCacheConfiguration.java +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/PeerCacheConfiguration.java @@ -32,6 +32,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; +import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.type.AnnotationMetadata; import org.springframework.data.gemfire.CacheFactoryBean; import org.springframework.util.StringUtils; @@ -42,11 +43,15 @@ import org.springframework.util.StringUtils; * * @author John Blum * @see org.apache.geode.cache.Cache + * @see org.springframework.beans.factory.ListableBeanFactory * @see org.springframework.context.annotation.Bean * @see org.springframework.context.annotation.Configuration * @see org.springframework.context.annotation.Import + * @see org.springframework.data.gemfire.CacheFactoryBean * @see org.springframework.data.gemfire.config.annotation.AbstractCacheConfiguration * @see org.springframework.data.gemfire.config.annotation.AdministrativeConfiguration + * @see org.springframework.data.gemfire.config.annotation.PeerCacheApplication + * @see org.springframework.data.gemfire.config.annotation.PeerCacheConfigurer * @since 1.9.0 */ @Configuration @@ -102,6 +107,7 @@ public class PeerCacheConfiguration extends AbstractCacheConfiguration { return Optional.ofNullable(this.peerCacheConfigurers) .filter(peerCacheConfigurers -> !peerCacheConfigurers.isEmpty()) .orElseGet(() -> + Optional.of(this.getBeanFactory()) .filter(beanFactory -> beanFactory instanceof ListableBeanFactory) .map(beanFactory -> { @@ -144,8 +150,7 @@ public class PeerCacheConfiguration extends AbstractCacheConfiguration { if (isCacheServerOrPeerCacheApplication(importMetadata)) { - Map peerCacheApplicationAttributes = - importMetadata.getAnnotationAttributes(getAnnotationTypeName()); + AnnotationAttributes peerCacheApplicationAttributes = getAnnotationAttributes(importMetadata); if (peerCacheApplicationAttributes != null) { @@ -174,6 +179,10 @@ public class PeerCacheConfiguration extends AbstractCacheConfiguration { Optional.ofNullable(resolveProperty(cachePeerProperty("locators"), (String) null)) .filter(StringUtils::hasText) .ifPresent(this::setLocators); + + Optional.ofNullable(resolveProperty(propertyName("locators"), (String) null)) + .filter(StringUtils::hasText) + .ifPresent(this::setLocators); } } } @@ -186,7 +195,6 @@ public class PeerCacheConfiguration extends AbstractCacheConfiguration { return PeerCacheApplication.class; } - /* (non-Javadoc) */ void setEnableAutoReconnect(boolean enableAutoReconnect) { this.enableAutoReconnect = enableAutoReconnect; } @@ -195,7 +203,6 @@ public class PeerCacheConfiguration extends AbstractCacheConfiguration { return this.enableAutoReconnect; } - /* (non-Javadoc) */ void setLockLease(Integer lockLease) { this.lockLease = lockLease; } @@ -204,7 +211,6 @@ public class PeerCacheConfiguration extends AbstractCacheConfiguration { return this.lockLease; } - /* (non-Javadoc) */ void setLockTimeout(Integer lockTimeout) { this.lockTimeout = lockTimeout; } @@ -213,7 +219,6 @@ public class PeerCacheConfiguration extends AbstractCacheConfiguration { return this.lockTimeout; } - /* (non-Javadoc) */ void setMessageSyncInterval(Integer messageSyncInterval) { this.messageSyncInterval = messageSyncInterval; } @@ -222,7 +227,6 @@ public class PeerCacheConfiguration extends AbstractCacheConfiguration { return this.messageSyncInterval; } - /* (non-Javadoc) */ void setSearchTimeout(Integer searchTimeout) { this.searchTimeout = searchTimeout; } @@ -231,7 +235,6 @@ public class PeerCacheConfiguration extends AbstractCacheConfiguration { return this.searchTimeout; } - /* (non-Javadoc) */ void setUseClusterConfiguration(boolean useClusterConfiguration) { this.useClusterConfiguration = useClusterConfiguration; } @@ -240,6 +243,14 @@ public class PeerCacheConfiguration extends AbstractCacheConfiguration { return this.useClusterConfiguration; } + /** + * Returns a {@link String} containing the name of the Spring-configured Apache Geode peer {@link Cache} application + * and data node in the cluster. + * + * @return a {@link String} containing the name of the Spring-configured Apache Geode peer {@link Cache} application + * and data node in the cluster. + * @see java.lang.Object#toString() + */ @Override public String toString() { return DEFAULT_NAME; diff --git a/src/test/java/org/springframework/data/gemfire/client/GemFireDataSourceUsingNonSpringConfiguredGemFireServerIntegrationTest.java b/src/test/java/org/springframework/data/gemfire/client/GemFireDataSourceUsingNonSpringConfiguredGemFireServerIntegrationTest.java index 827ad0c9..74349f61 100644 --- a/src/test/java/org/springframework/data/gemfire/client/GemFireDataSourceUsingNonSpringConfiguredGemFireServerIntegrationTest.java +++ b/src/test/java/org/springframework/data/gemfire/client/GemFireDataSourceUsingNonSpringConfiguredGemFireServerIntegrationTest.java @@ -48,7 +48,7 @@ import org.springframework.data.gemfire.process.ProcessWrapper; import org.springframework.data.gemfire.test.support.FileSystemUtils; import org.springframework.data.gemfire.test.support.ThreadUtils; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.junit4.SpringRunner; import org.springframework.util.Assert; import org.springframework.util.FileCopyUtils; import org.springframework.util.StringUtils; @@ -64,7 +64,7 @@ import org.springframework.util.StringUtils; * @see org.springframework.data.gemfire.client.GemfireDataSourcePostProcessor * @since 1.7.0 */ -@RunWith(SpringJUnit4ClassRunner.class) +@RunWith(SpringRunner.class) @ContextConfiguration @SuppressWarnings({ "rawtypes", "unused"}) public class GemFireDataSourceUsingNonSpringConfiguredGemFireServerIntegrationTest { @@ -88,7 +88,8 @@ public class GemFireDataSourceUsingNonSpringConfiguredGemFireServerIntegrationTe @BeforeClass public static void setupBeforeClass() throws IOException { - System.setProperty("gemfire.log-level", "warning"); + + System.setProperty("gemfire.log-level", "error"); String serverName = "GemFireDataSourceGemFireBasedServer"; @@ -123,6 +124,7 @@ public class GemFireDataSourceUsingNonSpringConfiguredGemFireServerIntegrationTe } private static String customClasspath() { + String[] classpathElements = ProcessExecutor.JAVA_CLASSPATH.split(File.pathSeparator); List customClasspath = new ArrayList(classpathElements.length); @@ -137,6 +139,7 @@ public class GemFireDataSourceUsingNonSpringConfiguredGemFireServerIntegrationTe } private static void waitForProcessStart(final long milliseconds, final ProcessWrapper process, final String processControlFilename) { + ThreadUtils.timedWait(milliseconds, TimeUnit.MILLISECONDS.toMillis(500), new ThreadUtils.WaitCondition() { private File processControlFile = new File(process.getWorkingDirectory(), processControlFilename); @@ -148,6 +151,7 @@ public class GemFireDataSourceUsingNonSpringConfiguredGemFireServerIntegrationTe @AfterClass public static void tearDown() { + serverProcess.shutdown(); if (Boolean.valueOf(System.getProperty("spring.gemfire.fork.clean", Boolean.TRUE.toString()))) { @@ -156,6 +160,7 @@ public class GemFireDataSourceUsingNonSpringConfiguredGemFireServerIntegrationTe } protected void assertRegion(Region actualRegion, String expectedRegionName) { + assertThat(actualRegion, is(not(nullValue()))); assertThat(actualRegion.getName(), is(equalTo(expectedRegionName))); assertThat(actualRegion.getFullPath(), is(equalTo(String.format("%1$s%2$s", @@ -168,9 +173,9 @@ public class GemFireDataSourceUsingNonSpringConfiguredGemFireServerIntegrationTe @Test @SuppressWarnings("unchecked") public void clientProxyRegionBeansExist() { + assertRegion(localRegion, "LocalRegion"); assertRegion(serverRegion, "ServerRegion"); assertRegion(anotherServerRegion, "AnotherServerRegion"); } - } diff --git a/src/test/java/org/springframework/data/gemfire/config/annotation/AbstractCacheConfigurationUnitTests.java b/src/test/java/org/springframework/data/gemfire/config/annotation/AbstractCacheConfigurationUnitTests.java index 80844876..af7589e1 100644 --- a/src/test/java/org/springframework/data/gemfire/config/annotation/AbstractCacheConfigurationUnitTests.java +++ b/src/test/java/org/springframework/data/gemfire/config/annotation/AbstractCacheConfigurationUnitTests.java @@ -31,6 +31,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; +import java.lang.annotation.Annotation; import java.util.HashMap; import java.util.Map; @@ -235,7 +236,7 @@ public class AbstractCacheConfigurationUnitTests { protected static class TestCacheConfiguration extends AbstractCacheConfiguration { @Override - protected Class getAnnotationType() { + protected Class getAnnotationType() { throw new UnsupportedOperationException("Not Implemented"); } diff --git a/src/test/java/org/springframework/data/gemfire/config/annotation/ClientCachePropertiesIntegrationTests.java b/src/test/java/org/springframework/data/gemfire/config/annotation/ClientCachePropertiesIntegrationTests.java index 9538a1b8..7f16e1af 100644 --- a/src/test/java/org/springframework/data/gemfire/config/annotation/ClientCachePropertiesIntegrationTests.java +++ b/src/test/java/org/springframework/data/gemfire/config/annotation/ClientCachePropertiesIntegrationTests.java @@ -76,7 +76,9 @@ public class ClientCachePropertiesIntegrationTests { MockPropertySource testPropertySource = new MockPropertySource() .withProperty("spring.data.gemfire.cache.critical-heap-percentage", 90.0f) + .withProperty("spring.data.gemfire.cache.critical-off-heap-percentage", 95.0f) .withProperty("spring.data.gemfire.cache.eviction-heap-percentage", 85.0f) + .withProperty("spring.data.gemfire.cache.eviction-off-heap-percentage", 80.0f) .withProperty("spring.data.gemfire.pdx.ignore-unread-fields", false) .withProperty("spring.data.gemfire.pdx.persistent", true) .withProperty("spring.data.gemfire.pool.free-connection-timeout", 20000L) @@ -134,7 +136,9 @@ public class ClientCachePropertiesIntegrationTests { assertThat(resourceManager).isNotNull(); assertThat(resourceManager.getCriticalHeapPercentage()).isEqualTo(90.0f); + assertThat(resourceManager.getCriticalOffHeapPercentage()).isEqualTo(95.0f); assertThat(resourceManager.getEvictionHeapPercentage()).isEqualTo(90.0f); + assertThat(resourceManager.getEvictionOffHeapPercentage()).isEqualTo(80.0f); } @Test @@ -240,16 +244,17 @@ public class ClientCachePropertiesIntegrationTests { // TODO add more tests! @EnableGemFireMockObjects - @EnablePdx(ignoreUnreadFields = true, readSerialized = true, serializerBeanName = "mockPdxSerializer") @ClientCacheApplication(name = "TestClientCache", copyOnRead = true, criticalHeapPercentage = 95.0f, evictionHeapPercentage = 80.0f, idleTimeout = 15000L, maxConnections = 100, minConnections = 10, pingInterval = 15000L, readTimeout = 15000, retryAttempts = 1, subscriptionEnabled = true, subscriptionRedundancy = 1) + @EnablePdx(ignoreUnreadFields = true, readSerialized = true, serializerBeanName = "mockPdxSerializer") @SuppressWarnings("unused") static class TestClientCacheConfiguration { @Bean ClientCacheConfigurer testClientCacheConfigurer() { + return (beanName, factoryBean) -> { factoryBean.setEvictionHeapPercentage(90.0f); factoryBean.setPdxReadSerialized(false); diff --git a/src/test/java/org/springframework/data/gemfire/config/annotation/EnableOffHeapConfigurationUnitTests.java b/src/test/java/org/springframework/data/gemfire/config/annotation/EnableOffHeapConfigurationUnitTests.java index 876f5019..1af1ba36 100644 --- a/src/test/java/org/springframework/data/gemfire/config/annotation/EnableOffHeapConfigurationUnitTests.java +++ b/src/test/java/org/springframework/data/gemfire/config/annotation/EnableOffHeapConfigurationUnitTests.java @@ -23,6 +23,7 @@ import java.util.Optional; import org.apache.geode.cache.GemFireCache; import org.apache.geode.cache.Region; +import org.apache.geode.cache.control.ResourceManager; import org.junit.After; import org.junit.Test; import org.springframework.context.ConfigurableApplicationContext; @@ -68,6 +69,30 @@ public class EnableOffHeapConfigurationUnitTests { assertThat(region.getAttributes().getOffHeap()).isEqualTo(offHeapEnabled); } + @Test + public void offHeapCriticalAndEvictionMemoryPercentagesConfiguredProperly() { + + this.applicationContext = newApplicationContext(OffHeapCriticalAndEvictionMemoryPercentagesConfiguration.class); + + assertThat(this.applicationContext).isNotNull(); + + GemFireCache gemfireCache = this.applicationContext.getBean("gemfireCache", GemFireCache.class); + + assertThat(gemfireCache).isNotNull(); + assertThat(gemfireCache.getDistributedSystem()).isNotNull(); + assertThat(gemfireCache.getDistributedSystem().getProperties()).containsKey("off-heap-memory-size"); + assertThat(gemfireCache.getDistributedSystem().getProperties().getProperty("off-heap-memory-size")) + .isEqualTo("1024g"); + + ResourceManager resourceManager = gemfireCache.getResourceManager(); + + assertThat(resourceManager).isNotNull(); + assertThat(resourceManager.getCriticalHeapPercentage()).isEqualTo(95.55f); + assertThat(resourceManager.getCriticalOffHeapPercentage()).isEqualTo(90.5f); + assertThat(resourceManager.getEvictionHeapPercentage()).isEqualTo(85.75f); + assertThat(resourceManager.getEvictionOffHeapPercentage()).isEqualTo(75.25f); + } + @Test public void offHeapConfiguredForAllRegions() { @@ -160,7 +185,6 @@ public class EnableOffHeapConfigurationUnitTests { @EnableEntityDefinedRegions(basePackageClasses = Person.class) @EnableOffHeap(memorySize = "8192m") @Import(TestRegionConfiguration.class) - @SuppressWarnings("unused") static class EnableOffHeapForAllRegionsConfiguration { } @@ -169,7 +193,17 @@ public class EnableOffHeapConfigurationUnitTests { @EnableEntityDefinedRegions(basePackageClasses = Person.class) @EnableOffHeap(memorySize = "1024m", regionNames = { "People", "ExamplePartitionRegion" }) @Import(TestRegionConfiguration.class) - @SuppressWarnings("unused") static class EnableOffHeapForSelectRegionsConfiguration { } + + @EnableGemFireMockObjects + @PeerCacheApplication( + criticalHeapPercentage = 95.55f, + criticalOffHeapPercentage = 90.5f, + evictionHeapPercentage = 85.75f, + evictionOffHeapPercentage = 75.25f + ) + @EnableOffHeap(memorySize = "1024g") + static class OffHeapCriticalAndEvictionMemoryPercentagesConfiguration { + } } diff --git a/src/test/java/org/springframework/data/gemfire/config/annotation/PeerCacheApplicationIntegrationTests.java b/src/test/java/org/springframework/data/gemfire/config/annotation/PeerCacheApplicationIntegrationTests.java index b0d4e560..d432a49d 100644 --- a/src/test/java/org/springframework/data/gemfire/config/annotation/PeerCacheApplicationIntegrationTests.java +++ b/src/test/java/org/springframework/data/gemfire/config/annotation/PeerCacheApplicationIntegrationTests.java @@ -58,14 +58,12 @@ public class PeerCacheApplicationIntegrationTests { assertThat(echo.get("Test")).isEqualTo("Test"); } - //@EnableLocator - //@PeerCacheApplication(name = "PeerCacheApplicationIntegrationTests", - // logLevel = "warn", locators="localhost[10334]") @PeerCacheApplication(name = "PeerCacheApplicationIntegrationTests", logLevel="warn") static class PeerCacheApplicationConfiguration { @Bean("Echo") PartitionedRegionFactoryBean echoRegion(Cache gemfireCache) { + PartitionedRegionFactoryBean echoRegion = new PartitionedRegionFactoryBean(); @@ -78,7 +76,9 @@ public class PeerCacheApplicationIntegrationTests { } CacheLoader echoCacheLoader() { + return new CacheLoader() { + @Override public String load(LoaderHelper helper) throws CacheLoaderException { return helper.getKey(); diff --git a/src/test/java/org/springframework/data/gemfire/config/annotation/PeerCachePropertiesIntegrationTests.java b/src/test/java/org/springframework/data/gemfire/config/annotation/PeerCachePropertiesIntegrationTests.java index 69986ca9..bff8690d 100644 --- a/src/test/java/org/springframework/data/gemfire/config/annotation/PeerCachePropertiesIntegrationTests.java +++ b/src/test/java/org/springframework/data/gemfire/config/annotation/PeerCachePropertiesIntegrationTests.java @@ -75,7 +75,9 @@ public class PeerCachePropertiesIntegrationTests { MockPropertySource testPropertySource = new MockPropertySource() .withProperty("spring.data.gemfire.cache.copy-on-read", true) .withProperty("spring.data.gemfire.cache.critical-heap-percentage", 95.0f) + .withProperty("spring.data.gemfire.cache.critical-off-heap-percentage", 90.0f) .withProperty("spring.data.gemfire.cache.eviction-heap-percentage", 85.0f) + .withProperty("spring.data.gemfire.cache.eviction-off-heap-percentage", 80.0f) .withProperty("spring.data.gemfire.cache.peer.lock-lease", 180) .withProperty("spring.data.gemfire.cache.peer.lock-timeout", 30) .withProperty("spring.data.gemfire.cache.peer.search-timeout", 120) @@ -107,7 +109,9 @@ public class PeerCachePropertiesIntegrationTests { assertThat(resourceManager).isNotNull(); assertThat(resourceManager.getCriticalHeapPercentage()).isEqualTo(95.0f); + assertThat(resourceManager.getCriticalOffHeapPercentage()).isEqualTo(90.0f); assertThat(resourceManager.getEvictionHeapPercentage()).isEqualTo(85.0f); + assertThat(resourceManager.getEvictionOffHeapPercentage()).isEqualTo(80.0f); } @Test diff --git a/src/test/java/org/springframework/data/gemfire/test/mock/MockGemFireObjectsSupport.java b/src/test/java/org/springframework/data/gemfire/test/mock/MockGemFireObjectsSupport.java index 752c6ba3..e7c82142 100644 --- a/src/test/java/org/springframework/data/gemfire/test/mock/MockGemFireObjectsSupport.java +++ b/src/test/java/org/springframework/data/gemfire/test/mock/MockGemFireObjectsSupport.java @@ -1656,14 +1656,12 @@ public abstract class MockGemFireObjectsSupport extends MockObjectsSupport { AtomicReference criticalHeapPercentage = new AtomicReference<>(ResourceManager.DEFAULT_CRITICAL_PERCENTAGE); - AtomicReference criticalOffHeapPercentage = - new AtomicReference<>(ResourceManager.DEFAULT_CRITICAL_PERCENTAGE); + AtomicReference criticalOffHeapPercentage = new AtomicReference<>(0.0f); AtomicReference evictionHeapPercentage = new AtomicReference<>(ResourceManager.DEFAULT_EVICTION_PERCENTAGE); - AtomicReference evictionOffHeapPercentage = - new AtomicReference<>(ResourceManager.DEFAULT_EVICTION_PERCENTAGE); + AtomicReference evictionOffHeapPercentage = new AtomicReference<>(0.0f); doAnswer(newSetter(criticalHeapPercentage, null)) .when(mockResourceManager).setCriticalHeapPercentage(anyFloat());