DATAGEODE-238 - Create module for Spring Data GemFire.
Create spring-data-gemfire module. Create spring-data-geode module. Modularize the SDG project and convert pom.xml into a parent POM. Move all source files to spring-data-geode/src.
This commit is contained in:
@@ -1,6 +0,0 @@
|
||||
[[appendix-schema]]
|
||||
[appendix]
|
||||
= {sdg-name} Schema
|
||||
|
||||
* {spring-data-schema-location}[{sdg-name} Core Schema (`gfe` XML namespace)]
|
||||
* {spring-data-access-schema-location}[{sdg-name} Data Access Schema (`gfe-data` XML namespace)]
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 48 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 8.7 KiB |
@@ -1,87 +0,0 @@
|
||||
= Spring Data for {data-store-name} Reference Guide
|
||||
Costin Leau; David Turanski; John Blum; Oliver Gierke; Jay Bryant
|
||||
:revdate: {localdate}
|
||||
:revnumber: {version}
|
||||
:toclevels: 2
|
||||
:apache-geode-version: 19
|
||||
:apache-geode-docs: https://geode.apache.org/docs/guide/{apache-geode-version}
|
||||
:apache-geode-javadoc: https://geode.apache.org/releases/latest/javadoc
|
||||
:apache-geode-website: https://geode.apache.org
|
||||
:apache-geode-wiki: https://cwiki.apache.org/confluence/display/GEODE
|
||||
:data-store-name-symbolic: geode
|
||||
:data-store-name-simple: Geode
|
||||
:data-store-name: Apache {data-store-name-simple}
|
||||
:data-store-version: 1.9.0
|
||||
:pivotal-gemfire-version: 98
|
||||
:pivotal-gemfire-docs: https://gemfire.docs.pivotal.io/{pivotal-gemfire-version}
|
||||
:pivotal-gemfire-javadoc: https://gemfire-{pivotal-gemfire-version}-javadocs.docs.pivotal.io/
|
||||
:pivotal-gemfire-website: https://pivotal.io/pivotal-gemfire
|
||||
:pivotal-gemfire-wiki: https://cwiki.apache.org/confluence/display/GEODE
|
||||
:sdg-acronym: SDG
|
||||
:sdg-javadoc: https://docs.spring.io/spring-data/{data-store-name-symbolic}/docs/current/api
|
||||
:sdg-name: Spring Data for {data-store-name}
|
||||
:sdg-website: https://projects.spring.io/spring-data-gemfire
|
||||
:spring-data-access-schema-location: https://www.springframework.org/schema/data/geode/spring-data-geode.xsd
|
||||
:spring-data-access-schema-namespace: https://www.springframework.org/schema/data/geode
|
||||
:spring-data-commons-docs: https://docs.spring.io/spring-data/commons/docs/current/reference
|
||||
:spring-data-commons-include: ../../../../spring-data-commons/src/main/asciidoc
|
||||
:spring-data-commons-docs-html: {spring-data-commons-docs}/html
|
||||
:spring-data-commons-javadoc: https://docs.spring.io/spring-data/commons/docs/current/api
|
||||
:spring-data-schema-location: https://www.springframework.org/schema/geode/spring-geode.xsd
|
||||
:spring-data-schema-namespace: https://www.springframework.org/schema/geode
|
||||
:spring-data-website: https://spring.io/projects/spring-data
|
||||
:spring-framework-docs: https://docs.spring.io/spring/docs/current/spring-framework-reference
|
||||
:spring-framework-javadoc: https://docs.spring.io/spring/docs/current/javadoc-api
|
||||
:spring-framework-website: https://spring.io/projects/spring-framework
|
||||
:x-data-store-docs: {apache-geode-docs}
|
||||
:x-data-store-javadoc: {apache-geode-javadoc}
|
||||
:x-data-store-website: {apache-geode-website}
|
||||
:x-data-store-wiki: {apache-geode-wiki}
|
||||
ifdef::backend-epub3[:front-cover-image: image:epub-cover.png[Front Cover,1050,1600]]
|
||||
|
||||
(C) 2010-2019 The original authors.
|
||||
|
||||
NOTE: Copies of this document may be made for your own use and for distribution to others provided that you do not
|
||||
charge any fee for such copies and further provided that each copy contains this Copyright Notice
|
||||
whether distributed in print or electronically.
|
||||
|
||||
[[preface]]
|
||||
include::{basedocdir}/preface.adoc[]
|
||||
|
||||
include::{basedocdir}/introduction/introduction.adoc[leveloffset=+1]
|
||||
include::{basedocdir}/introduction/requirements.adoc[leveloffset=+1]
|
||||
include::{basedocdir}/introduction/new-features.adoc[leveloffset=+1]
|
||||
|
||||
[[reference]]
|
||||
= Reference Guide
|
||||
|
||||
include::{basedocdir}/reference/introduction.adoc[leveloffset=+1]
|
||||
include::{basedocdir}/reference/bootstrap.adoc[leveloffset=+1]
|
||||
include::{basedocdir}/reference/bootstrap-annotations.adoc[leveloffset=+1]
|
||||
include::{basedocdir}/reference/data.adoc[leveloffset=+1]
|
||||
include::{basedocdir}/reference/serialization.adoc[leveloffset=+1]
|
||||
include::{basedocdir}/reference/mapping.adoc[leveloffset=+1]
|
||||
include::{basedocdir}/reference/repositories.adoc[leveloffset=+1]
|
||||
include::{basedocdir}/reference/function-annotations.adoc[leveloffset=+1]
|
||||
include::{basedocdir}/reference/lucene.adoc[leveloffset=+1]
|
||||
include::{basedocdir}/reference/gemfire-bootstrap.adoc[leveloffset=+1]
|
||||
include::{basedocdir}/reference/samples.adoc[leveloffset=+1]
|
||||
|
||||
[[resources]]
|
||||
= Resources
|
||||
|
||||
In addition to this reference documentation, there are a number of other resources that may help you learn
|
||||
how to use {data-store-product-name} with the _Spring Framework_. These additional, third-party resources
|
||||
are enumerated in this section.
|
||||
|
||||
include::{basedocdir}/links.adoc[leveloffset=+1]
|
||||
|
||||
[[appendices]]
|
||||
= Appendices
|
||||
|
||||
:!sectnums:
|
||||
include::{spring-data-commons-include}/repository-namespace-reference.adoc[leveloffset=+1]
|
||||
include::{spring-data-commons-include}/repository-populator-namespace-reference.adoc[leveloffset=+1]
|
||||
include::{spring-data-commons-include}/repository-query-keywords-reference.adoc[leveloffset=+1]
|
||||
include::{spring-data-commons-include}/repository-query-return-types-reference.adoc[leveloffset=+1]
|
||||
include::{basedocdir}/appendix/appendix-schema.adoc[leveloffset=+1]
|
||||
@@ -1,6 +0,0 @@
|
||||
[[introduction]]
|
||||
= Introduction
|
||||
|
||||
The {sdg-name} reference guide explains how to use the Spring Framework
|
||||
to configure and develop applications with {data-store-name}. It presents the basic concepts
|
||||
and provides numerous examples to help you get started quickly.
|
||||
@@ -1,45 +0,0 @@
|
||||
[[new-features]]
|
||||
= New Features
|
||||
|
||||
NOTE: As of the 1.2.0.RELEASE, this project, formerly known as Spring GemFire, has been renamed to {sdg-name}
|
||||
to reflect that it is now a module of the {spring-data-website}[Spring Data] project and built on
|
||||
{x-data-store-website}[{data-store-name}].
|
||||
|
||||
[[new-in-2-0-0]]
|
||||
== New in the 2.0 Release
|
||||
|
||||
* Upgraded to {data-store-name} 9.1.1.
|
||||
* Upgraded to Spring Data Commons 2.0.8.RELEASE.
|
||||
* Upgraded to Spring Framework 5.0.7.RELEASE.
|
||||
* Reorganized the SDG codebase by packaging different classes and components by concern.
|
||||
* Added extensive support for Java 8 types, particularly in the SD Repository abstraction.
|
||||
* Changed to the Repository interface and abstraction, e.g. IDs are no longer required to be `java.io.Serializable`.
|
||||
* Set `@EnableEntityDefinedRegions` annotation `ignoreIfExists` attribute to `true` by default.
|
||||
* Set `@Indexed` annotation `override` attribute to `false` by default.
|
||||
* Renamed `@EnableIndexes` to `@EnableIndexing`.
|
||||
* Introduced a `InterestsBuilder` class to easily and conveniently express Interests in keys and values between client
|
||||
and server when using JavaConfig.
|
||||
* Added support in the Annotation configuration model for Off-Heap, Redis Adapter,
|
||||
and {data-store-name}'s new Security framework.
|
||||
|
||||
[[new-in-2-1-0]]
|
||||
== New in the 2.1 Release
|
||||
|
||||
* Upgraded to {data-store-name} {data-store-version}.
|
||||
* Upgraded to Spring Framework 5.1.0.RELEASE.
|
||||
* Upgraded to Spring Data Commons 2.1.0.RELEASE.
|
||||
* Added support for parallel cache/Region snapshots along with invoking callbacks when loading snapshots.
|
||||
* Added support for registering QueryPostProcessors to customize the OQL generated fro Repository query methods.
|
||||
* Added support for include/exclude TypeFilters in o.s.d.g.mapping.MappingPdxSerializer.
|
||||
* Updated docs.
|
||||
|
||||
[[new-in-2-2-0]]
|
||||
== New in the 2.2 Release
|
||||
|
||||
* Upgraded to {data-store-name} {data-store-version}.
|
||||
* Upgraded to Spring Framework 5.2.0.RELEASE.
|
||||
* Upgraded to Spring Data Commons 2.2.0.RELEASE.
|
||||
* Add Annotation configuration support to configure and bootstrap {data-store-name} Locator applications
|
||||
using `@LocatorApplication`.
|
||||
* Added Annotation configuration support for GatewayReceivers and GatewaySenders.
|
||||
* Updated docs.
|
||||
@@ -1,5 +0,0 @@
|
||||
[[requirements]]
|
||||
= Requirements
|
||||
|
||||
{sdg-name} requires Java 8.0, {spring-framework-website}[Spring Framework] 5
|
||||
and {x-data-store-website}[{data-store-name}] {data-store-version}.
|
||||
@@ -1,14 +0,0 @@
|
||||
[[sgf-links]]
|
||||
= Useful Links
|
||||
|
||||
* https://projects.spring.io/spring-data-gemfire[{sdg-name} Project Page]
|
||||
* https://github.com/spring-projects/spring-data-gemfire[{sdg-name} source code]
|
||||
* https://jira.spring.io/browse/SGF[{sdg-name} JIRA]
|
||||
* https://stackoverflow.com/questions/tagged/spring-data-gemfire[{sdg-name} on StackOverflow]
|
||||
* https://forum.spring.io/forum/spring-projects/data/gemfire[Archive of the {sdg-name} Forum on Spring IO]
|
||||
* {x-data-store-website}[{data-store-name} Home Page]
|
||||
* {x-data-store-docs}/getting_started/book_intro.html[{data-store-name} Documentation]
|
||||
* {apache-geode-website}/community/[Apache Geode Community]
|
||||
* https://github.com/apache/geode[Apache Geode source code]
|
||||
* https://issues.apache.org/jira/projects/GEODE/issues[Apache Geode JIRA]
|
||||
* https://stackoverflow.com/questions/tagged/gemfire[{data-store-name} on StackOverflow]
|
||||
@@ -1,14 +0,0 @@
|
||||
= Preface
|
||||
|
||||
{sdg-name} focuses on integrating the Spring Framework's powerful, non-invasive programming model
|
||||
and concepts with {data-store-name} to simplify configuration and development of Java applications
|
||||
when using {data-store-name} as you data management solution.
|
||||
|
||||
This document assumes you already have a basic understanding of, and some familiarity with, the core Spring Framework
|
||||
and {data-store-name} concepts.
|
||||
|
||||
While every effort has been made to ensure this documentation is comprehensive and complete without errors,
|
||||
some topics are beyond the scope of this document and may require more explanation (for example, data distribution management
|
||||
using partitioning with HA while still preserving consistency). Additionally, some typographical errors might have crept in.
|
||||
If you do spot mistakes or even more serious errors, please bring these issues to the attention of the Spring Data team
|
||||
by raising an appropriate https://jira.spring.io/browse/SGF[issue in JIRA].
|
||||
@@ -1,761 +0,0 @@
|
||||
[[bootstap-annotations-quickstart]]
|
||||
= Annotation-based Configuration Quick Start
|
||||
|
||||
The following sections provide an overview to the {sdg-acronym} annotations in order to get started quickly.
|
||||
|
||||
NOTE: All annotations provide additional configuration attributes along with associated <<bootstrap-annotation-config-properties, properties>>
|
||||
to conveniently customize the configuration and behavior of {data-store-name} at runtime. However, in general,
|
||||
none of the attributes or associated properties are required to use a particular {data-store-name} feature.
|
||||
Simply declare the annotation to enable the feature and you are done. Refer to the individual Javadoc of
|
||||
each annotation for more details.
|
||||
|
||||
[[bootstap-annotations-quickstart-clientcache]]
|
||||
== Configure a `ClientCache` Application
|
||||
|
||||
To configure and bootstrap a {data-store-name} `ClientCache` application, use the following:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@SpringBootApplication
|
||||
@ClientCacheApplication
|
||||
public class ClientApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ClientApplication.class, args);
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
See {sdg-javadoc}/org/springframework/data/gemfire/config/annotation/ClientCacheApplication.html[`@ClientCacheApplication` Javadoc].
|
||||
|
||||
See <<bootstrap-annotation-config-geode-applications>> for more details.
|
||||
|
||||
[[bootstap-annotations-quickstart-peercache]]
|
||||
== Configure a Peer `Cache` Application
|
||||
|
||||
To configure and bootstrap a {data-store-name} Peer `Cache` application, use the following:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@SpringBootApplication
|
||||
@PeerCacheApplication
|
||||
public class ServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ServerApplication.class, args);
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
NOTE: If you would like to enable a `CacheServer` that allows `ClientCache` applications to connect to this server,
|
||||
then simply replace the `@PeerCacheApplication` annotation with the `@CacheServerApplication` annotation. This will
|
||||
start a `CacheServer` running on "`localhost`", listening on the default `CacheServer` port of `40404`.
|
||||
|
||||
See {sdg-javadoc}/org/springframework/data/gemfire/config/annotation/CacheServerApplication.html[`@CacheServerApplication` Javadoc].
|
||||
|
||||
See {sdg-javadoc}/org/springframework/data/gemfire/config/annotation/PeerCacheApplication.html[`@PeerCacheApplication` Javadoc].
|
||||
|
||||
See <<bootstrap-annotation-config-geode-applications>> for more details.
|
||||
|
||||
[[bootstap-annotations-quickstart-locator]]
|
||||
== Configure an Embedded Locator
|
||||
|
||||
Annotate your Spring `@PeerCacheApplication` or `@CacheServerApplication` class with `@EnableLocator` to start
|
||||
an embedded Locator bound to all NICs listening on the default Locator port, `10334`, as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@SpringBootApplication
|
||||
@CacheServerApplication
|
||||
@EnableLocator
|
||||
public class ServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ServerApplication.class, args);
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
NOTE: `@EnableLocator` can only be used with {data-store-name} server applications.
|
||||
|
||||
See {sdg-javadoc}/org/springframework/data/gemfire/config/annotation/EnableLocator.html[`@EnableLocator` Javadoc].
|
||||
|
||||
See <<bootstrap-annotation-config-embedded-services-locator>> for more details.
|
||||
|
||||
[[bootstap-annotations-quickstart-manager]]
|
||||
== Configure an Embedded Manager
|
||||
|
||||
Annotate your Spring `@PeerCacheApplication` or `@CacheServerApplication` class with `@EnableManager` to start
|
||||
an embedded Manager bound to all NICs listening on the default Manager port, `1099`, as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@SpringBootApplication
|
||||
@CacheServerApplication
|
||||
@EnableManager
|
||||
public class ServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ServerApplication.class, args);
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
NOTE: `@EnableManager` can only be used with {data-store-name} server applications.
|
||||
|
||||
See {sdg-javadoc}/org/springframework/data/gemfire/config/annotation/EnableManager.html[`@EnableManager` Javadoc].
|
||||
|
||||
See <<bootstrap-annotation-config-embedded-services-manager>> for more details.
|
||||
|
||||
[[bootstap-annotations-quickstart-httpserver]]
|
||||
== Configure the Embedded HTTP Server
|
||||
|
||||
Annotate your Spring `@PeerCacheApplication` or `@CacheServerApplication` class with `@EnableHttpService` to start
|
||||
the embedded HTTP server (Jetty) listening on port `7070`, as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@SpringBootApplication
|
||||
@CacheServerApplication
|
||||
@EnableHttpService
|
||||
public class ServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ServerApplication.class, args);
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
NOTE: `@EnableHttpService` can only be used with {data-store-name} server applications.
|
||||
|
||||
See {sdg-javadoc}/org/springframework/data/gemfire/config/annotation/EnableHttpService.html[`@EnableHttpService` Javadoc].
|
||||
|
||||
See <<bootstrap-annotation-config-embedded-services-http>> for more details.
|
||||
|
||||
[[bootstap-annotations-quickstart-memcachedserver]]
|
||||
== Configure the Embedded Memcached Server
|
||||
|
||||
Annotate your Spring `@PeerCacheApplication` or `@CacheServerApplication` class with `@EnableMemcachedServer` to start
|
||||
the embedded Memcached server (Gemcached) listening on port `11211`, as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@SpringBootApplication
|
||||
@CacheServerApplication
|
||||
@EnableMemcachedServer
|
||||
public class ServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ServerApplication.class, args);
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
NOTE: `@EnableMemcachedServer` can only be used with {data-store-name} server applications.
|
||||
|
||||
See {sdg-javadoc}/org/springframework/data/gemfire/config/annotation/EnableMemcachedServer.html[`@EnableMemcachedServer` Javadoc].
|
||||
|
||||
See <<bootstrap-annotation-config-embedded-services-memcached>> for more details.
|
||||
|
||||
[[bootstap-annotations-quickstart-redisserver]]
|
||||
== Configure the Embedded Redis Server
|
||||
|
||||
Annotate your Spring `@PeerCacheApplication` or `@CacheServerApplication` class with `@EnableRedisServer` to start
|
||||
the embedded Redis server listening on port `6379`, as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@SpringBootApplication
|
||||
@CacheServerApplication
|
||||
@EnableRedisServer
|
||||
public class ServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ServerApplication.class, args);
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
NOTE: `@EnableRedisServer` can only be used with {data-store-name} server applications.
|
||||
|
||||
WARNING: You must explicitly declare the `org.apache.geode:geode-redis` module on your Spring [Boot] application
|
||||
classpath.
|
||||
|
||||
See {sdg-javadoc}/org/springframework/data/gemfire/config/annotation/EnableRedisServer.html[`@EnableRedisServer` Javadoc].
|
||||
|
||||
See <<bootstrap-annotation-config-embedded-services-redis>> for more details.
|
||||
|
||||
[[bootstap-annotations-quickstart-logging]]
|
||||
== Configure Logging
|
||||
|
||||
To configure or adjust {data-store-name} logging, annotate your Spring, {data-store-name} client or server
|
||||
application class with `@EnableLogging`, as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@SpringBootApplication
|
||||
@ClientCacheApplication
|
||||
@EnableLogging(logLevel="trace")
|
||||
public class ClientApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ClientApplication.class, args);
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
NOTE: Default `log-level` is "`config`". Also, this annotation will not adjust log levels in your application,
|
||||
only for {data-store-name}.
|
||||
|
||||
See {sdg-javadoc}/org/springframework/data/gemfire/config/annotation/EnableLogging.html[`@EnableLogging` Javadoc].
|
||||
|
||||
See <<bootstrap-annotation-config-logging>> for more details.
|
||||
|
||||
[[bootstap-annotations-quickstart-statistics]]
|
||||
== Configure Statistics
|
||||
|
||||
To gather {data-store-name} statistics at runtime, annotate your Spring, {data-store-name} client or server
|
||||
application class with `@EnableStatistics`, as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@SpringBootApplication
|
||||
@ClientCacheApplication
|
||||
@EnableStatistics
|
||||
public class ClientApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ClientApplication.class, args);
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
See {sdg-javadoc}/org/springframework/data/gemfire/config/annotation/EnableStatistics.html[`@EnableStatistics` Javadoc].
|
||||
|
||||
See <<bootstrap-annotation-config-statistics>> for more details.
|
||||
|
||||
[[bootstap-annotations-quickstart-pdx]]
|
||||
== Configure PDX
|
||||
|
||||
To enable {data-store-name} PDX serialization, annotate your Spring, {data-store-name} client or server
|
||||
application class with `@EnablePdx`, as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@SpringBootApplication
|
||||
@ClientCacheApplication
|
||||
@EnablePdx
|
||||
public class ClientApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ClientApplication.class, args);
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
NOTE: {data-store-name} PDX Serialization is an alternative to Java Serialization with many added benefits. For one,
|
||||
it makes short work of making all of your application domain model types serializable without having to implement
|
||||
`java.io.Serializable`.
|
||||
|
||||
NOTE: By default, {sdg-acronym} configures the `MappingPdxSerializer` to serialize your application domain model types,
|
||||
which does not require any special configuration out-of-the-box in order to properly identify application domain objects
|
||||
that need to be serialized and then perform the serialization since, the logic in `MappingPdxSerializer` is based on
|
||||
Spring Data's mapping infrastructure. See <<mapping.pdx-serializer>> for more details.
|
||||
|
||||
See {sdg-javadoc}/org/springframework/data/gemfire/config/annotation/EnablePdx.html[`@EnablePdx` Javadoc].
|
||||
|
||||
See <<bootstrap-annotation-config-pdx>> for more details.
|
||||
|
||||
[[bootstap-annotations-quickstart-ssl]]
|
||||
== Configure SSL
|
||||
|
||||
To enable {data-store-name} SSL, annotate your Spring, {data-store-name} client or server application class
|
||||
with `@EnableSsl`, as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@SpringBootApplication
|
||||
@ClientCacheApplication
|
||||
@EnableSsl(components = SERVER)
|
||||
public class ClientApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ClientApplication.class, args);
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
NOTE: Minimally, {data-store-name} requires you to specify a keystore & truststore using the appropriate configuration
|
||||
attributes or properties. Both keystore & truststore configuration attributes or properties may refer to the same
|
||||
`KeyStore` file. Additionally, you will need to specify a username and password to access the `KeyStore` file
|
||||
if the file has been secured.
|
||||
|
||||
NOTE: {data-store-name} SSL allows you to configure the specific components of the system that require TLS, such as
|
||||
client/server, Locators, Gateways, etc. Optionally, you can specify that all components of {data-store-name}
|
||||
use SSL with "`ALL`".
|
||||
|
||||
See {sdg-javadoc}/org/springframework/data/gemfire/config/annotation/EnableSsl.html[`@EnableSsl` Javadoc].
|
||||
|
||||
See <<bootstrap-annotation-config-ssl>> for more details.
|
||||
|
||||
[[bootstap-annotations-quickstart-security]]
|
||||
== Configure Security
|
||||
|
||||
To enable {data-store-name} security, annotate your Spring, {data-store-name} client or server application class
|
||||
with `@EnableSecurity`, as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@SpringBootApplication
|
||||
@ClientCacheApplication
|
||||
@EnableSecurity
|
||||
public class ClientApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ClientApplication.class, args);
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
NOTE: On the server, you must configure access to the auth credentials. You may either implement the {data-store-name}
|
||||
{x-data-store-javadoc}/org/apache/geode/security/SecurityManager.html[`SecurityManager`] interface or declare
|
||||
1 or more Apache Shiro `Realms`. See <<bootstrap-annotation-config-security-server>> for more details.
|
||||
|
||||
NOTE: On the client, you must configure a username and password. See <<bootstrap-annotation-config-security-client>>
|
||||
for more details.
|
||||
|
||||
See {sdg-javadoc}/org/springframework/data/gemfire/config/annotation/EnableSecurity.html[`@EnableSecurity` Javadoc].
|
||||
|
||||
See <<bootstrap-annotation-config-security>> for more details.
|
||||
|
||||
[[bootstap-annotations-quickstart-properties]]
|
||||
== Configure {data-store-name} Properties
|
||||
|
||||
To configure other, low-level {data-store-name} properties not covered by the feature-oriented, {sdg-acronym}
|
||||
configuration annotations, annotate your Spring, {data-store-name} client or server application class
|
||||
with `@GemFireProperties`, as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@SpringBootApplication
|
||||
@PeerCacheApplication
|
||||
@EnableGemFireProperties(
|
||||
cacheXmlFile = "/path/to/cache.xml",
|
||||
conserveSockets = true,
|
||||
groups = "GroupOne",
|
||||
remoteLocators = "lunchbox[11235],mailbox[10101],skullbox[12480]"
|
||||
)
|
||||
public class ServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ServerApplication.class, args);
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
NOTE: Some {data-store-name} properties are client-side only while others are server-side only. Please review the
|
||||
{data-store-name} {x-data-store-docs}/reference/topics/gemfire_properties.html[docs] for the appropriate use
|
||||
of each property.
|
||||
|
||||
See {sdg-javadoc}/org/springframework/data/gemfire/config/annotation/EnableGemFireProperties.html[`@EnableGemFireProperties` Javadoc].
|
||||
|
||||
See <<bootstrap-annotation-config-gemfire-properties>> for more details.
|
||||
|
||||
[[bootstap-annotations-quickstart-caching]]
|
||||
== Configure Caching
|
||||
|
||||
To use {data-store-name} as a _caching provider_ in Spring's {spring-framework-docs}/integration.html#cache[_Cache Abstraction_],
|
||||
and have {sdg-acronym} automatically create {data-store-name} Regions for the caches required by your application
|
||||
service components, then annotate your Spring, {data-store-name} client or server application class
|
||||
with `@EnableGemfireCaching` and `@EnableCachingDefinedRegions`, as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@SpringBootApplication
|
||||
@ClientCacheApplication
|
||||
@EnableCachingDefinedRegions
|
||||
@EnableGemfireCaching
|
||||
public class ClientApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ClientApplication.class, args);
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Then, simply go on to define the application services that require caching, as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Service
|
||||
public class BookService {
|
||||
|
||||
@Cacheable("Books")
|
||||
public Book findBy(ISBN isbn) {
|
||||
...
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
NOTE: `@EnableCachingDefinedRegions` is optional. That is, you may manually define your Regions if you desire.
|
||||
|
||||
See {sdg-javadoc}/org/springframework/data/gemfire/config/annotation/EnableCachingDefinedRegions.html[`@EnableCachingDefinedRegions` Javadoc].
|
||||
|
||||
See {sdg-javadoc}/org/springframework/data/gemfire/cache/config/EnableGemfireCaching.html[`@EnableGemfireCaching` Javadoc].
|
||||
|
||||
See <<bootstrap-annotation-config-caching>> for more details.
|
||||
|
||||
[[bootstap-annotations-quickstart-repositories]]
|
||||
== Configure Regions, Indexes, Repositories and Entities for Persistent Applications
|
||||
|
||||
To make short work of creating Spring, {data-store-name} persistent client or server applications, annotate your
|
||||
application class with `@EnableEntityDefinedRegions`, `@EnableGemfireRepositories` and `@EnableIndexing`, as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@SpringBootApplication
|
||||
@ClientCacheApplication
|
||||
@EnableEntityDefinedRegions(basePackageClasses = Book.class)
|
||||
@EnableGemfireRepositories(basePackageClasses = BookRepository.class)
|
||||
@EnableIndexing
|
||||
public class ClientApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ClientApplication.class, args);
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
NOTE: The `@EnableEntityDefinedRegions` annotation is required when using the `@EnableIndexing` annotation.
|
||||
See <<bootstrap-annotation-config-region-indexes>> for more details.
|
||||
|
||||
Next, define your entity class and use the `@Region` mapping annotation to specify the Region in which your entity
|
||||
will be stored. Use the `@Indexed` annotation to define Indexes on entity fields used in your application queries,
|
||||
as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
package example.app.model;
|
||||
|
||||
import ...;
|
||||
|
||||
@Region("Books")
|
||||
public class Book {
|
||||
|
||||
@Id
|
||||
private ISBN isbn;
|
||||
|
||||
@Indexed;
|
||||
private Author author;
|
||||
|
||||
@Indexed
|
||||
private LocalDate published;
|
||||
|
||||
@LuceneIndexed
|
||||
private String title;
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
NOTE: The `@Region("Books")` entity class annotation is used by the `@EnableEntityDefinedRegions` to determine
|
||||
the Regions required by the application. See <<bootstrap-annotation-config-region-types>> and <<mapping>>
|
||||
for more details.
|
||||
|
||||
Finally, define your CRUD Repository with simple queries to persist and access `Books`, as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
package example.app.repo;
|
||||
|
||||
import ...;
|
||||
|
||||
public interface BookRepository extends CrudRepository {
|
||||
|
||||
List<Book> findByAuthorOrderByPublishedDesc(Author author);
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
TIP: See <<gemfire-repositories>> for more details.
|
||||
|
||||
See {sdg-javadoc}/org/springframework/data/gemfire/config/annotation/EnableEntityDefinedRegions.html[`@EnableEntityDefinedRegions` Javadoc].
|
||||
|
||||
See {sdg-javadoc}/org/springframework/data/gemfire/repository/config/EnableGemfireRepositories.html[`@EnableGemfireRepositories` Javadoc].
|
||||
|
||||
See {sdg-javadoc}/org/springframework/data/gemfire/config/annotation/EnableIndexing.html[`@EnableIndexing` Javadoc].
|
||||
|
||||
See {sdg-javadoc}/org/springframework/data/gemfire/mapping/annotation/Region.html[`@Region` Javadoc].
|
||||
|
||||
See {sdg-javadoc}/org/springframework/data/gemfire/mapping/annotation/Indexed.html[`@Indexed` Javadoc].
|
||||
|
||||
See {sdg-javadoc}/org/springframework/data/gemfire/mapping/annotation/LuceneIndexed.html[`@LuceneIndexed` Javadoc].
|
||||
|
||||
See <<bootstrap-annotation-config-regions>> for more details.
|
||||
|
||||
See <<gemfire-repositories>> for more details.
|
||||
|
||||
[[bootstap-annotations-quickstart-cluster-defined-regions]]
|
||||
== Configure Client Regions from Cluster-defined Regions
|
||||
|
||||
Alternatively, you can define client [*PROXY] Regions from Regions already defined in the cluster
|
||||
using `@EnableClusterDefinedRegions`, as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@SpringBootApplication
|
||||
@ClientCacheApplication
|
||||
@EnableClusterDefinedRegions
|
||||
@EnableGemfireRepositories
|
||||
public class ClientApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ClientApplication.class, args);
|
||||
}
|
||||
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
See <<bootstrap-annotation-config-region-cluster-defined>> for more details.
|
||||
|
||||
[[bootstap-annotations-quickstart-functions]]
|
||||
== Configure Functions
|
||||
|
||||
{data-store-name} Functions are useful in distributed compute scenarios where a potentially expensive computation
|
||||
requiring data can be performed in parallel across the nodes in the cluster. In this case, it is more efficient
|
||||
to bring the logic to where the data is located (stored) rather than requesting and fetching the data to be processed
|
||||
by the computation.
|
||||
|
||||
Use the `@EnableGemfireFunctions` along with the `@GemfireFunction` annotation to enable {data-store-name} Functions
|
||||
definitions implemented as methods on POJOs, as follows:
|
||||
|
||||
[source, java]
|
||||
----
|
||||
@PeerCacheApplication
|
||||
@EnableGemfireFunctions
|
||||
class ServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ServerApplication.class, args);
|
||||
}
|
||||
|
||||
@GemfireFunction
|
||||
Integer computeLoyaltyPoints(Customer customer) {
|
||||
...
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Use the `@EnableGemfireFunctionExecutions` along with 1 of the Function calling annotations: `@OnMember`, `@OnMembers`,
|
||||
`@OnRegion`, `@OnServer` and `@OnServers`.
|
||||
|
||||
[source, java]
|
||||
----
|
||||
@ClientCacheApplication
|
||||
@EnableGemfireFunctionExecutions(basePackageClasses = CustomerRewardsFunction.class)
|
||||
class ClientApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ClientApplication.class, args);
|
||||
}
|
||||
}
|
||||
|
||||
@OnRegion("Customers")
|
||||
interface CustomerRewardsFunctions {
|
||||
|
||||
Integer computeLoyaltyPoints(Customer customer);
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
See {sdg-javadoc}/org/springframework/data/gemfire/function/config/EnableGemfireFunctions.html[`@EnableGemfireFunctions` Javadoc].
|
||||
|
||||
See {sdg-javadoc}/org/springframework/data/gemfire/function/annotation/GemfireFunction.html[`@GemfireFunction` Javadoc].
|
||||
|
||||
See {sdg-javadoc}/org/springframework/data/gemfire/function/config/EnableGemfireFunctionExecutions.html[`@EnableGemfireFunctionExecutions` Javadoc].
|
||||
|
||||
See {sdg-javadoc}/org/springframework/data/gemfire/function/annotation/OnMember.html[`@OnMember` Javadoc],
|
||||
{sdg-javadoc}/org/springframework/data/gemfire/function/annotation/OnMembers.html[`@OnMembers` Javadoc],
|
||||
{sdg-javadoc}/org/springframework/data/gemfire/function/annotation/OnRegion.html[`@OnRegion` Javadoc],
|
||||
{sdg-javadoc}/org/springframework/data/gemfire/function/annotation/OnServer.html[`@OnServer` Javadoc],
|
||||
and {sdg-javadoc}/org/springframework/data/gemfire/function/annotation/OnServers.html[`@OnServers` Javadoc].
|
||||
|
||||
See <<function-annotations>> for more details.
|
||||
|
||||
[[bootstap-annotations-quickstart-continuousquery]]
|
||||
== Configure Continuous Query
|
||||
|
||||
Real-time, event stream processing is becoming an increasingly important task for data-intensive applications,
|
||||
primarily in order to respond to user requests in a timely manner. {data-store-name} Continuous Query (CQ)
|
||||
will help you achieve this rather complex task quite easily.
|
||||
|
||||
Enable CQ by annotating your application class with `@EnableContinuousQueries` and define your CQs along with
|
||||
the associated event handlers, as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@ClientCacheApplication
|
||||
@EnableContinuousQueries
|
||||
class ClientApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ClientApplication.class, args);
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Then, define your CQs by annotating the associated handler method with `@ContinousQuery`, as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Service
|
||||
class CustomerService {
|
||||
|
||||
@ContinuousQuery(name = "CustomerQuery", query = "SELECT * FROM /Customers c WHERE ...")
|
||||
public void process(CqEvent event) {
|
||||
...
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Anytime an event occurs changing the `Customer` data to match the predicate in your continuous OQL query (CQ),
|
||||
the `process` method will be called.
|
||||
|
||||
NOTE: {data-store-name} CQ is a client-side feature only.
|
||||
|
||||
See {sdg-javadoc}/org/springframework/data/gemfire/config/annotation/EnableContinuousQueries.html[`@EnableContinuousQueries` Javadoc].
|
||||
|
||||
See {sdg-javadoc}/org/springframework/data/gemfire/listener/annotation/ContinuousQuery.html[`@ContinuousQuery` Javadoc].
|
||||
|
||||
See <<apis:continuous-query>> and <<bootstrap-annotation-config-continuous-queries>> for more details.
|
||||
|
||||
[[bootstap-annotations-quickstart-cluster-configuration]]
|
||||
== Configure Cluster Configuration
|
||||
|
||||
When developing Spring Data applications using {data-store-name} as {data-store-name} `ClientCache` applications, it is
|
||||
useful during development to configure the server to match the client in a client/server topology. In fact,
|
||||
{data-store-name} expects that when you have a "/Example" PROXY `Region` on the client, that a matching `Region` by name
|
||||
(i.e. "Example") exists in the server.
|
||||
|
||||
You could use _Gfsh_ to create every Region and Index that your application requires, or, you could simply push
|
||||
the configuration meta-data already expressed when developing your Spring Data application using {data-store-name}
|
||||
when you run it.
|
||||
|
||||
This is as simple as annotation your main application class with `@EnableClusterConfiguration(..)`:
|
||||
|
||||
.Using `@EnableClusterConfiguration`
|
||||
[source,java]
|
||||
----
|
||||
@ClientCacheApplication
|
||||
@EnableClusterConfiguration(useHttp = true)
|
||||
class ClientApplication {
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
NOTE: Most of the time, when using a client/server topology, particularly in production environments, the servers
|
||||
of the cluster will be started using _Gfsh_. In which case, it customary to use HTTP(S) to send the configuration
|
||||
metadata (e.g. Region & Index definitions) to the cluster. When HTTP is used, the configuration metadata is sent
|
||||
to the Manager in the cluster and distributed across the server nodes in the cluster consistently.
|
||||
|
||||
WARNING: In order to use `@EnableClusterConfiguration` you must declare the `org.springframework:spring-web` dependency
|
||||
in your Spring application classpath.
|
||||
|
||||
See {sdg-javadoc}/org/springframework/data/gemfire/config/annotation/EnableClusterConfiguration.html[`@EnableClusterConfiguration` Javadoc].
|
||||
|
||||
See <<bootstrap-annotation-config-cluster>> for more details.
|
||||
|
||||
[[bootstap-annotations-quickstart-gatewayreceiver]]
|
||||
== Configure `GatewayReceivers`
|
||||
|
||||
The replication of data between different {data-store-name} clusters is an increasingly important fault-tolerance
|
||||
and high-availability (HA) mechanism. {data-store-name} WAN replication is a mechanism that allows one
|
||||
{data-store-name} cluster to replicate its data to another {data-store-name} cluster in a reliable and fault-tolerant
|
||||
manner.
|
||||
|
||||
{data-store-name} WAN replication requires two components to be configured:
|
||||
|
||||
* `GatewayReceiver` - The WAN replication component that receives data from a remote {data-store-name} cluster's `GatewaySender`.
|
||||
* `GatewaySender` - The WAN replication component that sends data to a remote {data-store-name} cluster's `GatewayReceiver`.
|
||||
|
||||
To enable a `GatewayReceiver`, the application class needs to be annotated with `@EnableGatewayReceiver` as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@CacheServerApplication
|
||||
@EnableGatewayReceiver(manualStart = false, startPort = 10000, endPort = 11000, maximumTimeBetweenPings = 1000,
|
||||
socketBufferSize = 16384, bindAddress = "localhost",transportFilters = {"transportBean1", "transportBean2"},
|
||||
hostnameForSenders = "hostnameLocalhost"){
|
||||
...
|
||||
...
|
||||
}
|
||||
}
|
||||
class MySpringApplication { .. }
|
||||
----
|
||||
|
||||
NOTE: {data-store-name} `GatewayReceiver` is a server-side feature only and can only be configured on a `CacheServer`
|
||||
or peer `Cache` node.
|
||||
|
||||
See {sdg-javadoc}/org/springframework/data/gemfire/wan/annotation/EnableGatewayReceiver.html[`@EnableGatewayReceiver` Javadoc].
|
||||
|
||||
[[bootstap-annotations-quickstart-gatewaysenders]]
|
||||
== Configure `GatewaySenders`
|
||||
|
||||
To enable `GatewaySender`, the application class needs to be annotated with `@EnableGatewaySenders`
|
||||
and `@EnableGatewaySender` as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@CacheServerApplication
|
||||
@EnableGatewaySenders(gatewaySenders = {
|
||||
@EnableGatewaySender(name = "GatewaySender", manualStart = true,
|
||||
remoteDistributedSystemId = 2, diskSynchronous = true, batchConflationEnabled = true,
|
||||
parallel = true, persistent = false,diskStoreReference = "someDiskStore",
|
||||
orderPolicy = OrderPolicyType.PARTITION, alertThreshold = 1234, batchSize = 100,
|
||||
eventFilters = "SomeEventFilter", batchTimeInterval = 2000, dispatcherThreads = 22,
|
||||
maximumQueueMemory = 400,socketBufferSize = 16384,
|
||||
socketReadTimeout = 4000, regions = { "Region1"}),
|
||||
@EnableGatewaySender(name = "GatewaySender2", manualStart = true,
|
||||
remoteDistributedSystemId = 2, diskSynchronous = true, batchConflationEnabled = true,
|
||||
parallel = true, persistent = false, diskStoreReference = "someDiskStore",
|
||||
orderPolicy = OrderPolicyType.PARTITION, alertThreshold = 1234, batchSize = 100,
|
||||
eventFilters = "SomeEventFilter", batchTimeInterval = 2000, dispatcherThreads = 22,
|
||||
maximumQueueMemory = 400, socketBufferSize = 16384,socketReadTimeout = 4000,
|
||||
regions = { "Region2" })
|
||||
}){
|
||||
class MySpringApplication { .. }
|
||||
}
|
||||
----
|
||||
NOTE: {data-store-name} `GatewaySender` is a server-side feature only and can only be configured on a `CacheServer`
|
||||
or a peer `Cache` node.
|
||||
|
||||
In the above example, the application is configured with 2 Regions, `Region1` and `Region2`. In addition,
|
||||
two `GatewaySenders` will be configured to service both Regions. `GatewaySender1` will be configured to replicate
|
||||
`Region1`'s data and `GatewaySender2` will be configured to replicate `Region2`'s data.
|
||||
|
||||
As demonstrated each `GatewaySender` property can be configured on each `EnableGatewaySender` annotation.
|
||||
|
||||
It is also possible to have a more generic, "defaulted" properties approach, where all properties are configured on
|
||||
the `EnableGatewaySenders` annotation. This way, a set of generic, defaulted values can be set on the parent annotation
|
||||
and then overridden on the child if required, as demonstrated below:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@CacheServerApplication
|
||||
@EnableGatewaySenders(gatewaySenders = {
|
||||
@EnableGatewaySender(name = "GatewaySender", transportFilters = "transportBean1", regions = "Region2"),
|
||||
@EnableGatewaySender(name = "GatewaySender2")},
|
||||
manualStart = true, remoteDistributedSystemId = 2,
|
||||
diskSynchronous = false, batchConflationEnabled = true, parallel = true, persistent = true,
|
||||
diskStoreReference = "someDiskStore", orderPolicy = OrderPolicyType.PARTITION, alertThreshold = 1234, batchSize = 1002,
|
||||
eventFilters = "SomeEventFilter", batchTimeInterval = 2000, dispatcherThreads = 22, maximumQueueMemory = 400,
|
||||
socketBufferSize = 16384, socketReadTimeout = 4000, regions = { "Region1", "Region2" },
|
||||
transportFilters = { "transportBean2", "transportBean1" })
|
||||
class MySpringApplication { .. }
|
||||
----
|
||||
|
||||
NOTE: When the `regions` attribute is left empty or not populated, the `GatewaySender`(s) will automatically attach
|
||||
itself to every configured `Region` within the application.
|
||||
|
||||
See {sdg-javadoc}/org/springframework/data/gemfire/wan/annotation/EnableGatewaySenders.html[`@EnableGatewaySenders` Javadoc]
|
||||
and {sdg-javadoc}/org/springframework/data/gemfire/wan/annotation/EnableGatewaySender.html[`@EnableGatewaySender` Javadoc].
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,115 +0,0 @@
|
||||
[[bootstrap]]
|
||||
= Bootstrapping {data-store-name} with the Spring Container
|
||||
|
||||
{sdg-name} provides full configuration and initialization of the {data-store-name} In-Memory Data Grid (IMDG)
|
||||
using the Spring IoC container. The framework includes several classes to help simplify the configuration of
|
||||
{data-store-name} components, including: Caches, Regions, Indexes, DiskStores, Functions, WAN Gateways,
|
||||
persistence backup, and several other Distributed System components to support a variety of application use cases
|
||||
with minimal effort.
|
||||
|
||||
NOTE: This section assumes basic familiarity with {data-store-name}. For more information, see the {data-store-name}
|
||||
{x-data-store-docs}/gemfire/about_gemfire.html[product documentation].
|
||||
|
||||
[[bootstrap:namespace:xml]]
|
||||
== Advantages of using Spring over {data-store-name} `cache.xml`
|
||||
|
||||
{sdg-name}'s XML namespace supports full configuration of the {data-store-name} In-Memory Data Grid (IMDG).
|
||||
The XML namespace is one of two ways to configure {data-store-name} in a Spring context in order to properly manage
|
||||
{data-store-name}'s lifecycle inside the Spring container. The other way to configure {data-store-name} in a Spring
|
||||
context is by using <<bootstrap-annotation-config,annotation-based configuration>>.
|
||||
|
||||
While support for {data-store-name}'s native `cache.xml` persists for legacy reasons, {data-store-name} application developers
|
||||
who use XML configuration are encouraged to do everything in Spring XML to take advantage of the many wonderful things
|
||||
Spring has to offer, such as modular XML configuration, property placeholders and overrides,
|
||||
SpEL ({spring-framework-docs}/core.html#expressions[Spring Expression Language]), and environment profiles.
|
||||
Behind the XML namespace, {sdg-name} makes extensive use of Spring's `FactoryBean` pattern to simplify the creation,
|
||||
configuration, and initialization of {data-store-name} components.
|
||||
|
||||
{data-store-name} provides several callback interfaces, such as `CacheListener`, `CacheLoader`, and `CacheWriter`,
|
||||
that let developers add custom event handlers. Using Spring's IoC container, you can configure these callbacks
|
||||
as normal Spring beans and inject them into {data-store-name} components. This is a significant improvement over
|
||||
native `cache.xml`, which provides relatively limited configuration options and requires callbacks to implement
|
||||
{data-store-name}'s `Declarable` interface (see <<apis:declarable>> to see how you can still use `Declarables`
|
||||
within Spring's container).
|
||||
|
||||
In addition, IDEs, such as the Spring Tool Suite (STS), provide excellent support for Spring XML namespaces,
|
||||
including code completion, pop-up annotations, and real time validation.
|
||||
|
||||
[[bootstrap:namespace]]
|
||||
== Using the Core Namespace
|
||||
|
||||
To simplify configuration, {sdg-name} provides a dedicated XML namespace for configuring core {data-store-name}
|
||||
components. It is possible to configure beans directly by using Spring's standard `<bean>` definition. However,
|
||||
all bean properties are exposed through the XML namespace, so there is little benefit to using raw bean definitions.
|
||||
|
||||
NOTE: For more information about XML Schema-based configuration in Spring, see the
|
||||
{spring-framework-docs}/core.html#appendix[appendix] in the Spring Framework reference documentation.
|
||||
|
||||
NOTE: Spring Data Repository support uses a separate XML namespace. See <<gemfire-repositories>> for more information
|
||||
on how to configure {sdg-name} Repositories.
|
||||
|
||||
To use the {sdg-name} XML namespace, declare it in your Spring XML configuration meta-data,
|
||||
as the following example shows:
|
||||
|
||||
[source,xml]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:gfe="{spring-data-schema-namespace}" <!--1--><!--2-->
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
{spring-data-schema-namespace} {spring-data-schema-location} <!--3-->
|
||||
">
|
||||
|
||||
<bean id ... >
|
||||
|
||||
<gfe:cache ...> <!--4-->
|
||||
|
||||
</beans>
|
||||
----
|
||||
<1> {sdg-name} XML namespace prefix. Any name works, but, throughout this reference documentation, `gfe` is used.
|
||||
<2> The XML namespace prefix is mapped to the URI.
|
||||
<3> The XML namespace URI location. Note that, even though the location points to an external address (which does exist
|
||||
and is valid), Spring resolves the schema locally, as it is included in the {sdg-name} library.
|
||||
<4> Example declaration using the XML namespace with the `gfe` prefix.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
You can change the default namespace from `beans` to `gfe`. This is useful for XML configuration composed mainly of
|
||||
{data-store-name} components, as it avoids declaring the prefix. To do so, swap the namespace prefix declaration
|
||||
shown earlier, as the following example shows:
|
||||
|
||||
[source,xml]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="{spring-data-schema-namespace}" <!--1-->
|
||||
xmlns:beans="http://www.springframework.org/schema/beans" <!--2-->
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
{spring-data-schema-namespace} {spring-data-schema-location}
|
||||
">
|
||||
|
||||
<beans:bean id ... > <!--3-->
|
||||
|
||||
<cache ...> <!--4-->
|
||||
|
||||
</beans>
|
||||
----
|
||||
<1> The default namespace declaration for this XML document points to the {sdg-name} XML namespace.
|
||||
<2> The `beans` namespace prefix declaration for Spring's raw bean definitions.
|
||||
<3> Bean declaration using the `beans` namespace. Notice the prefix.
|
||||
<4> Bean declaration using the `gfe` namespace. Notice the lack of prefix since `gfe` is the default namespace.
|
||||
====
|
||||
|
||||
include::{basedocdir}/reference/data-access.adoc[leveloffset=+1]
|
||||
include::{basedocdir}/reference/cache.adoc[leveloffset=+1]
|
||||
include::{basedocdir}/reference/region.adoc[leveloffset=+1]
|
||||
include::{basedocdir}/reference/indexing.adoc[leveloffset=+1]
|
||||
include::{basedocdir}/reference/diskstore.adoc[leveloffset=+1]
|
||||
include::{basedocdir}/reference/snapshot.adoc[leveloffset=+1]
|
||||
include::{basedocdir}/reference/function.adoc[leveloffset=+1]
|
||||
include::{basedocdir}/reference/gateway.adoc[v]
|
||||
@@ -1,437 +0,0 @@
|
||||
[[bootstrap:cache]]
|
||||
= Configuring a Cache
|
||||
|
||||
To use {data-store-name}, you need to either create a new cache or connect to an existing one. With the current version
|
||||
of {data-store-name}, you can have only one open cache per VM (more strictly speaking, per `ClassLoader`). In most cases,
|
||||
the cache should only be created once.
|
||||
|
||||
NOTE: This section describes the creation and configuration of a peer `Cache` member, appropriate in peer-to-peer (P2P)
|
||||
topologies and cache servers. A `Cache` member can also be used in stand-alone applications and integration tests.
|
||||
However, in typical production systems, most application processes act as cache clients, creating a `ClientCache`
|
||||
instance instead. This is described in the <<bootstrap:cache:client>> and <<bootstrap:region:client>> sections.
|
||||
|
||||
A peer `Cache` with default configuration can be created with the following simple declaration:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<gfe:cache/>
|
||||
----
|
||||
|
||||
During Spring container initialization, any `ApplicationContext` containing this cache definition registers a
|
||||
`CacheFactoryBean` that creates a Spring bean named `gemfireCache`, which references a {data-store-name} `Cache` instance.
|
||||
This bean refers to either an existing `Cache` or, if one does not already exist, a newly created one. Since no
|
||||
additional properties were specified, a newly created `Cache` applies the default cache configuration.
|
||||
|
||||
All {sdg-name} components that depend on the `Cache` respect this naming convention, so you need not explicitly declare
|
||||
the `Cache` dependency. If you prefer, you can make the dependency explicit by using the `cache-ref` attribute provided
|
||||
by various {sdg-acronym} XML namespace elements. Also, you can override the cache's bean name using the `id` attribute,
|
||||
as follows:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<gfe:cache id="myCache"/>
|
||||
----
|
||||
|
||||
A {data-store-name} `Cache` can be fully configured using Spring. However, {data-store-name}'s native XML configuration
|
||||
file, `cache.xml`, is also supported. For situations where the {data-store-name} cache needs to be configured natively,
|
||||
you can provide a reference to the {data-store-name} XML configuration file by using the `cache-xml-location` attribute,
|
||||
as follows:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<gfe:cache id="cacheConfiguredWithNativeCacheXml" cache-xml-location="classpath:cache.xml"/>
|
||||
----
|
||||
|
||||
In this example, if a cache needs to be created, it uses a file named `cache.xml` located in the classpath root
|
||||
to configure it.
|
||||
|
||||
NOTE: The configuration makes use of Spring's https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#resources[`Resource`]
|
||||
abstraction to locate the file. The `Resource` abstraction lets various search patterns be used, depending on the runtime environment
|
||||
or the prefix specified (if any) in the resource location.
|
||||
|
||||
In addition to referencing an external XML configuration file, you can also specify {data-store-name} System
|
||||
{x-data-store-docs}/reference/topics/gemfire_properties.html[properties] that use any of Spring's `Properties`
|
||||
support features.
|
||||
|
||||
For example, you can use the `properties` element defined in the `util` namespace to define `Properties` directly
|
||||
or load properties from a properties file, as follows:
|
||||
|
||||
[source,xml]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:gfe="{spring-data-schema-namespace}"
|
||||
xmlns:util="http://www.springframework.org/schema/util"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
{spring-data-schema-namespace} {spring-data-schema-location}
|
||||
http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd
|
||||
">
|
||||
|
||||
<util:properties id="gemfireProperties" location="file:/path/to/gemfire.properties"/>
|
||||
|
||||
<gfe:cache properties-ref="gemfireProperties"/>
|
||||
|
||||
</beans>
|
||||
----
|
||||
|
||||
Using a properties file is recommended for externalizing environment-specific settings
|
||||
outside the application configuration.
|
||||
|
||||
NOTE: Cache settings apply only when a new cache needs to be created. If an open cache already exists in the VM,
|
||||
these settings are ignored.
|
||||
|
||||
[[bootstrap:cache:advanced]]
|
||||
== Advanced Cache Configuration
|
||||
|
||||
For advanced cache configuration, the `cache` element provides a number of configuration options exposed as attributes
|
||||
or child elements, as the following listing shows:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<!--1-->
|
||||
<gfe:cache
|
||||
cache-xml-location=".."
|
||||
properties-ref=".."
|
||||
close="false"
|
||||
copy-on-read="true"
|
||||
critical-heap-percentage="90"
|
||||
eviction-heap-percentage="70"
|
||||
enable-auto-reconnect="false" <!--2-->
|
||||
lock-lease="120"
|
||||
lock-timeout="60"
|
||||
message-sync-interval="1"
|
||||
pdx-serializer-ref="myPdxSerializer"
|
||||
pdx-persistent="true"
|
||||
pdx-disk-store="diskStore"
|
||||
pdx-read-serialized="false"
|
||||
pdx-ignore-unread-fields="true"
|
||||
search-timeout="300"
|
||||
use-bean-factory-locator="true" <!--3-->
|
||||
use-cluster-configuration="false" <!--4-->
|
||||
>
|
||||
|
||||
<gfe:transaction-listener ref="myTransactionListener"/> <!--5-->
|
||||
|
||||
<gfe:transaction-writer> <!--6-->
|
||||
<bean class="org.example.app.gemfire.transaction.TransactionWriter"/>
|
||||
</gfe:transaction-writer>
|
||||
|
||||
<gfe:gateway-conflict-resolver ref="myGatewayConflictResolver"/> <!--7-->
|
||||
|
||||
<gfe:jndi-binding jndi-name="myDataSource" type="ManagedDataSource"/> <!--8-->
|
||||
|
||||
</gfe:cache>
|
||||
----
|
||||
|
||||
<1> Attributes support various cache options. For further information regarding anything shown in this example,
|
||||
see the {data-store-name} https://docs.pivotal.io/gemfire[product documentation].
|
||||
The `close` attribute determines whether the cache should be closed when the Spring application context is closed.
|
||||
The default is `true`. However, for use cases in which multiple application contexts use the cache
|
||||
(common in web applications), set this value to `false`.
|
||||
<2> Setting the `enable-auto-reconnect` attribute to `true` (the default is `false`) lets a disconnected {data-store-name} member
|
||||
automatically reconnect and rejoin the {data-store-name} cluster.
|
||||
See the {data-store-name} {x-data-store-docs}/managing/autoreconnect/member-reconnect.html[product documentation]
|
||||
for more details.
|
||||
<3> Setting the `use-bean-factory-locator` attribute to `true` (it defaults to `false`) applies only when both
|
||||
Spring (XML) configuration metadata and {data-store-name} `cache.xml` is used to configure the {data-store-name} cache node
|
||||
(whether client or peer). This option lets {data-store-name} components (such as `CacheLoader`) expressed in `cache.xml`
|
||||
be auto-wired with beans (such as `DataSource`) defined in the Spring application context. This option is typically
|
||||
used in conjunction with `cache-xml-location`.
|
||||
<4> Setting the `use-cluster-configuration` attribute to `true` (the default is `false`) enables a {data-store-name} member to
|
||||
retrieve the common, shared Cluster-based configuration from a Locator.
|
||||
See the {data-store-name} {x-data-store-docs}/configuring/cluster_config/gfsh_persist.html[product documentation]
|
||||
for more details.
|
||||
<5> Example of a `TransactionListener` callback declaration that uses a bean reference. The referenced bean must implement
|
||||
{x-data-store-javadoc}/org/apache/geode/cache/TransactionListener.html[TransactionListener].
|
||||
A `TransactionListener` can be implemented to handle transaction related events (such as afterCommit and afterRollback).
|
||||
<6> Example of a `TransactionWriter` callback declaration using an inner bean declaration. The bean must implement
|
||||
{x-data-store-javadoc}/org/apache/geode/cache/TransactionWriter.html[TransactionWriter].
|
||||
The `TransactionWriter` is a callback that can veto a transaction.
|
||||
<7> Example of a `GatewayConflictResolver` callback declaration using a bean reference. The referenced bean
|
||||
must implement {x-data-store-javadoc}/org/apache/geode/cache/util/GatewayConflictResolver.html
|
||||
[GatewayConflictResolver].
|
||||
A `GatewayConflictResolver` is a `Cache`-level plugin that is called upon to decide what to do with events
|
||||
that originate in other systems and arrive through the WAN Gateway.
|
||||
which provides a distributed Region creation service.
|
||||
<8> Declares a JNDI binding to enlist an external DataSource in a {data-store-name} transaction.
|
||||
|
||||
[[bootstrap:cache:pdx-serialization]]
|
||||
=== Enabling PDX Serialization
|
||||
|
||||
The preceding example includes a number of attributes related to {data-store-name}'s enhanced serialization framework, PDX.
|
||||
While a complete discussion of PDX is beyond the scope of this reference guide, it is important to note that PDX
|
||||
is enabled by registering a `PdxSerializer`, which is specified by setting the `pdx-serializer` attribute.
|
||||
|
||||
{data-store-name} provides an implementing class (`org.apache.geode.pdx.ReflectionBasedAutoSerializer`) that uses
|
||||
Java Reflection. However, it is common for developers to provide their own implementation. The value of the attribute
|
||||
is simply a reference to a Spring bean that implements the `PdxSerializer` interface.
|
||||
|
||||
More information on serialization support can be found in <<serialization>>.
|
||||
|
||||
[[boostrap:cache:auto-reconnect]]
|
||||
=== Enabling Auto-reconnect
|
||||
|
||||
You should be careful when setting the `<gfe:cache enable-auto-reconnect="[true|false*]>` attribute to `true`.
|
||||
|
||||
Generally, 'auto-reconnect' should only be enabled in cases where {sdg-name}'s XML namespace is used to configure
|
||||
and bootstrap a new, non-application {data-store-name} server added to a cluster. In other words, 'auto-reconnect'
|
||||
should not be enabled when {sdg-name} is used to develop and build a {data-store-name} application that also happens
|
||||
to be a peer `Cache` member of the {data-store-name} cluster.
|
||||
|
||||
The main reason for this restriction is that most {data-store-name} applications use references to the {data-store-name}
|
||||
`Cache` or Regions in order to perform data access operations. These references are "`injected`" by the Spring container
|
||||
into application components (such as Repositories) for use by the application. When a peer member is forcefully
|
||||
disconnected from the rest of the cluster, presumably because the peer member has become unresponsive or a
|
||||
network partition separates one or more peer members into a group too small to function as an independent
|
||||
distributed system, the peer member shuts down and all {data-store-name} component references (caches, Regions,
|
||||
and others) become invalid.
|
||||
|
||||
Essentially, the current forced disconnect processing logic in each peer member dismantles the system from the ground up.
|
||||
The JGroups stack shuts down, the distributed system is put in a shutdown state and, finally, the cache is closed.
|
||||
Effectively, all memory references become stale and are lost.
|
||||
|
||||
After being disconnected from the distributed system, a peer member enters a "`reconnecting`" state and periodically
|
||||
attempts to rejoin the distributed system. If the peer member succeeds in reconnecting, the member rebuilds its "`view`"
|
||||
of the distributed system from existing members and receives a new distributed system ID. Additionally, all caches,
|
||||
Regions, and other {data-store-name} components are reconstructed. Therefore, all old references, which may have been
|
||||
injected into application by the Spring container, are now stale and no longer valid.
|
||||
|
||||
{data-store-name} makes no guarantee (even when using the {data-store-name} public Java API) that application cache,
|
||||
Regions, or other component references are automatically refreshed by the reconnect operation. As such, {data-store-name}
|
||||
applications must take care to refresh their own references.
|
||||
|
||||
Unfortunately, there is no way to be notified of a disconnect event and, subsequently, a reconnect event either.
|
||||
If that were the case, you would have a clean way to know when to call `ConfigurableApplicationContext.refresh()`,
|
||||
if it were even applicable for an application to do so, which is why this "`feature`" of {data-store-name} is not
|
||||
recommended for peer `Cache` applications.
|
||||
|
||||
For more information about 'auto-reconnect', see {data-store-name}'s
|
||||
{x-data-store-docs}/managing/autoreconnect/member-reconnect.html[product documentation].
|
||||
|
||||
[[bootstrap:cache:cluster-configuration]]
|
||||
=== Using Cluster-based Configuration
|
||||
|
||||
{data-store-name}'s Cluster Configuration Service is a convenient way for any peer member joining the cluster to get
|
||||
a "`consistent view`" of the cluster by using the shared, persistent configuration maintained by a Locator.
|
||||
Using the cluster-based configuration ensures the peer member's configuration is compatible with the {data-store-name}
|
||||
Distributed System when the member joins.
|
||||
|
||||
This feature of {sdg-name} (setting the `use-cluster-configuration` attribute to `true`) works in the same way
|
||||
as the `cache-xml-location` attribute, except the source of the {data-store-name} configuration meta-data comes
|
||||
from the network through a Locator, as opposed to a native `cache.xml` file residing in the local file system.
|
||||
|
||||
All {data-store-name} native configuration metadata, whether from `cache.xml` or from the Cluster Configuration Service,
|
||||
gets applied before any Spring (XML) configuration metadata. As a result, Spring's config serves to "`augment`" the
|
||||
native {data-store-name} configuration metadata and would most likely be specific to the application.
|
||||
|
||||
Again, to enable this feature, specify the following in the Spring XML config:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<gfe:cache use-cluster-configuration="true"/>
|
||||
----
|
||||
|
||||
NOTE: While certain {data-store-name} tools, such as _Gfsh_, have their actions "`recorded`" when schema-like changes
|
||||
are made (for example, `gfsh>create region --name=Example --type=PARTITION`), {sdg-name}'s configuration metadata
|
||||
is not recorded. The same is true when using {data-store-name}'s public Java API directly. It, too, is not recorded.
|
||||
|
||||
For more information on {data-store-name}'s Cluster Configuration Service, see the
|
||||
{x-data-store-docs}/configuring/cluster_config/gfsh_persist.html[product documentation].
|
||||
|
||||
[[bootstrap:cache:server]]
|
||||
== Configuring a {data-store-name} CacheServer
|
||||
|
||||
{sdg-name} includes dedicated support for configuring a
|
||||
{x-data-store-javadoc}/org/apache/geode/cache/server/CacheServer.html[CacheServer],
|
||||
allowing complete configuration through the Spring container, as the following example shows:
|
||||
|
||||
[source,xml]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:gfe="{spring-data-schema-namespace}"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
|
||||
{spring-data-schema-namespace} {spring-data-schema-location}
|
||||
">
|
||||
|
||||
<gfe:cache/>
|
||||
|
||||
<!-- Example depicting serveral {data-store-name} CacheServer configuration options -->
|
||||
<gfe:cache-server id="advanced-config" auto-startup="true"
|
||||
bind-address="localhost" host-name-for-clients="localhost" port="${gemfire.cache.server.port}"
|
||||
load-poll-interval="2000" max-connections="22" max-message-count="1000" max-threads="16"
|
||||
max-time-between-pings="30000" groups="test-server">
|
||||
|
||||
<gfe:subscription-config eviction-type="ENTRY" capacity="1000" disk-store="file://${java.io.tmpdir}"/>
|
||||
|
||||
</gfe:cache-server>
|
||||
|
||||
<context:property-placeholder location="classpath:cache-server.properties"/>
|
||||
|
||||
</beans>
|
||||
----
|
||||
|
||||
The preceding configuration shows the `cache-server` element and the many available options.
|
||||
|
||||
NOTE: Rather than hard-coding the port, this configuration uses Spring's
|
||||
https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#xsd-config-body-schemas-context[context]
|
||||
namespace to declare a `property-placeholder`. A
|
||||
https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-factory-placeholderconfigurer[property placeholder]
|
||||
reads one or more properties files and then replaces property placeholders with values at runtime. Doing so lets administrators
|
||||
change values without having to touch the main application configuration. Spring also provides
|
||||
https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#expressions[SpEL]
|
||||
and an https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-environment[environment abstraction]
|
||||
to support externalization of environment-specific properties from the main codebase, easing deployment across multiple machines.
|
||||
|
||||
NOTE: To avoid initialization problems, the `CacheServer` started by {sdg-name} starts *after* the Spring container
|
||||
has been fully initialized. Doing so lets potential Regions, listeners, writers or instantiators that are defined
|
||||
declaratively to be fully initialized and registered before the server starts accepting connections. Keep this in mind
|
||||
when programmatically configuring these elements, as the server might start before your components and thus not be seen
|
||||
by the clients connecting right away.
|
||||
|
||||
[[bootstrap:cache:client]]
|
||||
== Configuring a {data-store-name} ClientCache
|
||||
|
||||
In addition to defining a {data-store-name} peer {x-data-store-javadoc}/org/apache/geode/cache/Cache.html[`Cache`],
|
||||
{sdg-name} also supports the definition of a {data-store-name} {x-data-store-javadoc}/org/apache/geode/cache/client/ClientCache.html[`ClientCache`]
|
||||
in a Spring container. A `ClientCache` definition is similar in configuration and use to the {data-store-name} peer <<bootstrap:cache,Cache>>
|
||||
and is supported by the `org.springframework.data.gemfire.client.ClientCacheFactoryBean`.
|
||||
|
||||
The simplest definition of a {data-store-name} cache client using default configuration follows:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<beans>
|
||||
<gfe:client-cache/>
|
||||
</beans>
|
||||
----
|
||||
|
||||
`client-cache` supports many of the same options as the <<bootstrap:cache:advanced,Cache>> element. However, as opposed
|
||||
to a full-fledged peer `Cache` member, a cache client connects to a remote cache server through a Pool. By default,
|
||||
a Pool is created to connect to a server running on `localhost` and listening to port `40404`. The default Pool is used
|
||||
by all client Regions unless the Region is configured to use a specific Pool.
|
||||
|
||||
Pools can be defined with the `pool` element. This client-side Pool can be used to configure connectivity directly to
|
||||
a server for individual entities or for the entire cache through one or more Locators.
|
||||
|
||||
For example, to customize the default Pool used by the `client-cache`, the developer needs to define a Pool and wire it
|
||||
to the cache definition, as the following example shows:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<beans>
|
||||
<gfe:client-cache id="myCache" pool-name="myPool"/>
|
||||
|
||||
<gfe:pool id="myPool" subscription-enabled="true">
|
||||
<gfe:locator host="${gemfire.locator.host}" port="${gemfire.locator.port}"/>
|
||||
</gfe:pool>
|
||||
</beans>
|
||||
----
|
||||
|
||||
The `<client-cache>` element also has a `ready-for-events` attribute. If the attribute is set to `true`, the client cache
|
||||
initialization includes a call to {x-data-store-javadoc}/org/apache/geode/cache/client/ClientCache.html#readyForEvents[`ClientCache.readyForEvents()`].
|
||||
|
||||
<<bootstrap:region:client>> covers client-side configuration in more detail.
|
||||
|
||||
[[bootstrap:cache:client:pool]]
|
||||
=== {data-store-name}'s DEFAULT Pool and {sdg-name} Pool Definitions
|
||||
|
||||
If a {data-store-name} `ClientCache` is local-only, then no Pool definition is required. For instance, you can define
|
||||
the following:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<gfe:client-cache/>
|
||||
|
||||
<gfe:client-region id="Example" shortcut="LOCAL"/>
|
||||
----
|
||||
|
||||
In this case, the "`Example`" Region is `LOCAL` and no data is distributed between the client and a server. Therefore,
|
||||
no Pool is necessary. This is true for any client-side, local-only Region, as defined by the {data-store-name}'s
|
||||
{x-data-store-javadoc}/org/apache/geode/cache/client/ClientRegionShortcut.html[`ClientRegionShortcut`]
|
||||
(all `LOCAL_*` shortcuts).
|
||||
|
||||
However, if a client Region is a (caching) proxy to a server-side Region, a Pool is required. In that case,
|
||||
there are several ways to define and use a Pool.
|
||||
|
||||
When a `ClientCache`, a Pool, and a proxy-based Region are all defined but not explicitly identified, {sdg-name}
|
||||
resolves the references automatically, as the following example shows:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<gfe:client-cache/>
|
||||
|
||||
<gfe:pool>
|
||||
<gfe:locator host="${geode.locator.host}" port="${geode.locator.port}"/>
|
||||
</gfe:pool>
|
||||
|
||||
<gfe:client-region id="Example" shortcut="PROXY"/>
|
||||
----
|
||||
|
||||
In the preceding example, the `ClientCache` is identified as `gemfireCache`, the Pool as `gemfirePool`,
|
||||
and the client Region as "`Example`". However, the `ClientCache` initializes {data-store-name}'s `DEFAULT` Pool
|
||||
from `gemfirePool`, and the client Region uses the `gemfirePool` when distributing data between the client
|
||||
and the server.
|
||||
|
||||
Basically, {sdg-name} resolves the preceding configuration to the following:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<gfe:client-cache id="gemfireCache" pool-name="gemfirePool"/>
|
||||
|
||||
<gfe:pool id="gemfirePool">
|
||||
<gfe:locator host="${geode.locator.host}" port="${geode.locator.port}"/>
|
||||
</gfe:pool>
|
||||
|
||||
<gfe:client-region id="Example" cache-ref="gemfireCache" pool-name="gemfirePool" shortcut="PROXY"/>
|
||||
----
|
||||
|
||||
{data-store-name} still creates a Pool called `DEFAULT`. {sdg-name} causes the `DEFAULT` Pool to be initialized
|
||||
from the `gemfirePool`. Doing so is useful in situations where multiple Pools are defined and client Regions
|
||||
are using separate Pools, or do not declare a Pool at all.
|
||||
|
||||
Consider the following:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<gfe:client-cache pool-name="locatorPool"/>
|
||||
|
||||
<gfe:pool id="locatorPool">
|
||||
<gfe:locator host="${geode.locator.host}" port="${geode.locator.port}"/>
|
||||
</gfe:pool>
|
||||
|
||||
<gfe:pool id="serverPool">
|
||||
<gfe:server host="${geode.server.host}" port="${geode.server.port}"/>
|
||||
</gfe:pool>
|
||||
|
||||
<gfe:client-region id="Example" pool-name="serverPool" shortcut="PROXY"/>
|
||||
|
||||
<gfe:client-region id="AnotherExample" shortcut="CACHING_PROXY"/>
|
||||
|
||||
<gfe:client-region id="YetAnotherExample" shortcut="LOCAL"/>
|
||||
----
|
||||
|
||||
In this setup, the {data-store-name} `client-cache` `DEFAULT` pool is initialized from `locatorPool`,
|
||||
as specified by the `pool-name` attribute. There is no {sdg-name}-defined `gemfirePool`, since both Pools
|
||||
were explicitly identified (named) -- `locatorPool` and `serverPool`, respectively.
|
||||
|
||||
The "`Example`" Region explicitly refers to and exclusively uses the `serverPool`. The `AnotherExample` Region uses
|
||||
{data-store-name}'s `DEFAULT` Pool, which, again, was configured from the `locatorPool` based on the client cache
|
||||
bean definition's `pool-name` attribute.
|
||||
|
||||
Finally, the `YetAnotherExample` Region does not use a Pool, because it is `LOCAL`.
|
||||
|
||||
NOTE: The `AnotherExample` Region would first look for a Pool bean named `gemfirePool`, but that would require
|
||||
the definition of an anonymous Pool bean (that is, `<gfe:pool/>`) or a Pool bean explicitly named `gemfirePool`
|
||||
(for example, `<gfe:pool id="gemfirePool"/>`).
|
||||
|
||||
NOTE: If we either changed the name of `locatorPool` to `gemfirePool` or made the Pool bean definition be anonymous,
|
||||
it would have the same effect as the preceding configuration.
|
||||
@@ -1,149 +0,0 @@
|
||||
[[apis:continuous-query]]
|
||||
= Continuous Query (CQ)
|
||||
|
||||
A powerful functionality offered by {data-store-name} is
|
||||
{x-data-store-docs}/developing/continuous_querying/chapter_overview.html[Continuous Query] (or CQ).
|
||||
|
||||
In short, CQ allows a developer to create and register an OQL query, and then automatically be notified when new data
|
||||
that gets added to {data-store-name} matches the query predicate. {sdg-name} provides dedicated
|
||||
support for CQs through the `org.springframework.data.gemfire.listener` package and its *listener container*;
|
||||
very similar in functionality and naming to the JMS integration in the _Spring Framework_; in fact, users familiar with
|
||||
the JMS support in Spring, should feel right at home.
|
||||
|
||||
Basically {sdg-name} allows methods on POJOs to become end-points for CQ. Simply define the query
|
||||
and indicate the method that should be called to be notified when there is a match. {sdg-name} takes care
|
||||
of the rest. This is very similar to Java EE's message-driven bean style, but without any requirement for base class
|
||||
or interface implementations, based on {data-store-name}.
|
||||
|
||||
NOTE: Currently, Continuous Query is only supported in {data-store-name}'s client/server topology. Additionally, the client Pool
|
||||
used is required to have the subscription enabled. Please refer to the {data-store-name}
|
||||
{x-data-store-docs}/developing/continuous_querying/implementing_continuous_querying.html[documentation]
|
||||
for more information.
|
||||
|
||||
[[apis:continuous-query:container]]
|
||||
== Continuous Query Listener Container
|
||||
|
||||
{sdg-name} simplifies creation, registration, life-cycle and dispatch of CQ events by taking care of
|
||||
the infrastructure around CQ with the use of SDG's `ContinuousQueryListenerContainer`, which does all the heavy lifting
|
||||
on behalf of the user. Users familiar with EJB and JMS should find the concepts familiar as it is designed
|
||||
as close as possible to the support provided in the _Spring Framework_ with its Message-driven POJOs (MDPs).
|
||||
|
||||
The SDG `ContinuousQueryListenerContainer` acts as an event (or message) listener container; it is used to
|
||||
receive the events from the registered CQs and invoke the POJOs that are injected into it. The listener container
|
||||
is responsible for all threading of message reception and dispatches into the listener for processing. It acts as
|
||||
the intermediary between an EDP (Event-driven POJO) and the event provider and takes care of creation and registration
|
||||
of CQs (to receive events), resource acquisition and release, exception conversion and the like. This allows you,
|
||||
as an application developer, to write the (possibly complex) business logic associated with receiving an event
|
||||
(and reacting to it), and delegate the boilerplate {data-store-name} infrastructure concerns to the framework.
|
||||
|
||||
The listener container is fully customizable. A developer can chose either to use the CQ thread to perform the dispatch
|
||||
(synchronous delivery) or a new thread (from an existing pool) for an asynchronous approach by defining the suitable
|
||||
`java.util.concurrent.Executor` (or Spring's `TaskExecutor`). Depending on the load, the number of listeners
|
||||
or the runtime environment, the developer should change or tweak the executor to better serve her needs. In particular,
|
||||
in managed environments (such as app servers), it is highly recommended to pick a proper `TaskExecutor`
|
||||
to take advantage of its runtime.
|
||||
|
||||
[[apis:continuous-query:adapter]]
|
||||
== The `ContinuousQueryListener` and `ContinuousQueryListenerAdapter`
|
||||
|
||||
The `ContinuousQueryListenerAdapter` class is the final component in {sdg-name} CQ support. In a nutshell,
|
||||
class allows you to expose almost *any* implementing class as an EDP with minimal constraints.
|
||||
`ContinuousQueryListenerAdapter` implements the `ContinuousQueryListener` interface, a simple listener interface
|
||||
similar to {data-store-name}'s {x-data-store-javadoc}/org/apache/geode/cache/query/CqListener.html[CqListener].
|
||||
|
||||
Consider the following interface definition. Notice the various event handling methods and their parameters:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
public interface EventDelegate {
|
||||
void handleEvent(CqEvent event);
|
||||
void handleEvent(Operation baseOp);
|
||||
void handleEvent(Object key);
|
||||
void handleEvent(Object key, Object newValue);
|
||||
void handleEvent(Throwable throwable);
|
||||
void handleQuery(CqQuery cq);
|
||||
void handleEvent(CqEvent event, Operation baseOp, byte[] deltaValue);
|
||||
void handleEvent(CqEvent event, Operation baseOp, Operation queryOp, Object key, Object newValue);
|
||||
}
|
||||
----
|
||||
|
||||
[source,java]
|
||||
----
|
||||
package example;
|
||||
|
||||
class DefaultEventDelegate implements EventDelegate {
|
||||
// implementation elided for clarity...
|
||||
}
|
||||
----
|
||||
|
||||
In particular, note how the above implementation of the `EventDelegate` interface has *no* {data-store-name} dependencies at all.
|
||||
It truly is a POJO that we can and will make into an EDP via the following configuration.
|
||||
|
||||
NOTE: the class does not have to implement an interface; an interface is only used to better showcase the decoupling
|
||||
between the contract and the implementation.
|
||||
|
||||
[source,xml]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:gfe="{spring-data-schema-namespace}"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
{spring-data-schema-namespace} {spring-data-schema-location}
|
||||
">
|
||||
|
||||
<gfe:client-cache/>
|
||||
|
||||
<gfe:pool subscription-enabled="true">
|
||||
<gfe:server host="localhost" port="40404"/>
|
||||
</gfe:pool>
|
||||
|
||||
<gfe:cq-listener-container>
|
||||
<!-- default handle method -->
|
||||
<gfe:listener ref="listener" query="SELECT * FROM /SomeRegion"/>
|
||||
<gfe:listener ref="another-listener" query="SELECT * FROM /AnotherRegion" name="myQuery" method="handleQuery"/>
|
||||
</gfe:cq-listener-container>
|
||||
|
||||
<bean id="listener" class="example.DefaultMessageDelegate"/>
|
||||
<bean id="another-listener" class="example.DefaultMessageDelegate"/>
|
||||
...
|
||||
<beans>
|
||||
----
|
||||
|
||||
NOTE: The example above shows a few of the various forms that a listener can have; at its minimum, the listener
|
||||
reference and the actual query definition are required. It's possible, however, to specify a name for
|
||||
the resulting Continuous Query (useful for monitoring) but also the name of the method (the default is `handleEvent`).
|
||||
The specified method can have various argument types, the `EventDelegate` interface lists the allowed types.
|
||||
|
||||
The example above uses the {sdg-name} namespace to declare the event listener container
|
||||
and automatically register the listeners. The full blown, *beans* definition is displayed below:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<!-- this is the Event Driven POJO (MDP) -->
|
||||
<bean id="eventListener" class="org.springframework.data.gemfire.listener.adapter.ContinuousQueryListenerAdapter">
|
||||
<constructor-arg>
|
||||
<bean class="gemfireexample.DefaultEventDelegate"/>
|
||||
</constructor-arg>
|
||||
</bean>
|
||||
|
||||
<!-- and this is the event listener container... -->
|
||||
<bean id="gemfireListenerContainer" class="org.springframework.data.gemfire.listener.ContinuousQueryListenerContainer">
|
||||
<property name="cache" ref="gemfireCache"/>
|
||||
<property name="queryListeners">
|
||||
<!-- set of CQ listeners -->
|
||||
<set>
|
||||
<bean class="org.springframework.data.gemfire.listener.ContinuousQueryDefinition" >
|
||||
<constructor-arg value="SELECT * FROM /SomeRegion" />
|
||||
<constructor-arg ref="eventListener"/>
|
||||
</bean>
|
||||
</set>
|
||||
</property>
|
||||
</bean>
|
||||
----
|
||||
|
||||
Each time an event is received, the adapter automatically performs type translation between the {data-store-name} event
|
||||
and the required method argument(s) transparently. Any exception caused by the method invocation is caught
|
||||
and handled by the container (by default, being logged).
|
||||
@@ -1,41 +0,0 @@
|
||||
[[data-access]]
|
||||
= Using the Data Access Namespace
|
||||
|
||||
In addition to the core XML namespace (`gfe`), {sdg-name} provides a data access XML namespace (`gfe-data`),
|
||||
which is primarily intended to simplify the development of {data-store-name} client applications. This namespace
|
||||
currently contains support for {data-store-name} <<gemfire-repositories, Repositories>> and Function
|
||||
<<function-execution, execution>>, as well as a `<datasource>` tag that offers a convenient way to connect to
|
||||
a {data-store-name} cluster.
|
||||
|
||||
[[data-access:datasource]]
|
||||
== An Easy Way to Connect to {data-store-name}
|
||||
|
||||
For many applications, a basic connection to a {data-store-name} data grid using default values is sufficient.
|
||||
{sdg-name}'s `<datasource>` tag provides a simple way to access data. The data source creates a `ClientCache`
|
||||
and connection `Pool`. In addition, it queries the cluster servers for all existing root Regions and creates
|
||||
an (empty) client Region proxy for each one.
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<gfe-data:datasource>
|
||||
<locator host="remotehost" port="1234"/>
|
||||
</gfe-data:datasource>
|
||||
----
|
||||
|
||||
The `<datasource>` tag is syntactically similar to `<gfe:pool>`. It may be configured with one or more nested `locator`
|
||||
or `server` elements to connect to an existing data grid. Additionally, all attributes available to configure a Pool
|
||||
are supported. This configuration automatically creates client Region beans for each Region defined on cluster members
|
||||
connected to the Locator, so they can be seamlessly referenced by Spring Data mapping annotations (`GemfireTemplate`)
|
||||
and autowired into application classes.
|
||||
|
||||
Of course, you can explicitly configure client Regions. For example, if you want to cache data in local memory,
|
||||
as the following example shows:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<gfe-data:datasource>
|
||||
<locator host="remotehost" port="1234"/>
|
||||
</gfe-data:datasource>
|
||||
|
||||
<gfe:client-region id="Example" shortcut="CACHING_PROXY"/>
|
||||
----
|
||||
@@ -1,747 +0,0 @@
|
||||
[[apis]]
|
||||
= Working with {data-store-name} APIs
|
||||
|
||||
Once the {data-store-name} Cache and Regions have been configured, they can be injected and used inside application objects.
|
||||
This chapter describes the integration with Spring's Transaction Management functionality and DAO exception hierarchy.
|
||||
This chapter also covers support for dependency injection of {data-store-name} managed objects.
|
||||
|
||||
[[apis:template]]
|
||||
== GemfireTemplate
|
||||
|
||||
As with many other high-level abstractions provided by Spring, {sdg-name} provides a *template*
|
||||
to simplify {data-store-name} data access operations. The class provides several methods containing common Region operations,
|
||||
but also provides the capability to *execute* code against native {data-store-name} APIs without having to deal with
|
||||
{data-store-name} checked exceptions by using a `GemfireCallback`.
|
||||
|
||||
The template class requires a {data-store-name} `Region`, and once configured, is thread-safe and is reusable
|
||||
across multiple application classes:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<bean id="gemfireTemplate" class="org.springframework.data.gemfire.GemfireTemplate" p:region-ref="SomeRegion"/>
|
||||
----
|
||||
|
||||
Once the template is configured, a developer can use it alongside `GemfireCallback` to work directly with
|
||||
the {data-store-name} `Region` without having to deal with checked exceptions, threading or resource management concerns:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
template.execute(new GemfireCallback<Iterable<String>>() {
|
||||
|
||||
public Iterable<String> doInGemfire(Region region)
|
||||
throws GemFireCheckedException, GemFireException {
|
||||
|
||||
Region<String, String> localRegion = (Region<String, String>) region;
|
||||
|
||||
localRegion.put("1", "one");
|
||||
localRegion.put("3", "three");
|
||||
|
||||
return localRegion.query("length < 5");
|
||||
}
|
||||
});
|
||||
----
|
||||
|
||||
For accessing the full power of the {data-store-name} query language, a developer can use the `find` and `findUnique`
|
||||
methods, which, compared to the `query` method, can execute queries across multiple Regions, execute projections,
|
||||
and the like.
|
||||
|
||||
The `find` method should be used when the query selects multiple items (through `SelectResults`) and the latter,
|
||||
`findUnique`, as the name suggests, when only one object is returned.
|
||||
|
||||
[[apis:exception-translation]]
|
||||
== Exception Translation
|
||||
|
||||
Using a new data access technology requires not only accommodating a new API but also handling exceptions
|
||||
specific to that technology.
|
||||
|
||||
To accommodate the exception handling case, the _Spring Framework_ provides a technology agnostic and consistent
|
||||
https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#dao-exceptions[exception hierarchy]
|
||||
that abstracts the application from proprietary, and usually "checked", exceptions to a set of focused runtime
|
||||
exceptions.
|
||||
|
||||
As mentioned in _Spring Framework's_ documentation,
|
||||
https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#orm-exception-translation[Exception translation]
|
||||
can be applied transparently to your Data Access Objects (DAO) through the use of the `@Repository` annotation and AOP
|
||||
by defining a `PersistenceExceptionTranslationPostProcessor` bean. The same exception translation functionality
|
||||
is enabled when using {data-store-name} as long as the `CacheFactoryBean` is declared, e.g. using either a `<gfe:cache/>`
|
||||
or `<gfe:client-cache>` declaration, which acts as an exception translator and is automatically detected by
|
||||
the Spring infrastructure and used accordingly.
|
||||
|
||||
[[apis:transaction-management]]
|
||||
== Local, Cache Transaction Management
|
||||
|
||||
One of the most popular features of the _Spring Framework_ is
|
||||
https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#transaction[Transaction Management].
|
||||
|
||||
If you are not familiar with Spring's transaction abstraction then we strongly recommend
|
||||
https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#transaction-motivation[reading]
|
||||
about _Spring's Transaction Management_ infrastructure as it offers a consistent _programming model_ that works
|
||||
transparently across multiple APIs and can be configured either programmatically or declaratively
|
||||
(the most popular choice).
|
||||
|
||||
For {data-store-name}, {sdg-name} provides a dedicated, per-cache, `PlatformTransactionManager` that, once declared,
|
||||
allows Region operations to be executed atomically through Spring:
|
||||
|
||||
.Enable Transaction Management using XML
|
||||
[source,xml]
|
||||
----
|
||||
<gfe:transaction-manager id="txManager" cache-ref="myCache"/>
|
||||
----
|
||||
|
||||
NOTE: The example above can be simplified even further by eliminating the `cache-ref` attribute if the {data-store-name}
|
||||
cache is defined under the default name, `gemfireCache`. As with the other {sdg-name} namespace elements, if the cache
|
||||
bean name is not configured, the aforementioned naming convention will be used. Additionally, the transaction manager
|
||||
name is "`gemfireTransactionManager`" if not explicitly specified.
|
||||
|
||||
Currently, {data-store-name} supports optimistic transactions with *read committed* isolation. Furthermore, to guarantee
|
||||
this isolation, developers should avoid making *in-place* changes that manually modify values present in the cache.
|
||||
To prevent this from happening, the transaction manager configures the cache to use *copy on read* semantics by default,
|
||||
meaning a clone of the actual value is created each time a read is performed. This behavior can be disabled if needed
|
||||
through the `copyOnRead` property.
|
||||
|
||||
Since a copy of the value for a given key is made when *copy on read* is enabled, you must subsequently call
|
||||
`Region.put(key, value)` inorder for the value to be updated, transactionally.
|
||||
|
||||
For more information on the semantics and behavior of the underlying Geode transaction manager, please refer to the Geode
|
||||
{x-data-store-javadoc}/org/apache/geode/cache/CacheTransactionManager.html[CacheTransactionManager Javadoc]
|
||||
as well as the {x-data-store-docs}/developing/transactions/chapter_overview.html[documentation].
|
||||
|
||||
[[apis:global-transaction-management]]
|
||||
== Global, JTA Transaction Management
|
||||
|
||||
It is also possible for {data-store-name} to participate in Global, JTA-based transactions, such as a transaction
|
||||
managed by an Java EE Application Server (e.g. WebSphere Application Server (WAS)) using Container Managed Transactions
|
||||
(CMT) along with other JTA resources.
|
||||
|
||||
However, unlike many other JTA "compliant" resources (e.g. JMS Message Brokers like ActiveMQ), {data-store-name} is *not*
|
||||
an XA compliant resource. Therefore, {data-store-name} must be positioned as the "_Last Resource_" in a JTA transaction
|
||||
(_prepare phase_) since it does not implement the 2-phase commit protocol, or rather does not handle distributed
|
||||
transactions.
|
||||
|
||||
Many managed environments capable of CMT maintain support for "_Last Resource_", non-XA compliant resources in JTA-based
|
||||
transactions, though it is not actually required in the JTA spec. More information on what a non-XA compliant,
|
||||
"_Last Resource_" means can be found in Red Hat's https://access.redhat.com/documentation/en-US/JBoss_Enterprise_Application_Platform/5/html/Administration_And_Configuration_Guide/lrco-overview.html[documentation].
|
||||
In fact, Red Hat's JBoss project, https://narayana.io/[Narayana] is one such LGPL Open Source implementation. _Narayana_
|
||||
refers to this as "_Last Resource Commit Optimization_" (LRCO). More details can be found https://narayana.io//docs/project/index.html#d0e1859[here].
|
||||
|
||||
However, whether you are using {data-store-name} in a standalone environment with an Open Source JTA Transaction
|
||||
Management implementation that supports "_Last Resource_", or a managed environment (e.g. Java EE AS such as WAS),
|
||||
{sdg-name} has you covered.
|
||||
|
||||
There are a series of steps you must complete to properly use {data-store-name} as a "_Last Resource_" in a JTA
|
||||
transaction involving more than 1 transactional resource. Additionally, there can only be 1 non-XA compliant resource
|
||||
(e.g. {data-store-name}) in such an arrangement.
|
||||
|
||||
1) First, you must complete Steps 1-4 in {data-store-name}'s documentation
|
||||
{apache-geode-docs}/developing/transactions/JTA_transactions.html#concept_csy_vfb_wk[here].
|
||||
|
||||
NOTE: #1 above is independent of your Spring [Boot] and/or [Data for {data-store-name}] application and must be
|
||||
completed successfully.
|
||||
|
||||
2) Referring to Step 5 in {data-store-name}'s {apache-geode-docs}/developing/transactions/JTA_transactions.html#concept_csy_vfb_wk[documentation],
|
||||
{sdg-name}'s Annotation support will attempt to set the `GemFireCache`, {x-data-store-javadoc}/org/apache/geode/cache/GemFireCache.html#setCopyOnRead-boolean-[`copyOnRead`]
|
||||
property for you when using the `@EnableGemFireAsLastResource` annotation.
|
||||
|
||||
However, if SDG's auto-configuration is unsuccessful in this regard, then you must explicitly set the `copy-on-read`
|
||||
attribute in the `<gfe:cache>` or `<gfe:client-cache>` XML element or set the `copyOnRead` property of
|
||||
the `CacheFactoryBean` class in JavaConfig to *true*. For example:
|
||||
|
||||
`ClientCache` XML:
|
||||
|
||||
.Set copy-on-read using XML (client)
|
||||
[source,xml]
|
||||
----
|
||||
<gfe:client-cache ... copy-on-read="true"/>
|
||||
----
|
||||
|
||||
`ClientCache` _JavaConfig_:
|
||||
|
||||
.Set copyOnRead using JavaConfig (client)
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
ClientCacheFactoryBean gemfireCache() {
|
||||
|
||||
ClientCacheFactoryBean gemfireCache = new ClientCacheFactoryBean();
|
||||
|
||||
gemfireCache.setCopyOnRead(true);
|
||||
|
||||
return gemfireCache;
|
||||
}
|
||||
----
|
||||
|
||||
Peer `Cache` XML:
|
||||
|
||||
.Set copy-on-read using XML (server)
|
||||
[source,xml]
|
||||
----
|
||||
<gfe:cache ... copy-on-read="true"/>
|
||||
----
|
||||
|
||||
Peer `Cache` _JavaConfig_:
|
||||
|
||||
.Set copyOnRead using JavaConfig (server)
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
CacheFactoryBean gemfireCache() {
|
||||
|
||||
CacheFactoryBean gemfireCache = new CacheFactoryBean();
|
||||
|
||||
gemfireCache.setCopyOnRead(true);
|
||||
|
||||
return gemfireCache;
|
||||
}
|
||||
----
|
||||
|
||||
NOTE: Explicitly setting the `copy-on-read` attribute or the `copyOnRead` property is really not necessary. Enabling
|
||||
transaction management takes case of copying on reads.
|
||||
|
||||
3) At this point, you *skip* Steps 6-8 in {data-store-name}'s {apache-geode-docs}/developing/transactions/JTA_transactions.html#concept_csy_vfb_wk[documentation]
|
||||
and let _Spring Data Geode_ work its magic. All you need to do is annotate your Spring `@Configuration` class
|
||||
with {sdg-name}'s *new* `@EnableGemFireAsLastResource` annotation and a combination of Spring's
|
||||
{spring-framework-docs}/#transaction[Transaction Management] infrastructure and {sdg-name}'s
|
||||
`@EnableGemFireAsLastResource` annotation configuration does the trick.
|
||||
|
||||
The configuration looks like this...
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Configuration
|
||||
@EnableGemFireAsLastResource
|
||||
@EnableTransactionManagement(order = 1)
|
||||
class GeodeConfiguration {
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
The only requirements are...
|
||||
|
||||
3.1) The `@EnableGemFireAsLastResource` annotation must be declared on the same Spring `@Configuration` class
|
||||
where Spring's `@EnableTransactionManagement` annotation is also specified.
|
||||
|
||||
3.2) The `order` attribute of the `@EnableTransactionManagement` annotation must be explicitly set to an integer value
|
||||
that is not `Integer.MAX_VALUE` or `Integer.MIN_VALUE` (defaults to `Integer.MAX_VALUE`).
|
||||
|
||||
Of course, hopefully you are aware that you also need to configure Spring's `JtaTransactionManager`
|
||||
when using JTA transactions like so..
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
public JtaTransactionManager transactionManager(UserTransaction userTransaction) {
|
||||
|
||||
JtaTransactionManager transactionManager = new JtaTransactionManager();
|
||||
|
||||
transactionManager.setUserTransaction(userTransaction);
|
||||
|
||||
return transactionManager;
|
||||
}
|
||||
----
|
||||
|
||||
NOTE: The configuration in section <<apis:transaction-management>> does *not* apply here.
|
||||
The use of {sdg-name}'s `GemfireTransactionManager` is applicable in "Local-only", Cache Transactions,
|
||||
*not* "Global", JTA Transactions. Therefore, you do *not* configure the SDG `GemfireTransactionManager` in this case.
|
||||
You configure Spring's `JtaTransactionManager` as shown above.
|
||||
|
||||
For more details on using _Spring's Transaction Management_ with JTA,
|
||||
see {spring-framework-docs}/#transaction-application-server-integration[here].
|
||||
|
||||
Effectively, {sdg-name}'s `@EnableGemFireAsLastResource` annotation imports configuration containing 2 Aspect
|
||||
bean definitions that handles the {data-store-name} `o.a.g.ra.GFConnectionFactory.getConnection()`
|
||||
and `o.a.g.ra.GFConnection.close()` operations at the appropriate points during the transactional operation.
|
||||
|
||||
Specifically, the correct sequence of events follow:
|
||||
|
||||
1. `jtaTransation.begin()`
|
||||
|
||||
2. `GFConnectionFactory.getConnection()`
|
||||
|
||||
3. Call the application's `@Transactional` service method
|
||||
|
||||
4. Either `jtaTransaction.commit()` or `jtaTransaction.rollback()`
|
||||
|
||||
5. Finally, `GFConnection.close()`
|
||||
|
||||
This is consistent with how you, as the application developer, would code this manually if you had to use the JTA API
|
||||
+ {data-store-name} API yourself, as shown in the
|
||||
{data-store-name} {apache-geode-docs}/developing/transactions/jca_adapter_example.html#concept_swv_z2p_wk[example].
|
||||
|
||||
Thankfully, Spring does the heavy lifting for you and all you need to do after applying the appropriate configuration
|
||||
(shown above) is:
|
||||
|
||||
.Declaring a service method as @Transactional
|
||||
[source,java]
|
||||
----
|
||||
@Service
|
||||
class MyTransactionalService {
|
||||
|
||||
@Transactional
|
||||
public <Return-Type> someTransactionalServiceMethod() {
|
||||
// perform business logic interacting with and accessing multiple JTA resources atomically
|
||||
}
|
||||
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
#1 & #4 above are appropriately handled for you by Spring's JTA based `PlatformTransactionManager` once the
|
||||
`@Transactional` boundary is entered by your application (i.e. when the `MyTransactionService.someTransactionalServiceMethod()`
|
||||
is called).
|
||||
|
||||
#2 & #3 are handled by {sdg-name}'s new Aspects enabled with the `@EnableGemFireAsLastResource` annotation.
|
||||
|
||||
#3 of course is the responsibility of your application.
|
||||
|
||||
Indeed, with the appropriate logging configured, you will see the correct sequence of events...
|
||||
|
||||
.Transaction Log Output
|
||||
[source,xml]
|
||||
----
|
||||
2017-Jun-22 11:11:37 TRACE TransactionInterceptor - Getting transaction for [example.app.service.MessageService.send]
|
||||
|
||||
2017-Jun-22 11:11:37 TRACE GemFireAsLastResourceConnectionAcquiringAspect - Acquiring {data-store-name} Connection
|
||||
from {data-store-name} JCA ResourceAdapter registered at [gfe/jca]
|
||||
|
||||
2017-Jun-22 11:11:37 TRACE MessageService - PRODUCER [ Message :
|
||||
[{ @type = example.app.domain.Message, id= MSG0000000000, message = SENT }],
|
||||
JSON : [{"id":"MSG0000000000","message":"SENT"}] ]
|
||||
|
||||
2017-Jun-22 11:11:37 TRACE TransactionInterceptor - Completing transaction for [example.app.service.MessageService.send]
|
||||
|
||||
2017-Jun-22 11:11:37 TRACE GemFireAsLastResourceConnectionClosingAspect - Closed {data-store-name} Connection @ [Reference [...]]
|
||||
----
|
||||
|
||||
For more details on using {data-store-name} cache-level transactions, see <<apis:transaction-management,here>>.
|
||||
|
||||
For more details on using {data-store-name} in JTA transactions,
|
||||
see https://gemfire90.docs.pivotal.io/geode/developing/transactions/JTA_transactions.html[here].
|
||||
|
||||
For more details on configuring {data-store-name} as a "_Last Resource_",
|
||||
see https://gemfire90.docs.pivotal.io/geode/developing/transactions/JTA_transactions.html#concept_csy_vfb_wk[here].
|
||||
|
||||
[[apis:using-transactional-event-listener]]
|
||||
== Using @TransactionalEventListener
|
||||
|
||||
When using transactions, it may be desirable to register a listener to perform certain actions before or after the
|
||||
transaction commits, or after a rollback occurs.
|
||||
|
||||
{sdg-name} makes it easy to create listeners that will be invoked during specific phases of a transaction with the
|
||||
`@TransactionalEventListener` annotation. Methods annotated with `@TransactionalEventListener` (as shown below) will be
|
||||
notified of events published from transactional methods, during the specified `phase`.
|
||||
|
||||
.After Transaction Commit Event Listener
|
||||
[source,java]
|
||||
----
|
||||
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
|
||||
public void handleAfterCommit(MyEvent event) {
|
||||
// do something after transaction is committed
|
||||
}
|
||||
----
|
||||
|
||||
Inorder for the above method to be invoked, you must publish an event from within your transaction, like below:
|
||||
|
||||
.Publishing a Transactional Event
|
||||
[source,java]
|
||||
----
|
||||
@Service
|
||||
class MyTransactionalService {
|
||||
|
||||
@Autowired
|
||||
private final ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
@Transactional
|
||||
public <Return-Type> someTransactionalServiceMethod() {
|
||||
|
||||
// Perform business logic interacting with and accessing multiple transactional resources atomically, then...
|
||||
|
||||
applicationEventPublisher.publishEvent(new MyApplicationEvent(...));
|
||||
}
|
||||
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
The `@TransactionalEventListener` annotation allows you to specify the transaction `phase` in which the event handler
|
||||
method will be invoked. Options include: `AFTER_COMMIT`, `AFTER_COMPLETION`, `AFTER_ROLLBACK`, and `BEFORE_COMMIT`.
|
||||
If not specified, the `phase` defaults to `AFTER_COMMIT`. If you wish the listener to be called even when no transaction
|
||||
is present, you may set `fallbackExecution` to `true`.
|
||||
|
||||
[[apis:auto-transaction-event-publishing]]
|
||||
== Auto Transaction Event Publishing
|
||||
|
||||
As of {sdg-name} `Neumann/2.3`, it is now possible to enable auto transaction event publishing.
|
||||
|
||||
Using the `@EnableGemfireCacheTransactions` annotation, set the `enableAutoTransactionEventPublishing` attribute
|
||||
to *true*. The default is *false*.
|
||||
|
||||
.Enable auto transaction event publishing
|
||||
[source,java]
|
||||
----
|
||||
@EnableGemfireCacheTransactions(enableAutoTransactionEventPublishing = true)
|
||||
class GeodeConfiguration { ... }
|
||||
----
|
||||
|
||||
Then you can create `@TransactionalEventListener` annotated POJO methods to handle transaction events during either
|
||||
the `AFTER_COMMIT` or `AFTER_ROLLBACK` transaction phases.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Component
|
||||
class TransactionEventListeners {
|
||||
|
||||
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
|
||||
public void handleAfterCommit(TransactionApplicationEvent event) {
|
||||
...
|
||||
}
|
||||
|
||||
@TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
|
||||
public void handleAfterRollback(TransactionApplicationEvent event) {
|
||||
...
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
WARNING: Only `TransactionPhase.AFTER_COMMIT` and `TransactionPhase.AFTER_ROLLBACK` are supported.
|
||||
`TransactionPhase.BEFORE_COMMIT` is not supported because 1) SDG adapts {data-store-name}'s `TransactionListener`
|
||||
and `TransactionWriter` interfaces to implement auto transaction event publishing, and 2) when {data-store-name}'s
|
||||
`TransactionWriter.beforeCommit(:TransactionEvent)` is called, it is already after the
|
||||
`AbstractPlatformTransactionManager.triggerBeforeCommit(:TransactionStatus)` call where `@TranactionalEventListener`
|
||||
annotated POJO methods are called during the transaction lifecycle.
|
||||
|
||||
With auto transaction event publishing, you do not need to explicitly call the
|
||||
`applicationEventPublisher.publishEvent(..)` method inside your application `@Transactional` `@Service` methods.
|
||||
|
||||
However, if you still want to receive transaction events "_before commit_", then you must still call the
|
||||
`applicationEventPublisher.publishEvent(..)` method within your application `@Transactional` `@Service` methods.
|
||||
See the *note* above for more details.
|
||||
|
||||
:leveloffset: +1
|
||||
|
||||
include::{basedocdir}/reference/cq-container.adoc[]
|
||||
|
||||
:leveloffset: -1
|
||||
|
||||
[[apis:declarable]]
|
||||
== Wiring `Declarable` Components
|
||||
|
||||
{data-store-name} XML configuration (usually referred to as `cache.xml`) allows *user* objects to be declared
|
||||
as part of the configuration. Usually these objects are `CacheLoaders` or other pluggable callback components
|
||||
supported by {data-store-name}. Using native {data-store-name} configuration, each user type declared through XML must implement
|
||||
the `Declarable` interface, which allows arbitrary parameters to be passed to the declared class
|
||||
through a `Properties` instance.
|
||||
|
||||
In this section, we describe how you can configure these pluggable components when defined in `cache.xml`
|
||||
using Spring while keeping your Cache/Region configuration defined in `cache.xml`. This allows your
|
||||
pluggable components to focus on the application logic and not the location or creation of `DataSources`
|
||||
or other collaborators.
|
||||
|
||||
However, if you are starting a green field project, it is recommended that you configure Cache, Region,
|
||||
and other pluggable {data-store-name} components directly in Spring. This avoids inheriting from the `Declarable` interface
|
||||
or the base class presented in this section.
|
||||
|
||||
See the following sidebar for more information on this approach.
|
||||
|
||||
.Eliminate `Declarable` components
|
||||
****
|
||||
A developer can configure custom types entirely through Spring as mentioned in <<bootstrap:region>>.
|
||||
That way, a developer does not have to implement the `Declarable` interface, and also benefits from
|
||||
all the features of the Spring IoC container (not just dependency injection but also life-cycle
|
||||
and instance management).
|
||||
****
|
||||
|
||||
As an example of configuring a `Declarable` component using Spring, consider the following declaration
|
||||
(taken from the `Declarable` {x-data-store-javadoc}/org/apache/geode/cache/Declarable.html[Javadoc]):
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<cache-loader>
|
||||
<class-name>com.company.app.DBLoader</class-name>
|
||||
<parameter name="URL">
|
||||
<string>jdbc://12.34.56.78/mydb</string>
|
||||
</parameter>
|
||||
</cache-loader>
|
||||
----
|
||||
|
||||
To simplify the task of parsing, converting the parameters and initializing the object, {sdg-name} offers
|
||||
a base class (`WiringDeclarableSupport`) that allows {data-store-name} user objects to be wired through a *template* bean definition
|
||||
or, in case that is missing, perform auto-wiring through the Spring IoC container. To take advantage of this feature,
|
||||
the user objects need to extend `WiringDeclarableSupport`, which automatically locates the declaring `BeanFactory`
|
||||
and performs wiring as part of the initialization process.
|
||||
|
||||
.Why is a base class needed?
|
||||
****
|
||||
In the current {data-store-name} release there is no concept of an *object factory* and the types declared are instantiated
|
||||
and used as is. In other words, there is no easy way to manage object creation outside {data-store-name}.
|
||||
****
|
||||
|
||||
[[apis:declarable:template-wiring]]
|
||||
=== Configuration using *template* bean definitions
|
||||
|
||||
When used, `WiringDeclarableSupport` tries to first locate an existing bean definition and use that
|
||||
as the wiring template. Unless specified, the component class name will be used as an implicit bean definition name.
|
||||
|
||||
Let's see how our `DBLoader` declaration would look in that case:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
class DBLoader extends WiringDeclarableSupport implements CacheLoader {
|
||||
|
||||
private DataSource dataSource;
|
||||
|
||||
public void setDataSource(DataSource dataSource){
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
public Object load(LoaderHelper helper) { ... }
|
||||
}
|
||||
----
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<cache-loader>
|
||||
<class-name>com.company.app.DBLoader</class-name>
|
||||
<!-- no parameter is passed (use the bean's implicit name, which is the class name) -->
|
||||
</cache-loader>
|
||||
----
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:p="http://www.springframework.org/schema/p"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
">
|
||||
|
||||
<bean id="dataSource" ... />
|
||||
|
||||
<!-- template bean definition -->
|
||||
<bean id="com.company.app.DBLoader" abstract="true" p:dataSource-ref="dataSource"/>
|
||||
</beans>
|
||||
----
|
||||
|
||||
In the scenario above, as no parameter was specified, a bean with the id/name `com.company.app.DBLoader` was used
|
||||
as a template for wiring the instance created by {data-store-name}. For cases where the bean name uses a different convention,
|
||||
one can pass in the `bean-name` parameter in the {data-store-name} configuration:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<cache-loader>
|
||||
<class-name>com.company.app.DBLoader</class-name>
|
||||
<!-- pass the bean definition template name as parameter -->
|
||||
<parameter name="bean-name">
|
||||
<string>template-bean</string>
|
||||
</parameter>
|
||||
</cache-loader>
|
||||
----
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:p="http://www.springframework.org/schema/p"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
">
|
||||
|
||||
<bean id="dataSource" ... />
|
||||
|
||||
<!-- template bean definition -->
|
||||
<bean id="template-bean" abstract="true" p:dataSource-ref="dataSource"/>
|
||||
|
||||
</beans>
|
||||
----
|
||||
|
||||
NOTE: The *template* bean definitions do not have to be declared in XML.
|
||||
Any format is allowed (Groovy, annotations, etc).
|
||||
|
||||
[[apis:declarable:autowiring]]
|
||||
=== Configuration using auto-wiring and annotations
|
||||
|
||||
By default, if no bean definition is found, `WiringDeclarableSupport` will
|
||||
https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-factory-autowire[autowire]
|
||||
the declaring instance. This means that unless any dependency injection *metadata* is offered by the instance,
|
||||
the container will find the object setters and try to automatically satisfy these dependencies.
|
||||
However, a developer can also use JDK 5 annotations to provide additional information to the auto-wiring process.
|
||||
|
||||
TIP: We strongly recommend reading the dedicated
|
||||
https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-annotation-config[chapter]
|
||||
in the Spring documentation for more information on the supported annotations and enabling factors.
|
||||
|
||||
For example, the hypothetical `DBLoader` declaration above can be injected with a Spring-configured `DataSource`
|
||||
in the following way:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
class DBLoader extends WiringDeclarableSupport implements CacheLoader {
|
||||
|
||||
// use annotations to 'mark' the needed dependencies
|
||||
@javax.inject.Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
public Object load(LoaderHelper helper) { ... }
|
||||
}
|
||||
----
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<cache-loader>
|
||||
<class-name>com.company.app.DBLoader</class-name>
|
||||
<!-- no need to declare any parameters since the class is auto-wired -->
|
||||
</cache-loader>
|
||||
----
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
|
||||
">
|
||||
|
||||
<!-- enable annotation processing -->
|
||||
<context:annotation-config/>
|
||||
|
||||
</beans>
|
||||
----
|
||||
|
||||
By using the JSR-330 annotations, the `CacheLoader` code has been simplified since the location and creation
|
||||
of the `DataSource` has been externalized and the user code is concerned only with the loading process.
|
||||
The `DataSource` might be transactional, created lazily, shared between multiple objects or retrieved from JNDI.
|
||||
These aspects can easily be configured and changed through the Spring container without touching
|
||||
the `DBLoader` code.
|
||||
|
||||
[[apis:spring-cache-abstraction]]
|
||||
== Support for the Spring Cache Abstraction
|
||||
|
||||
{sdg-name} provides an implementation of the Spring
|
||||
https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#cache[Cache Abstraction]
|
||||
to position {data-store-name} as a _caching provider_ in Spring's caching infrastructure.
|
||||
|
||||
To use {data-store-name} as a backing implementation, a "_caching provider_" _in Spring's Cache Abstraction_,
|
||||
simply add `GemfireCacheManager` to your configuration:
|
||||
|
||||
[source,xml]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:cache="http://www.springframework.org/schema/cache"
|
||||
xmlns:gfe="{spring-data-schema-namespace}"
|
||||
xmlns:p="http://www.springframework.org/schema/p"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/cache https://www.springframework.org/schema/cache/spring-cache.xsd
|
||||
{spring-data-schema-namespace} {spring-data-schema-location}
|
||||
">
|
||||
|
||||
<!-- enable declarative caching -->
|
||||
<cache:annotation-driven/>
|
||||
|
||||
<gfe:cache id="gemfire-cache"/>
|
||||
|
||||
<!-- declare GemfireCacheManager; must have a bean ID of 'cacheManager' -->
|
||||
<bean id="cacheManager" class="org.springframework.data.gemfire.cache.GemfireCacheManager"
|
||||
p:cache-ref="gemfire-cache">
|
||||
|
||||
</beans>
|
||||
----
|
||||
|
||||
NOTE: The `cache-ref` attribute on the `CacheManager` bean definition is not necessary if the default cache bean name
|
||||
is used (i.e. "gemfireCache"), i.e. `<gfe:cache>` without an explicit ID.
|
||||
|
||||
When the `GemfireCacheManager` (Singleton) bean instance is declared and declarative caching is enabled
|
||||
(either in XML with `<cache:annotation-driven/>` or in JavaConfig with Spring's `@EnableCaching` annotation),
|
||||
the Spring caching annotations (e.g. `@Cacheable`) identify the "caches" that will cache data in-memory
|
||||
using {data-store-name} Regions.
|
||||
|
||||
These caches (i.e. Regions) must exist before the caching annotations that use them otherwise an error will occur.
|
||||
|
||||
By way of example, suppose you have a Customer Service application with a `CustomerService` application component
|
||||
that performs caching...
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Service
|
||||
class CustomerService {
|
||||
|
||||
@Cacheable(cacheNames="Accounts", key="#customer.id")
|
||||
Account createAccount(Customer customer) {
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
Then you will need the following config.
|
||||
|
||||
XML:
|
||||
|
||||
[source,xml]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:cache="http://www.springframework.org/schema/cache"
|
||||
xmlns:gfe="{spring-data-schema-namespace}"
|
||||
xmlns:p="http://www.springframework.org/schema/p"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/cache https://www.springframework.org/schema/cache/spring-cache.xsd
|
||||
{spring-data-schema-namespace} {spring-data-schema-location}
|
||||
">
|
||||
|
||||
<!-- enable declarative caching -->
|
||||
<cache:annotation-driven/>
|
||||
|
||||
<bean id="cacheManager" class="org.springframework.data.gemfire.cache.GemfireCacheManager">
|
||||
|
||||
<gfe:cache/>
|
||||
|
||||
<gfe:partitioned-region id="accountsRegion" name="Accounts" persistent="true" ...>
|
||||
...
|
||||
</gfe:partitioned-region>
|
||||
</beans>
|
||||
----
|
||||
|
||||
JavaConfig:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
class ApplicationConfiguration {
|
||||
|
||||
@Bean
|
||||
CacheFactoryBean gemfireCache() {
|
||||
return new CacheFactoryBean();
|
||||
}
|
||||
|
||||
@Bean
|
||||
GemfireCacheManager cacheManager() {
|
||||
GemfireCacheManager cacheManager = GemfireCacheManager();
|
||||
cacheManager.setCache(gemfireCache());
|
||||
return cacheManager;
|
||||
}
|
||||
|
||||
@Bean("Accounts")
|
||||
PartitionedRegionFactoryBean accountsRegion() {
|
||||
PartitionedRegionFactoryBean accounts = new PartitionedRegionFactoryBean();
|
||||
|
||||
accounts.setCache(gemfireCache());
|
||||
accounts.setClose(false);
|
||||
accounts.setPersistent(true);
|
||||
|
||||
return accounts;
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Of course, you are free to choose whatever Region type you like (e.g. REPLICATE, PARTITION, LOCAL, etc).
|
||||
|
||||
For more details on _Spring's Cache Abstraction_, again, please refer to the
|
||||
https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#cache[documentation].
|
||||
@@ -1,23 +0,0 @@
|
||||
[[bootstrap:diskstore]]
|
||||
= Configuring a DiskStore
|
||||
|
||||
{sdg-name} supports `DiskStore` configuration and creation through the `disk-store` element,
|
||||
as the following example shows:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<gfe:disk-store id="Example" auto-compact="true" max-oplog-size="10"
|
||||
queue-size="50" time-interval="9999">
|
||||
<gfe:disk-dir location="/disk/location/one" max-size="20"/>
|
||||
<gfe:disk-dir location="/disk/location/two" max-size="20"/>
|
||||
</gfe:disk-store>
|
||||
----
|
||||
|
||||
`DiskStore` instances are used by Regions for file system persistent backup and overflow of evicted entries
|
||||
as well as persistent backup for WAN Gateways. Multiple {data-store-name} components may share the same `DiskStore`.
|
||||
Additionally, multiple file system directories may be defined for a single `DiskStore`, as shown in
|
||||
the preceding example.
|
||||
|
||||
See {data-store-name}'s documentation for a complete explanation of
|
||||
{x-data-store-docs}/developing/storing_data_on_disk/chapter_overview.html[Persistence and Overflow]
|
||||
and configuration options on `DiskStore` instances.
|
||||
@@ -1,460 +0,0 @@
|
||||
[[function-annotations]]
|
||||
= Annotation Support for Function Execution
|
||||
|
||||
{sdg-name} includes annotation support to simplify working with {data-store-name}
|
||||
{x-data-store-docs}/developing/function_exec/chapter_overview.html[Function execution].
|
||||
|
||||
Under the hood, the {data-store-name} API provides classes to implement and register {data-store-name}
|
||||
{x-data-store-javadoc}/org/apache/geode/cache/execute/Function.html[Functions] that are deployed on {data-store-name}
|
||||
servers, which may then be invoked by other peer member applications or remotely from cache clients.
|
||||
|
||||
Functions can execute in parallel, distributed among multiple {data-store-name} servers in the cluster, aggregating the
|
||||
results using the map-reduce pattern and sent back to the caller. Functions can also be targeted to run on a single
|
||||
server or Region. The {data-store-name} API supports remote execution of Functions targeted by using various predefined
|
||||
scopes: on Region, on members (in groups), on servers, and others. The implementation and execution of remote Functions,
|
||||
as with any RPC protocol, requires some boilerplate code.
|
||||
|
||||
{sdg-name}, true to Spring's core value proposition, aims to hide the mechanics of remote Function execution and let you
|
||||
focus on core POJO programming and business logic. To this end, {sdg-name} introduces annotations to declaratively
|
||||
register the public methods of a POJO class as {data-store-name} Functions along with the ability to invoke registered
|
||||
Functions (including remotely) by using annotated interfaces.
|
||||
|
||||
== Implementation Versus Execution
|
||||
|
||||
There are two separate concerns to address: implementation and execution.
|
||||
|
||||
The first is Function implementation (server-side), which must interact with the
|
||||
{x-data-store-javadoc}/org/apache/geode/cache/execute/FunctionContext.html[`FunctionContext`]
|
||||
to access the invocation arguments,
|
||||
{x-data-store-javadoc}/org/apache/geode/cache/execute/ResultSender.html[`ResultsSender`] to send results,
|
||||
and other execution context information. The Function implementation typically accesses the cache and Regions
|
||||
and is registered with the
|
||||
{x-data-store-javadoc}/org/apache/geode/cache/execute/FunctionService.html[`FunctionService`] under a unique ID.
|
||||
|
||||
A cache client application invoking a Function does not depend on the implementation. To invoke a Function,
|
||||
the application instantiates an
|
||||
{x-data-store-javadoc}/org/apache/geode/cache/execute/Execution.html[`Execution`]
|
||||
providing the Function ID, invocation arguments, and the Function target, which defines its scope:
|
||||
Region, server, servers, member, or members. If the Function produces a result, the invoker uses a
|
||||
{x-data-store-javadoc}/org/apache/geode/cache/execute/ResultCollector.html[`ResultCollector`]
|
||||
to aggregate and acquire the execution results. In certain cases, a custom `ResultCollector` implementation
|
||||
is required and may be registered with the `Execution`.
|
||||
|
||||
NOTE: 'Client' and 'Server' are used here in the context of Function execution, which may have a different meaning
|
||||
than client and server in {data-store-name}'s client-server topology. While it is common for an application using
|
||||
a `ClientCache` instance to invoke a Function on one or more {data-store-name} servers in a cluster, it is also
|
||||
possible to execute Functions in a peer-to-peer (P2P) configuration, where the application is a member of the cluster
|
||||
hosting a peer `Cache` instance. Keep in mind that a peer member cache application is subject to all the constraints
|
||||
of being a peer member of the cluster.
|
||||
|
||||
[[function-implementation]]
|
||||
== Implementing a Function
|
||||
|
||||
Using {data-store-name} APIs, the `FunctionContext` provides a runtime invocation context that includes the client's
|
||||
calling arguments and a `ResultSender` implementation to send results back to the client. Additionally, if the Function
|
||||
is executed on a Region, the `FunctionContext` is actually an instance of `RegionFunctionContext`, which provides
|
||||
additional information, such as the target Region on which the Function was invoked, any filter (a set of specific keys)
|
||||
associated with the `Execution`, and so on. If the Region is a `PARTITION` Region, the Function should use
|
||||
the `PartitionRegionHelper` to extract the local data set.
|
||||
|
||||
By using Spring, you can write a simple POJO and use the Spring container to bind one or more of your POJO's
|
||||
public methods to a Function. The signature for a POJO method intended to be used as a Function must generally conform
|
||||
to the client's execution arguments. However, in the case of a Region execution, the Region data may also be provided
|
||||
(presumably the data is held in the local partition if the Region is a `PARTITION` Region).
|
||||
|
||||
Additionally, the Function may require the filter that was applied, if any. This suggests that the client and server
|
||||
share a contract for the calling arguments but that the method signature may include additional parameters to pass values
|
||||
provided by the `FunctionContext`. One possibility is for the client and server to share a common interface, but this
|
||||
is not strictly required. The only constraint is that the method signature includes the same sequence of calling arguments
|
||||
with which the Function was invoked after the additional parameters are resolved.
|
||||
|
||||
For example, suppose the client provides a `String` and an `int` as the calling arguments. These are provided
|
||||
in the `FunctionContext` as an array, as the following example shows:
|
||||
|
||||
`Object[] args = new Object[] { "test", 123 };`
|
||||
|
||||
The Spring container should be able to bind to any method signature similar to the following (ignoring the return type
|
||||
for the moment):
|
||||
|
||||
[source,java]
|
||||
----
|
||||
public Object method1(String s1, int i2) { ... }
|
||||
public Object method2(Map<?, ?> data, String s1, int i2) { ... }
|
||||
public Object method3(String s1, Map<?, ?> data, int i2) { ... }
|
||||
public Object method4(String s1, Map<?, ?> data, Set<?> filter, int i2) { ... }
|
||||
public void method4(String s1, Set<?> filter, int i2, Region<?,?> data) { ... }
|
||||
public void method5(String s1, ResultSender rs, int i2) { ... }
|
||||
public void method6(FunctionContest context) { ... }
|
||||
----
|
||||
|
||||
The general rule is that once any additional arguments (that is, Region data and filter) are resolved,
|
||||
the remaining arguments must correspond exactly, in order and type, to the expected Function method parameters.
|
||||
The method's return type must be void or a type that may be serialized (as a `java.io.Serializable`, `DataSerializable`,
|
||||
or `PdxSerializable`). The latter is also a requirement for the calling arguments.
|
||||
|
||||
The Region data should normally be defined as a `Map`, to facilitate unit testing, but may also be of type Region,
|
||||
if necessary. As shown in the preceding example, it is also valid to pass the `FunctionContext` itself
|
||||
or the `ResultSender` if you need to control over how the results are returned to the client.
|
||||
|
||||
=== Annotations for Function Implementation
|
||||
|
||||
The following example shows how {sdg-acronym}'s Function annotations are used to expose POJO methods
|
||||
as {data-store-name} Functions:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Component
|
||||
public class ApplicationFunctions {
|
||||
|
||||
@GemfireFunction
|
||||
public String function1(String value, @RegionData Map<?, ?> data, int i2) { ... }
|
||||
|
||||
@GemfireFunction(id = "myFunction", batchSize=100, HA=true, optimizedForWrite=true)
|
||||
public List<String> function2(String value, @RegionData Map<?, ?> data, int i2, @Filter Set<?> keys) { ... }
|
||||
|
||||
@GemfireFunction(hasResult=true)
|
||||
public void functionWithContext(FunctionContext functionContext) { ... }
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
Note that the class itself must be registered as a Spring bean and each {data-store-name} Function is annotated with
|
||||
`@GemfireFunction`. In the preceding example, Spring's `@Component` annotation was used, but you can register the bean
|
||||
by using any method supported by Spring (such as XML configuration or with a Java configuration class when using
|
||||
Spring Boot). This lets the Spring container create an instance of this class and wrap it in a
|
||||
https://docs.spring.io/spring-data-gemfire/docs/current/api/org/springframework/data/gemfire/function/PojoFunctionWrapper.html[`PojoFunctionWrapper`].
|
||||
Spring creates a wrapper instance for each method annotated with `@GemfireFunction`. Each wrapper instance shares
|
||||
the same target object instance to invoke the corresponding method.
|
||||
|
||||
TIP: The fact that the POJO Function class is a Spring bean may offer other benefits. Since it shares
|
||||
the `ApplicationContext` with {data-store-name} components, such as the cache and Regions, these may be injected into
|
||||
the class if necessary.
|
||||
|
||||
Spring creates the wrapper class and registers the Functions with {data-store-name}'s `FunctionService`. The Function ID
|
||||
used to register each Function must be unique. By using convention, it defaults to the simple (unqualified) method name.
|
||||
The name can be explicitly defined by using the `id` attribute of the `@GemfireFunction` annotation.
|
||||
|
||||
The `@GemfireFunction` annotation also provides other configuration attributes: `HA` and `optimizedForWrite`,
|
||||
which correspond to properties defined by {data-store-name}'s
|
||||
{x-data-store-javadoc}/org/apache/geode/cache/execute/Function.html[`Function`] interface.
|
||||
|
||||
If the POJO Function method's return type is `void`, then the `hasResult` attribute is automatically set to `false`.
|
||||
Otherwise, if the method returns a value, the `hasResult` attributes is set to `true`. Even for `void` method return
|
||||
types, the `GemfireFunction` annotation's `hasResult` attribute can be set to `true` to override this convention,
|
||||
as shown in the `functionWithContext` method shown previously. Presumably, the intention is that you will use
|
||||
the `ResultSender` directly to send results to the caller.
|
||||
|
||||
Finally, the `GemfireFunction` annotation supports the `requiredPermissions` attribute, which specifies the permissions
|
||||
required to execute the Function. By default, all Functions require the `DATA:WRITE` permission. The attribute
|
||||
accepts an array of Strings allowing you to modify the permissions as required by your application and/or Function UC.
|
||||
Each resource permission is expected to be in the following format: `<RESOURCE>:<OPERATION>:[Target]:[Key]`.
|
||||
|
||||
`RESOURCE` can be 1 of the {data-store-javadoc]/org/apache/geode/security/ResourcePermission.Resource.html[`ResourcePermission.Resource`]
|
||||
enumerated values. `OPERATION` can be 1 of the {data-store-javadoc}/org/apache/geode/security/ResourcePermission.Operation.html[`ResourcePermission.Operation`]
|
||||
enumerated values. Optionally, `Target` can be the name of a Region or 1 of the
|
||||
{data-store-javadoc}/org/apache/geode/security/ResourcePermission.Target.html[`ResourcePermission.Target`]
|
||||
enumerated values. And finally, optionally, `Key` is a valid Key in the `Target` Region if specified.
|
||||
|
||||
The `PojoFunctionWrapper` implements {data-store-name}'s `Function` interface, binds method parameters, and invokes
|
||||
the target method in its `execute()` method. It also sends the method's return value back to the caller
|
||||
by using the `ResultSender`.
|
||||
|
||||
=== Batching Results
|
||||
|
||||
If the return type is an array or `Collection`, then some consideration must be given to how the results are returned.
|
||||
By default, the `PojoFunctionWrapper` returns the entire array or `Collection` at once. If the number of elements
|
||||
in the array or `Collection` is quite large, it may incur a performance penalty. To divide the payload into smaller,
|
||||
more manageable chunks, you can set the `batchSize` attribute, as illustrated in `function2`, shown earlier.
|
||||
|
||||
TIP: If you need more control of the `ResultSender`, especially if the method itself would use too much memory
|
||||
to create the `Collection`, you can pass in the `ResultSender` or access it through the `FunctionContext`
|
||||
and use it directly within the method to sends results back to the caller.
|
||||
|
||||
=== Enabling Annotation Processing
|
||||
|
||||
In accordance with Spring standards, you must explicitly activate annotation processing for `@GemfireFunction`
|
||||
annotations. The following example activates annotation processing with XML:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<gfe:annotation-driven/>
|
||||
----
|
||||
|
||||
The following example activates annotation processing by annotating a Java configuration class:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Configuration
|
||||
@EnableGemfireFunctions
|
||||
class ApplicationConfiguration { ... }
|
||||
----
|
||||
|
||||
[[function-execution]]
|
||||
== Executing a Function
|
||||
|
||||
A process that invokes a remote Function needs to provide the Function's ID, calling arguments, the execution target
|
||||
(`onRegion`, `onServers`, `onServer`, `onMember`, or `onMembers`) and (optionally) a filter set. By using {sdg-name},
|
||||
all you need do is define an interface supported by annotations. Spring creates a dynamic proxy for the interface,
|
||||
which uses the `FunctionService` to create an `Execution`, invoke the `Execution`, and (if necessary) coerce
|
||||
the results to the defined return type. This technique is similar to the way {sdg-name}'s Repository extension works.
|
||||
Thus, some of the configuration and concepts should be familiar.
|
||||
|
||||
Generally, a single interface definition maps to multiple Function executions, one corresponding to each method
|
||||
defined in the interface.
|
||||
|
||||
=== Annotations for Function Execution
|
||||
|
||||
To support client-side Function execution, the following {sdg-acronym} Function annotations are provided: `@OnRegion`,
|
||||
`@OnServer`, `@OnServers`, `@OnMember`, and `@OnMembers`. These annotations correspond to the `Execution`
|
||||
implementations provided by {data-store-name}'s
|
||||
{x-data-store-javadoc}/org/apache/geode/cache/execute/FunctionService.html[`FunctionService`] class.
|
||||
|
||||
Each annotation exposes the appropriate attributes. These annotations also provide an optional `resultCollector` attribute
|
||||
whose value is the name of a Spring bean implementing the
|
||||
{x-data-store-javadoc}/org/apache/geode/cache/execute/ResultCollector.html[`ResultCollector`] interface
|
||||
to use for the execution.
|
||||
|
||||
CAUTION: The proxy interface binds all declared methods to the same execution configuration. Although it is expected
|
||||
that single method interfaces are common, all methods in the interface are backed by the same proxy instance
|
||||
and therefore all share the same configuration.
|
||||
|
||||
The following listing shows a few examples:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@OnRegion(region="SomeRegion", resultCollector="myCollector")
|
||||
public interface FunctionExecution {
|
||||
|
||||
@FunctionId("function1")
|
||||
String doIt(String s1, int i2);
|
||||
|
||||
String getString(Object arg1, @Filter Set<Object> keys);
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
By default, the Function ID is the simple (unqualified) method name. The `@FunctionId` annotation can be used
|
||||
to bind this invocation to a different Function ID.
|
||||
|
||||
=== Enabling Annotation Processing
|
||||
|
||||
The client-side uses Spring's classpath component scanning capability to discover annotated interfaces. To enable
|
||||
Function execution annotation processing in XML, insert the following element in your XML configuration:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<gfe-data:function-executions base-package="org.example.myapp.gemfire.functions"/>
|
||||
----
|
||||
|
||||
The `function-executions` element is provided in the `gfe-data` XML namespace. The `base-package` attribute is required
|
||||
to avoid scanning the entire classpath. Additional filters can be provided as described in the Spring
|
||||
https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-scanning-filters[reference documentation].
|
||||
|
||||
Optionally, you can annotate your Java configuration class as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@EnableGemfireFunctionExecutions(basePackages = "org.example.myapp.gemfire.functions")
|
||||
----
|
||||
|
||||
[[function-execution-programmatic]]
|
||||
== Programmatic Function Execution
|
||||
|
||||
Using the Function execution annotated interface defined in the previous section, simply auto-wire your interface
|
||||
into an application bean that will invoke the Function:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Component
|
||||
public class MyApplication {
|
||||
|
||||
@Autowired
|
||||
FunctionExecution functionExecution;
|
||||
|
||||
public void doSomething() {
|
||||
functionExecution.doIt("hello", 123);
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Alternately, you can use a Function execution template directly. In the following example,
|
||||
the `GemfireOnRegionFunctionTemplate` creates an `onRegion` Function `Execution`:
|
||||
|
||||
.Using the `GemfireOnRegionFunctionTemplate`
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
Set<?, ?> myFilter = getFilter();
|
||||
Region<?, ?> myRegion = getRegion();
|
||||
GemfireOnRegionOperations template = new GemfireOnRegionFunctionTemplate(myRegion);
|
||||
String result = template.executeAndExtract("someFunction", myFilter, "hello", "world", 1234);
|
||||
----
|
||||
====
|
||||
|
||||
Internally, Function `Executions` always return a `List`. `executeAndExtract` assumes a singleton `List`
|
||||
containing the result and attempts to coerce that value into the requested type. There is also an `execute` method
|
||||
that returns the `List` as is. The first parameter is the Function ID. The filter argument is optional. The remaining
|
||||
arguments are a variable argument `List`.
|
||||
|
||||
[[function-execution-pdx]]
|
||||
== Function Execution with PDX
|
||||
|
||||
When using {sdg-name}'s Function annotation support combined with {data-store-name}'s
|
||||
{x-data-store-docs}/developing/data_serialization/gemfire_pdx_serialization.html[PDX Serialization],
|
||||
there are a few logistical things to keep in mind.
|
||||
|
||||
As explained earlier in this section, and by way of example, you should typically define {data-store-name} Functions
|
||||
by using POJO classes annotated with {sdg-name}
|
||||
https://docs.spring.io/spring-data-gemfire/docs/current/api/org/springframework/data/gemfire/function/annotation/package-summary.html[Function annotations],
|
||||
as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
public class OrderFunctions {
|
||||
|
||||
@GemfireFunction(...)
|
||||
Order process(@RegionData data, Order order, OrderSource orderSourceEnum, Integer count) { ... }
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
NOTE: The `Integer` typed `count` parameter is arbitrary, as is the separation of the `Order` class
|
||||
and the `OrderSource` enum, which might be logical to combine. However, the arguments were setup this way
|
||||
to demonstrate the problem with Function executions in the context of PDX.
|
||||
|
||||
Your `Order` class and `OrderSource` enum might be defined as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
public class Order ... {
|
||||
|
||||
private Long orderNumber;
|
||||
private LocalDateTime orderDateTime;
|
||||
private Customer customer;
|
||||
private List<Item> items
|
||||
|
||||
...
|
||||
}
|
||||
|
||||
|
||||
public enum OrderSource {
|
||||
ONLINE,
|
||||
PHONE,
|
||||
POINT_OF_SALE
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
Of course, you can define a Function `Execution` interface to call the 'process' {data-store-name} server Function,
|
||||
as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@OnServer
|
||||
public interface OrderProcessingFunctions {
|
||||
Order process(Order order, OrderSource orderSourceEnum, Integer count);
|
||||
}
|
||||
----
|
||||
|
||||
Clearly, this `process(..)` `Order` Function is being called from the client-side with a `ClientCache` instance
|
||||
(that is `<gfe:client-cache/>`). This implies that the Function arguments must also be serializable. The same is true
|
||||
when invoking peer-to-peer member Functions (such as `@OnMember(s)`) between peers in the cluster. Any form of
|
||||
`distribution` requires the data transmitted between client and server (or peers) to be serialized.
|
||||
|
||||
Now, if you have configured {data-store-name} to use PDX for serialization (instead of Java serialization, for instance)
|
||||
you can also set the `pdx-read-serialized` attribute to `true` in your configuration of the {data-store-name} server(s),
|
||||
as follows:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<gfe:cache ... pdx-read-serialized="true"/>
|
||||
----
|
||||
|
||||
Alternatively, you can set the `pdx-read-serialized` attribute to `true` for a {data-store-name} cache client application,
|
||||
as follows:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<gfe:client-cache ... pdx-read-serialized="true"/>
|
||||
----
|
||||
|
||||
Doing so causes all values read from the cache (that is, Regions) as well as information passed between client and servers
|
||||
(or peers) to remain in serialized form, including, but not limited to, Function arguments.
|
||||
|
||||
{data-store-name} serializes only application domain object types that you have specifically configured (registered)
|
||||
either by using {data-store-name}'s
|
||||
{x-data-store-javadoc}/org/apache/geode/pdx/ReflectionBasedAutoSerializer.html[`ReflectionBasedAutoSerializer`],
|
||||
or specifically (and recommended) by using a "`custom`" {data-store-name}
|
||||
{x-data-store-javadoc}/org/apache/geode/pdx/PdxSerializer.html[`PdxSerializer`].
|
||||
If you use {sdg-name}'s Repository extension, you might even want to consider using {sdg-name}'s
|
||||
{sdg-javadoc}/org/springframework/data/gemfire/mapping/MappingPdxSerializer.html[`MappingPdxSerializer`],
|
||||
which uses an entity's mapping metadata to determine data from the application domain object that is serialized
|
||||
to the PDX instance.
|
||||
|
||||
What is less than apparent, though, is that {data-store-name} automatically handles Java `Enum` types regardless
|
||||
of whether they are explicitly configured (that is, registered with a `ReflectionBasedAutoSerializer`,
|
||||
using a regex pattern and the `classes` parameter or are handled by a "`custom`" {data-store-name} `PdxSerializer`),
|
||||
despite the fact that Java enumerations implement `java.io.Serializable`.
|
||||
|
||||
So, when you set `pdx-read-serialized` to `true` on {data-store-name} servers where the {data-store-name} Functions
|
||||
(including {sdg-name} Function-annotated POJO classes) are registered, then you may encounter surprising behavior
|
||||
when invoking the Function `Execution`.
|
||||
|
||||
You might pass the following arguments when invoking the Function:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
orderProcessingFunctions.process(new Order(123, customer, LocalDateTime.now(), items), OrderSource.ONLINE, 400);
|
||||
----
|
||||
|
||||
However, the {data-store-name} Function on the server gets the following:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
process(regionData, order:PdxInstance, :PdxInstanceEnum, 400);
|
||||
----
|
||||
|
||||
The `Order` and `OrderSource` have been passed to the Function as
|
||||
{x-data-store-javadoc}/org/apache/geode/pdx/PdxInstance.html[PDX instances].
|
||||
Again, this all happens because `pdx-read-serialized` is set to `true`, which may be necessary in cases where
|
||||
the {data-store-name} servers interact with multiple different clients (for example, a combination of Java clients
|
||||
and native clients, such as C/C++, C#, and others).
|
||||
|
||||
This flies in the face of {sdg-name}'s strongly-typed Function-annotated POJO class method signatures, where you would
|
||||
reasonably expect application domain object types instead, not PDX serialized instances.
|
||||
|
||||
Consequently, {sdg-name} includes enhanced Function support to automatically convert PDX typed method arguments
|
||||
to the desired application domain object types defined by the Function method's signature (parameter types).
|
||||
|
||||
However, this also requires you to explicitly register a {data-store-name} `PdxSerializer` on {data-store-name} servers
|
||||
where {sdg-name} Function-annotated POJOs are registered and used, as the following example shows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
<bean id="customPdxSerializer" class="x.y.z.gemfire.serialization.pdx.MyCustomPdxSerializer"/>
|
||||
|
||||
<gfe:cache ... pdx-serializer-ref="customPdxSerializeer" pdx-read-serialized="true"/>
|
||||
----
|
||||
|
||||
Alternatively, you can use {data-store-name}'s
|
||||
{x-data-store-javadoc}/org/apache/geode/pdx/ReflectionBasedAutoSerializer.html[`ReflectionBasedAutoSerializer`]
|
||||
for convenience. Of course, we recommend that, where possible, you use a custom `PdxSerializer` to maintain
|
||||
finer-grained control over your serialization strategy.
|
||||
|
||||
Finally, {sdg-name} is careful not to convert your Function arguments if you treat your Function arguments generically
|
||||
or as one of {data-store-name}'s PDX types, as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@GemfireFunction
|
||||
public Object genericFunction(String value, Object domainObject, PdxInstanceEnum enum) {
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
{sdg-name} converts PDX typed data to the corresponding application domain types if and only if the corresponding
|
||||
application domain types are on the classpath and the Function-annotated POJO method expects it.
|
||||
|
||||
For a good example of custom, composed application-specific {data-store-name} `PdxSerializers` as well as appropriate
|
||||
POJO Function parameter type handling based on the method signatures, see {sdg-name}'s
|
||||
https://github.com/spring-projects/spring-data-gemfire/blob/{revnumber}/src/test/java/org/springframework/data/gemfire/function/ClientCacheFunctionExecutionWithPdxIntegrationTest.java[`ClientCacheFunctionExecutionWithPdxIntegrationTest`] class.
|
||||
@@ -1,29 +0,0 @@
|
||||
[[bootstrap:function]]
|
||||
= Configuring the Function Service
|
||||
|
||||
{sdg-name} provides <<function-annotations,annotation>> support for implementing, registering and executing
|
||||
{data-store-name} Functions.
|
||||
|
||||
{sdg-name} also provides XML namespace support for registering {data-store-name}
|
||||
{x-data-store-javadoc}/org/apache/geode/cache/execute/Function.html[Functions]
|
||||
for remote function execution.
|
||||
|
||||
See {data-store-name}'s {x-data-store-docs}/developing/function_exec/chapter_overview.html[documentation]
|
||||
for more information on the Function execution framework.
|
||||
|
||||
{data-store-name} Functions are declared as Spring beans and must implement the `org.apache.geode.cache.execute.Function`
|
||||
interface or extend `org.apache.geode.cache.execute.FunctionAdapter`.
|
||||
|
||||
The namespace uses a familiar pattern to declare Functions, as the following example shows:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<gfe:function-service>
|
||||
<gfe:function>
|
||||
<bean class="example.FunctionOne"/>
|
||||
<ref bean="function2"/>
|
||||
</gfe:function>
|
||||
</gfe:function-service>
|
||||
|
||||
<bean id="function2" class="example.FunctionTwo"/>
|
||||
----
|
||||
@@ -1,66 +0,0 @@
|
||||
[[bootstrap:gateway]]
|
||||
= Configuring WAN Gateways
|
||||
|
||||
WAN Gateways provides a way to synchronize {data-store-name} Distributed Systems across geographic locations.
|
||||
{sdg-name} provides XML namespace support for configuring WAN Gateways as illustrated in the following examples.
|
||||
|
||||
== WAN Configuration in {data-store-name} 7.0
|
||||
|
||||
In the following example, `GatewaySenders` are configured for a `PARTITION` Region by adding child elements
|
||||
(`gateway-sender` and `gateway-sender-ref`) to the Region. A `GatewaySender` may register `EventFilters`
|
||||
and `TransportFilters`.
|
||||
|
||||
The following example also shows a sample configuration of an `AsyncEventQueue`, which must also be auto-wired
|
||||
into a Region (not shown):
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<gfe:partitioned-region id="region-with-inner-gateway-sender" >
|
||||
<gfe:gateway-sender remote-distributed-system-id="1">
|
||||
<gfe:event-filter>
|
||||
<bean class="org.springframework.data.gemfire.example.SomeEventFilter"/>
|
||||
</gfe:event-filter>
|
||||
<gfe:transport-filter>
|
||||
<bean class="org.springframework.data.gemfire.example.SomeTransportFilter"/>
|
||||
</gfe:transport-filter>
|
||||
</gfe:gateway-sender>
|
||||
<gfe:gateway-sender-ref bean="gateway-sender"/>
|
||||
</gfe:partitioned-region>
|
||||
|
||||
<gfe:async-event-queue id="async-event-queue" batch-size="10" persistent="true" disk-store-ref="diskstore"
|
||||
maximum-queue-memory="50">
|
||||
<gfe:async-event-listener>
|
||||
<bean class="example.AsyncEventListener"/>
|
||||
</gfe:async-event-listener>
|
||||
</gfe:async-event-queue>
|
||||
|
||||
<gfe:gateway-sender id="gateway-sender" remote-distributed-system-id="2">
|
||||
<gfe:event-filter>
|
||||
<ref bean="event-filter"/>
|
||||
<bean class="org.springframework.data.gemfire.example.SomeEventFilter"/>
|
||||
</gfe:event-filter>
|
||||
<gfe:transport-filter>
|
||||
<ref bean="transport-filter"/>
|
||||
<bean class="org.springframework.data.gemfire.example.SomeTransportFilter"/>
|
||||
</gfe:transport-filter>
|
||||
</gfe:gateway-sender>
|
||||
|
||||
<bean id="event-filter" class="org.springframework.data.gemfire.example.AnotherEventFilter"/>
|
||||
<bean id="transport-filter" class="org.springframework.data.gemfire.example.AnotherTransportFilter"/>
|
||||
----
|
||||
|
||||
On the other end of a `GatewaySender` is a corresponding `GatewayReceiver` to receive Gateway events.
|
||||
The `GatewayReceiver` may also be configured with `EventFilters` and `TransportFilters`, as follows:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<gfe:gateway-receiver id="gateway-receiver" start-port="12345" end-port="23456" bind-address="192.168.0.1">
|
||||
<gfe:transport-filter>
|
||||
<bean class="org.springframework.data.gemfire.example.SomeTransportFilter"/>
|
||||
</gfe:transport-filter>
|
||||
</gfe:gateway-receiver>
|
||||
----
|
||||
|
||||
See the {data-store-name}
|
||||
{x-data-store-docs}/topologies_and_comm/multi_site_configuration/chapter_overview.html[documentation]
|
||||
for a detailed explanation of all the configuration options.
|
||||
@@ -1,178 +0,0 @@
|
||||
[[gemfire-bootstrap]]
|
||||
= Bootstrapping a Spring ApplicationContext in {data-store-name}
|
||||
|
||||
Normally, a Spring-based application <<bootstrap,bootstraps {data-store-name}>> by using {sdg-name}'s features.
|
||||
By specifying a `<gfe:cache/>` element that uses the {sdg-name} XML namespace, a single embedded {data-store-name}
|
||||
peer `Cache` instance is created and initialized with default settings in the same JVM process as your application.
|
||||
|
||||
However, it is sometimes necessary (perhaps as a requirement imposed by your IT organization) that {data-store-name}
|
||||
be fully managed and operated by the provided {data-store-name} tool suite, perhaps using
|
||||
{x-data-store-docs}/tools_modules/gfsh/chapter_overview.html[Gfsh]. By using _Gfsh_, {data-store-name} bootstraps
|
||||
your Spring `ApplicationContext` rather than the other way around. Instead of an application server or a Java main class
|
||||
that uses Spring Boot, {data-store-name} does the bootstrapping and hosts your application.
|
||||
|
||||
NOTE: {data-store-name} is not an application server. In addition, there are limitations to using this approach
|
||||
where the {data-store-name} cache configuration is concerned.
|
||||
|
||||
[[gemfire-bootstrap-gfsh]]
|
||||
== Using {data-store-name} to Bootstrap a Spring Context Started with Gfsh
|
||||
|
||||
In order to bootstrap a Spring `ApplicationContext` in {data-store-name} when starting a {data-store-name} server
|
||||
using _Gfsh_, you must use {data-store-name}'s
|
||||
{x-data-store-docs}/basic_config/the_cache/setting_cache_initializer.html[initalizer] capability.
|
||||
An initializer block can declare a application callback that is launched after the cache is initialized
|
||||
by {data-store-name}.
|
||||
|
||||
An initializer is declared within an {x-data-store-docs}/reference/topics/cache_xml.html#initializer[initializer] element
|
||||
by using a minimal snippet of {data-store-name}'s native `cache.xml`. To bootstrap the Spring `ApplicationContext`,
|
||||
a `cache.xml` file is required, in much the same way as a minimal snippet of Spring XML config is needed to bootstrap
|
||||
a Spring `ApplicationContext` configured with component scanning
|
||||
(for example `<context:component-scan base-packages="..."/>`).
|
||||
|
||||
Fortunately, such an initializer is already conveniently provided by the framework: the
|
||||
{sdg-javadoc}/org/springframework/data/gemfire/support/SpringContextBootstrappingInitializer.html[`SpringContextBootstrappingInitializer`].
|
||||
|
||||
The following example shows a typical, yet minimal, configuration for this class inside {data-store-name}'s
|
||||
`cache.xml` file:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<cache xmlns="http://geode.apache.org/schema/cache"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://geode.apache.org/schema/cache https://geode.apache.org/schema/cache/cache-1.0.xsd"
|
||||
version="1.0">
|
||||
|
||||
<initializer>
|
||||
<class-name>org.springframework.data.gemfire.support.SpringContextBootstrappingInitializer</class-name>
|
||||
<parameter name="contextConfigLocations">
|
||||
<string>classpath:application-context.xml</string>
|
||||
</parameter>
|
||||
</initializer>
|
||||
|
||||
</cache>
|
||||
----
|
||||
|
||||
The `SpringContextBootstrappingInitializer` class follows conventions similar to Spring's `ContextLoaderListener`
|
||||
class, which is used to bootstrap a Spring `ApplicationContext` inside a web application, where `ApplicationContext`
|
||||
configuration files are specified with the `contextConfigLocations` Servlet context parameter.
|
||||
|
||||
In addition, the `SpringContextBootstrappingInitializer` class can also be used with a `basePackages` parameter
|
||||
to specify a comma-separated list of base packages that contain appropriately annotated application components.
|
||||
The Spring container searches these components to find and create Spring beans and other application components
|
||||
in the classpath, as the following example shows:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<cache xmlns="http://geode.apache.org/schema/cache"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://geode.apache.org/schema/cache https://geode.apache.org/schema/cache/cache-1.0.xsd"
|
||||
version="1.0">
|
||||
|
||||
<initializer>
|
||||
<class-name>org.springframework.data.gemfire.support.SpringContextBootstrappingInitializer</class-name>
|
||||
<parameter name="basePackages">
|
||||
<string>org.mycompany.myapp.services,org.mycompany.myapp.dao,...</string>
|
||||
</parameter>
|
||||
</initializer>
|
||||
|
||||
</cache>
|
||||
----
|
||||
|
||||
Then, with a properly configured and constructed `CLASSPATH` and `cache.xml` file (shown earlier) specified as
|
||||
a command-line option when starting a {data-store-name} server in _Gfsh_, the command-line would be as follows:
|
||||
|
||||
[source]
|
||||
----
|
||||
gfsh>start server --name=ExampleServer --log-level=config ...
|
||||
--classpath="/path/to/application/classes.jar:/path/to/spring-data-geode-<major>.<minor>.<maint>.RELEASE.jar"
|
||||
--cache-xml-file="/path/to/geode/cache.xml"
|
||||
----
|
||||
|
||||
The `application-context.xml` can be any valid Spring configuration metadata, including all of the {sdg-acronym}
|
||||
XML namespace elements. The only limitation with this approach is that a {data-store-name} cache cannot be configured
|
||||
by using the {sdg-acronym} XML namespace. In other words, none of the `<gfe:cache/>` element attributes
|
||||
(such as `cache-xml-location`, `properties-ref`, `critical-heap-percentage`, `pdx-serializer-ref`, `lock-lease`,
|
||||
and others) can be specified. If used, these attributes are ignored.
|
||||
|
||||
The reason for this is that {data-store-name} itself has already created and initialized the cache before the initializer
|
||||
gets invoked. As a result, the cache already exists and, since it is a "`singleton`", it cannot be re-initialized
|
||||
or have any of its configuration augmented.
|
||||
|
||||
[[gemfire-bootstrap-lazywiring]]
|
||||
== Lazy-wiring {data-store-name} Components
|
||||
|
||||
{sdg-name} already provides support for auto-wiring {data-store-name} components (such as `CacheListeners`,
|
||||
`CacheLoaders`, `CacheWriters` and so on) that are declared and created by {data-store-name} in `cache.xml` by using
|
||||
{sdg-acronym}'s `WiringDeclarableSupport` class, as described in <<apis:declarable:autowiring>>. However, this works
|
||||
only when Spring is the one doing the bootstrapping (that is, when Spring bootstraps {data-store-name}).
|
||||
|
||||
When your Spring `ApplicationContext` is bootstrapped by {data-store-name}, these {data-store-name} application components
|
||||
go unnoticed, because the Spring `ApplicationContext` does not exist yet. The Spring `ApplicationContext` does not get
|
||||
created until {data-store-name} calls the initializer block, which only occurs after all the other {data-store-name}
|
||||
components (cache, Regions, and others) have already been created and initialized.
|
||||
|
||||
To solve this problem, a new `LazyWiringDeclarableSupport` class was introduced. This new class is aware of the
|
||||
Spring `ApplicationContext`. The intention behind this abstract base class is that any implementing class registers
|
||||
itself to be configured by the Spring container that is eventually created by {data-store-name} once the initializer
|
||||
is called. In essence, this gives your {data-store-name} application components a chance to be configured and auto-wired
|
||||
with Spring beans defined in the Spring container.
|
||||
|
||||
In order for your {data-store-name} application components to be auto-wired by the Spring container, you should create
|
||||
an application class that extends the `LazyWiringDeclarableSupport` and annotate any class member that needs to be
|
||||
provided as a Spring bean dependency, similar to the following example:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
public class UserDataSourceCacheLoader extends LazyWiringDeclarableSupport
|
||||
implements CacheLoader<String, User> {
|
||||
|
||||
@Autowired
|
||||
private DataSource userDataSource;
|
||||
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
As implied in the `CacheLoader` example above, you might necessarily (though rarely) have defined both a Region
|
||||
and a `CacheListener` component in {data-store-name} `cache.xml`. The `CacheLoader` may need access to an application
|
||||
Repository (or perhaps a JDBC `DataSource` defined in the Spring `ApplicationContext`) for loading `Users` into a
|
||||
{data-store-name} `REPLICATE` Region on startup.
|
||||
|
||||
CAUTION
|
||||
====
|
||||
Be careful when mixing the different life-cycles of {data-store-name} and the Spring container together in this manner.
|
||||
Not all use cases and scenarios are supported. The {data-store-name} `cache.xml` configuration would be similar to
|
||||
the following (which comes from {sdg-acronym}'s test suite):
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<cache xmlns="http://geode.apache.org/schema/cache"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://geode.apache.org/schema/cache https://geode.apache.org/schema/cache/cache-1.0.xsd"
|
||||
version="1.0">
|
||||
|
||||
<region name="Users" refid="REPLICATE">
|
||||
<region-attributes initial-capacity="101" load-factor="0.85">
|
||||
<key-constraint>java.lang.String</key-constraint>
|
||||
<value-constraint>org.springframework.data.gemfire.repository.sample.User</value-constraint>
|
||||
<cache-loader>
|
||||
<class-name>
|
||||
org.springframework.data.gemfire.support.SpringContextBootstrappingInitializerIntegrationTest$UserDataStoreCacheLoader
|
||||
</class-name>
|
||||
</cache-loader>
|
||||
</region-attributes>
|
||||
</region>
|
||||
|
||||
<initializer>
|
||||
<class-name>org.springframework.data.gemfire.support.SpringContextBootstrappingInitializer</class-name>
|
||||
<parameter name="basePackages">
|
||||
<string>org.springframework.data.gemfire.support.sample</string>
|
||||
</parameter>
|
||||
</initializer>
|
||||
|
||||
</cache>
|
||||
----
|
||||
====
|
||||
@@ -1,235 +0,0 @@
|
||||
[[bootstrap:indexing]]
|
||||
= Configuring an Index
|
||||
|
||||
{data-store-name} allows indexes (also sometimes pluralized as indices) to be created on Region data
|
||||
to improve the performance of OQL (Object Query Language) queries.
|
||||
|
||||
In {sdg-name}, indexes are declared with the `index` element, as the following example shows:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<gfe:index id="myIndex" expression="someField" from="/SomeRegion" type="HASH"/>
|
||||
----
|
||||
|
||||
In {sdg-name}'s XML schema (also called the {sdg-acronym} XML namespace), `index` bean declarations are not bound
|
||||
to a Region, unlike {data-store-name}'s native `cache.xml`. Rather, they are top-level elements similar to
|
||||
`<gfe:cache>` element. This lets you declare any number of indexes on any Region, whether they were just created
|
||||
or already exist -- a significant improvement over {data-store-name}'s native `cache.xml` format.
|
||||
|
||||
An `Index` must have a name. You can give the `Index` an explicit name by using the `name` attribute.
|
||||
Otherwise, the bean name (that is, the value of the `id` attribute) of the `index` bean definition is used as
|
||||
the `Index` name.
|
||||
|
||||
The `expression` and `from` clause form the main components of an `Index`, identifying the data to index
|
||||
(that is, the Region identified in the `from` clause) along with what criteria (that is, `expression`) is used
|
||||
to index the data. The `expression` should be based on what application domain object fields are used in the predicate
|
||||
of application-defined OQL queries used to query and look up the objects stored in the Region.
|
||||
|
||||
Consider the following example, which has a `lastName` property:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Region("Customers")
|
||||
class Customer {
|
||||
|
||||
@Id
|
||||
Long id;
|
||||
|
||||
String lastName;
|
||||
String firstName;
|
||||
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
Now consider the following example, which has an application-defined {sdg-acronym} Repository
|
||||
to query for `Customer` objects:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
interface CustomerRepository extends GemfireRepository<Customer, Long> {
|
||||
|
||||
Customer findByLastName(String lastName);
|
||||
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
The {sdg-acronym} Repository finder/query method results in the following OQL statement being generated and ran:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
SELECT * FROM /Customers c WHERE c.lastName = '$1'
|
||||
----
|
||||
|
||||
Therefore, you might want to create an `Index` with a statement similar to the following:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<gfe:index id="myIndex" name="CustomersLastNameIndex" expression="lastName" from="/Customers" type="HASH"/>
|
||||
----
|
||||
|
||||
The `from` clause must refer to a valid, existing Region and is how an `Index` gets applied to a Region.
|
||||
This is not specific to {sdg-name}. It is a feature of {data-store-name}.
|
||||
|
||||
The `Index` `type` may be one of three enumerated values defined by {sdg-name}'s
|
||||
{sdg-javadoc}/org/springframework/data/gemfire/IndexType.html[`IndexType`] enumeration:
|
||||
`FUNCTIONAL`, `HASH`, and `PRIMARY_KEY`.
|
||||
|
||||
Each of the enumerated values corresponds to one of the {x-data-store-javadoc}/org/apache/geode/cache/query/QueryService.html[`QueryService`]
|
||||
`create[|Key|Hash]Index` methods invoked when the actual `Index` is to be created (or "`defined`" -- you can find
|
||||
more on "`defining`" indexes in the next section). For instance, if the `IndexType` is `PRIMARY_KEY`, then the
|
||||
{x-data-store-javadoc}/org/apache/geode/cache/query/QueryService.html#createKeyIndex-java.lang.String-java.lang.String-java.lang.String-[QueryService.createKeyIndex(..)]
|
||||
is invoked to create a `KEY` `Index`.
|
||||
|
||||
The default is `FUNCTIONAL` and results in one of the `QueryService.createIndex(..)` methods being invoked. See the
|
||||
{sdg-name} XML schema for a full set of options.
|
||||
|
||||
For more information on indexing in {data-store-name}, see "`https://gemfire90.docs.pivotal.io/geode/developing/query_index/query_index.html[Working with Indexes]`"
|
||||
in {data-store-name}'s User Guide.
|
||||
|
||||
== Defining Indexes
|
||||
|
||||
In addition to creating indexes up front as `Index` bean definitions are processed by {sdg-name} on Spring container
|
||||
initialization, you may also define all of your application indexes prior to creating them by using the `define`
|
||||
attribute, as follows:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<gfe:index id="myDefinedIndex" expression="someField" from="/SomeRegion" define="true"/>
|
||||
----
|
||||
|
||||
When `define` is set to `true` (it defaults to `false`), it does not actually create the `Index` at that moment.
|
||||
All "`defined`" Indexes are created all at once, when the Spring `ApplicationContext` is "`refreshed`" or, to put it
|
||||
differently, when a `ContextRefreshedEvent` is published by the Spring container. {sdg-name} registers itself as
|
||||
an `ApplicationListener` listening for the `ContextRefreshedEvent`. When fired, {sdg-name} calls
|
||||
{x-data-store-javadoc}/org/apache/geode/cache/query/QueryService.html#createDefinedIndexes[`QueryService.createDefinedIndexes()`].
|
||||
|
||||
Defining indexes and creating them all at once boosts speed and efficiency when creating indexes.
|
||||
|
||||
See "`https://gemfire90.docs.pivotal.io/geode/developing/query_index/create_multiple_indexes.html[Creating Multiple Indexes at Once]`"
|
||||
for more details.
|
||||
|
||||
== `IgnoreIfExists` and `Override`
|
||||
|
||||
Two {sdg-name} `Index` configuration options warrant special mention: `ignoreIfExists` and `override`.
|
||||
|
||||
These options correspond to the `ignore-if-exists` and `override` attributes on the `<gfe:index>` element
|
||||
in {sdg-name}'s XML namespace, respectively.
|
||||
|
||||
WARNING: Make sure you absolutely understand what you are doing before using either of these options. These options can
|
||||
affect the performance and resources (such as memory) consumed by your application at runtime. As a result, both of
|
||||
these options are disabled (set to `false`) in {sdg-acronym} by default.
|
||||
|
||||
NOTE: These options are only available in {sdg-name} and exist to workaround known limitations with {data-store-name}.
|
||||
{data-store-name} has no equivalent options or functionality.
|
||||
|
||||
Each option significantly differs in behavior and entirely depends on the type of {data-store-name} `Index` exception
|
||||
thrown. This also means that neither option has any effect if a {data-store-name} Index-type exception is not thrown.
|
||||
These options are meant to specifically handle {data-store-name} `IndexExistsException` and `IndexNameConflictException`
|
||||
instances, which can occur for various, sometimes obscure reasons. The exceptions have the following causes:
|
||||
|
||||
* An {x-data-store-javadoc}/org/apache/geode/cache/query/IndexExistsException.html[`IndexExistsException`]
|
||||
is thrown when there exists another `Index` with the same definition but a different name when attempting to
|
||||
create an `Index`.
|
||||
|
||||
* An {x-data-store-javadoc}/org/apache/geode/cache/query/IndexNameConflictException.html[`IndexNameConflictException`]
|
||||
is thrown when there exists another `Index` with the same name but possibly different definition when attempting to
|
||||
create an `Index`.
|
||||
|
||||
{sdg-name}'s default behavior is to fail-fast, always. So, neither `Index` _Exception_ are "`handled`" by default.
|
||||
These `Index` exceptions are wrapped in a {sdg-acronym} `GemfireIndexException` and rethrown. If you wish for {sdg-name}
|
||||
to handle them for you, you can set either of these `Index` bean definition options to `true`.
|
||||
|
||||
`IgnoreIfExists` always takes precedence over `Override`, primarily because it uses fewer resources, simply because
|
||||
it returns the "`existing`" `Index` in both exceptional cases.
|
||||
|
||||
=== `IgnoreIfExists` Behavior
|
||||
|
||||
When an `IndexExistsException` is thrown and `ignoreIfExists` is set to `true` (or `<gfe:index ignore-if-exists="true">`),
|
||||
then the `Index` that would have been created by this `index` bean definition or declaration is simply ignored,
|
||||
and the existing `Index` is returned.
|
||||
|
||||
There is little consequence in returning the existing `Index`, since the `index` bean definition is the same,
|
||||
as determined by {data-store-name} itself, not {sdg-acronym}.
|
||||
|
||||
However, this also means that no `Index` with the "`name`" specified in your `index` bean definition or declaration
|
||||
actually exists from {data-store-name}'s perspective (that is, with
|
||||
{x-data-store-javadoc}/org/apache/geode/cache/query/QueryService.html#getIndexes[`QueryService.getIndexes()`]).
|
||||
Therefore, you should be careful when writing OQL query statements that use query hints, especially query hints
|
||||
that refer to the application `Index` being ignored. Those query hints need to be changed.
|
||||
|
||||
When an `IndexNameConflictException` is thrown and `ignoreIfExists` is set to `true` (or `<gfe:index ignore-if-exists="true">`),
|
||||
the `Index` that would have been created by this `index` bean definition or declaration is also ignored,
|
||||
and the "existing" `Index` is again returned, as when an `IndexExistsException` is thrown.
|
||||
|
||||
However, there is more risk in returning the existing `Index` and ignoring the application's definition of the `Index`
|
||||
when an `IndexNameConflictException` is thrown. For a `IndexNameConflictException`, while the names of the conflicting
|
||||
indexes are the same, the definitions could be different. This situation could have implications for OQL queries
|
||||
specific to the application, where you would presume the indexes were defined specifically with the application
|
||||
data access patterns and queries in mind. However, if like-named indexes differ in definition, this might not be
|
||||
the case. Consequently, you should verify your `Index` names.
|
||||
|
||||
NOTE: {sdg-acronym} makes a best effort to inform the user when the `Index` being ignored is significantly different
|
||||
in its definition from the existing `Index`. However, in order for {sdg-acronym} to accomplish this, it must be able to
|
||||
find the existing `Index`, which is looked up by using the {data-store-name} API (the only means available).
|
||||
|
||||
|
||||
=== `Override` Behavior
|
||||
|
||||
When an `IndexExistsException` is thrown and `override` is set to `true` (or `<gfe:index override="true">`),
|
||||
the `Index` is effectively renamed. Remember, `IndexExistsExceptions` are thrown when multiple indexes exist that
|
||||
have the same definition but different names.
|
||||
|
||||
{sdg-name} can only accomplish this by using {data-store-name}'s API, by first removing the existing `Index`
|
||||
and then recreating the `Index` with the new name. It is possible that either the remove or subsequent create invocation
|
||||
could fail. There is no way to execute both actions atomically and rollback this joint operation if either fails.
|
||||
|
||||
However, if it succeeds, then you have the same problem as before with the `ignoreIfExists` option. Any existing OQL
|
||||
query statement using query hints that refer to the old `Index` by name must be changed.
|
||||
|
||||
When an `IndexNameConflictException` is thrown and `override` is set to `true` (or `<gfe:index override="true">`),
|
||||
the existing `Index` can potentially be re-defined. We say "`potentially`" because it is possible for the like-named,
|
||||
existing `Index` to have exactly the same definition and name when an `IndexNameConflictException` is thrown.
|
||||
|
||||
If so, {sdg-acronym} is smart and returns the existing `Index` as is, even on `override`. There is no harm
|
||||
in this behavior, since both the name and the definition are exactly the same. Of course, {sdg-acronym} can only
|
||||
accomplish this when {sdg-acronym} is able to find the existing `Index`, which is dependent on {data-store-name}'s APIs.
|
||||
If it cannot be found, nothing happens and a {sdg-acronym} `GemfireIndexException` is thrown that wraps the
|
||||
`IndexNameConflictException`.
|
||||
|
||||
However, when the definition of the existing `Index` is different, {sdg-acronym} attempts to re-create the `Index`
|
||||
by using the `Index` definition specified in the `index` bean definition. Make sure this is what you want and make sure
|
||||
the `index` bean definition matches your expectations and application requirements.
|
||||
|
||||
=== How Does `IndexNameConflictExceptions` Actually Happen?
|
||||
|
||||
It is probably not all that uncommon for `IndexExistsExceptions` to be thrown, especially when multiple configuration
|
||||
sources are used to configure {data-store-name} ({sdg-name}, {data-store-name} Cluster Config, {data-store-name} native
|
||||
`cache.xml`, the API, and so on). You should definitely prefer one configuration method and stick with it.
|
||||
|
||||
However, when does an `IndexNameConflictException` get thrown?
|
||||
|
||||
One particular case is an `Index` defined on a `PARTITION` Region (PR). When an `Index` is defined on a `PARTITION` Region
|
||||
(for example, `X`), {data-store-name} distributes the `Index` definition (and name) to other peer members
|
||||
in the cluster that also host the same `PARTITION` Region (that is, "X"). The distribution of this `Index` definition
|
||||
to, and subsequent creation of, this `Index` by peer members is on a need-to-know basis (that is, by peer member hosting
|
||||
the same PR) is performed asynchronously.
|
||||
|
||||
During this window of time, it is possible that these pending PR `Indexes` cannot be identified by {data-store-name} --
|
||||
such as with a call to {x-data-store-javadoc}/org/apache/geode/cache/query/QueryService.html#getIndexes[`QueryService.getIndexes()`]
|
||||
with {x-data-store-javadoc}/org/apache/geode/cache/query/QueryService.html#getIndexes-org.apache.geode.cache.Region[`QueryService.getIndexes(:Region)`],
|
||||
or even with {x-data-store-javadoc}/org/apache/geode/cache/query/QueryService.html#getIndex-org.apache.geode.cache.Region-java.lang.String[`QueryService.getIndex(:Region, indexName:String)`].
|
||||
|
||||
As a result, the only way for {sdg-acronym} or other {data-store-name} cache client applications (not involving Spring)
|
||||
to know for sure is to attempt to create the `Index`. If it fails with either an `IndexNameConflictException` or even
|
||||
an `IndexExistsException`, the application knows there is a problem. This is because the `QueryService` `Index` creation
|
||||
waits on pending `Index` definitions, whereas the other {data-store-name} API calls do not.
|
||||
|
||||
In any case, {sdg-acronym} makes a best effort and attempts to inform you what has happened or is happening and tell you
|
||||
the corrective action. Given that all {data-store-name} `QueryService.createIndex(..)` methods are synchronous,
|
||||
blocking operations, the state of {data-store-name} should be consistent and accessible after either of these index-type
|
||||
exceptions are thrown. Consequently, {sdg-acronym} can inspect the state of the system and act accordingly,
|
||||
based on your configuration.
|
||||
|
||||
In all other cases, {sdg-acronym} embraces a fail-fast strategy.
|
||||
@@ -1,30 +0,0 @@
|
||||
[[ref-introduction]]
|
||||
= Document Structure
|
||||
|
||||
The following chapters explain the core functionality offered by {sdg-name}:
|
||||
|
||||
* <<bootstrap>> describes the configuration support provided for configuring, initializing, and accessing
|
||||
{data-store-name} Caches, Regions, and related distributed system components.
|
||||
|
||||
* <<apis>> explains the integration between the {data-store-name} APIs and the various data access features
|
||||
available in Spring, such as template-based data access, exception translation, transaction management, and caching.
|
||||
|
||||
* <<serialization>> describes enhancements to {data-store-name}'s serialization and deserialization of managed objects.
|
||||
|
||||
* <<mapping>> describes persistence mapping for POJOs stored in {data-store-name} using Spring Data.
|
||||
|
||||
* <<gemfire-repositories>> describes how to create and use Spring Data Repositories to access data
|
||||
stored in {data-store-name} by using basic CRUD and simple query operations.
|
||||
|
||||
* <<function-annotations>> describes how to create and use {data-store-name} Functions by using annotations
|
||||
to perform distributed computations where the data lives.
|
||||
|
||||
* <<apis:continuous-query>> describes how to use {data-store-name}'s Continuous Query (CQ) functionality
|
||||
to process a stream of events based on interest that is defined and registered with {data-store-name}'s
|
||||
OQL (Object Query Language).
|
||||
|
||||
* <<gemfire-bootstrap>> describes how to configure and bootstrap a Spring `ApplicationContext`
|
||||
running in an {data-store-name} server using `Gfsh`.
|
||||
|
||||
* <<samples>> describes the examples provided with the distribution to illustrate the various features
|
||||
available in {sdg-name}.
|
||||
@@ -1,342 +0,0 @@
|
||||
[[bootstrap:lucene]]
|
||||
= Apache Lucene Integration
|
||||
|
||||
{x-data-store-website}[{data-store-name}] integrates with https://lucene.apache.org/[Apache Lucene] to let you
|
||||
index and search on data stored in {data-store-name} by using Lucene queries. Search-based queries also include
|
||||
the ability to page through query results.
|
||||
|
||||
Additionally, {sdg-name} adds support for query projections based on the Spring Data Commons projection infrastructure.
|
||||
This feature lets the query results be projected into first-class application domain types as needed by the application.
|
||||
|
||||
A Lucene `Index` must be created before any Lucene search-based query can be run. A `LuceneIndex`
|
||||
can be created in Spring (Data for {data-store-name}) XML config as follows:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<gfe:lucene-index id="IndexOne" fields="fieldOne, fieldTwo" region-path="/Example"/>
|
||||
----
|
||||
|
||||
Additionally, Apache Lucene allows the specification of
|
||||
https://lucene.apache.org/core/6_5_0/core/org/apache/lucene/analysis/Analyzer.html[analyzers]
|
||||
per field and can be configured as shown in the following example:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<gfe:lucene-index id="IndexTwo" lucene-service-ref="luceneService" region-path="/AnotherExample">
|
||||
<gfe:field-analyzers>
|
||||
<map>
|
||||
<entry key="fieldOne">
|
||||
<bean class="example.AnalyzerOne"/>
|
||||
</entry>
|
||||
<entry key="fieldTwo">
|
||||
<bean class="example.AnalyzerTwo"/>
|
||||
</entry>
|
||||
</map>
|
||||
</gfe:field-analyzers>
|
||||
</gfe:lucene-index>
|
||||
----
|
||||
|
||||
The `Map` can be specified as a top-level bean definition and referenced by using the `ref` attribute
|
||||
in the nested `<gfe:field-analyzers>` element, as follows:
|
||||
`<gfe-field-analyzers ref="refToTopLevelMapBeanDefinition"/>`.
|
||||
|
||||
{sdg-name}'s `LuceneIndexFactoryBean` API and {sdg-acronym}'s XML namespace also lets a
|
||||
{x-data-store-javadoc}/org/apache/geode/cache/lucene/LuceneSerializer.html[`org.apache.geode.cache.lucene.LuceneSerializer`]
|
||||
be specified when you create the `LuceneIndex`. The `LuceneSerializer` lets you configure the way objects are converted
|
||||
to Lucene documents for the index when the object is indexed.
|
||||
|
||||
The following example shows how to add an `LuceneSerializer` to the `LuceneIndex`:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<bean id="MyLuceneSerializer" class="example.CustomLuceneSerializer"/>
|
||||
|
||||
<gfe:lucene-index id="IndexThree" lucene-service-ref="luceneService" region-path="/YetAnotherExample">
|
||||
<gfe:lucene-serializer ref="MyLuceneSerializer">
|
||||
</gfe:lucene-index>
|
||||
----
|
||||
|
||||
You can specify the `LuceneSerializer` as an anonymous, nested bean definition as well, as follows:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<gfe:lucene-index id="IndexThree" lucene-service-ref="luceneService" region-path="/YetAnotherExample">
|
||||
<gfe:lucene-serializer>
|
||||
<bean class="example.CustomLuceneSerializer"/>
|
||||
</gfe:lucene-serializer>
|
||||
</gfe:lucene-index>
|
||||
----
|
||||
|
||||
Alternatively, you can declare or define a `LuceneIndex` in Spring Java config, inside a `@Configuration` class,
|
||||
as the following example shows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Bean(name = "Books")
|
||||
@DependsOn("bookTitleIndex")
|
||||
PartitionedRegionFactoryBean<Long, Book> booksRegion(GemFireCache gemfireCache) {
|
||||
|
||||
PartitionedRegionFactoryBean<Long, Book> peopleRegion =
|
||||
new PartitionedRegionFactoryBean<>();
|
||||
|
||||
peopleRegion.setCache(gemfireCache);
|
||||
peopleRegion.setClose(false);
|
||||
peopleRegion.setPersistent(false);
|
||||
|
||||
return peopleRegion;
|
||||
}
|
||||
|
||||
@Bean
|
||||
LuceneIndexFactoryBean bookTitleIndex(GemFireCache gemFireCache,
|
||||
LuceneSerializer luceneSerializer) {
|
||||
|
||||
LuceneIndexFactoryBean luceneIndex = new LuceneIndexFactoryBean();
|
||||
|
||||
luceneIndex.setCache(gemFireCache);
|
||||
luceneIndex.setFields("title");
|
||||
luceneIndex.setLuceneSerializer(luceneSerializer);
|
||||
luceneIndex.setRegionPath("/Books");
|
||||
|
||||
return luceneIndex;
|
||||
}
|
||||
|
||||
@Bean
|
||||
CustomLuceneSerializer myLuceneSerialier() {
|
||||
return new CustomeLuceneSerializer();
|
||||
}
|
||||
----
|
||||
|
||||
There are a few limitations of {data-store-name}'s, Apache Lucene integration and support.
|
||||
|
||||
First, a `LuceneIndex` can only be created on a {data-store-name} `PARTITION` Region.
|
||||
|
||||
Second, all `LuceneIndexes` must be created before the Region to which the `LuceneIndex` applies.
|
||||
|
||||
NOTE: To help ensure that all declared `LuceneIndexes` defined in a Spring container are created before the Regions
|
||||
on which they apply, {sdg-acronym} includes the `org.springframework.data.gemfire.config.support.LuceneIndexRegionBeanFactoryPostProcessor`.
|
||||
You may register this Spring {spring-framework-javadoc}/org/springframework/beans/factory/config/BeanFactoryPostProcessor.html[`BeanFactoryPostProcessor`]
|
||||
in XML config by using `<bean class="org.springframework.data.gemfire.config.support.LuceneIndexRegionBeanFactoryPostProcessor"/>`.
|
||||
The `o.s.d.g.config.support.LuceneIndexRegionBeanFactoryPostProcessor` may only be used when using {sdg-acronym} XML config.
|
||||
More details about Spring's `BeanFactoryPostProcessors` can be found {spring-framework-docs}/core.html#beans-factory-extension-factory-postprocessors[here].
|
||||
|
||||
It is possible that these {data-store-name} restrictions will not apply in a future release which is why
|
||||
the {sdg-acronym} `LuceneIndexFactoryBean` API takes a reference to the Region directly as well,
|
||||
rather than just the Region path.
|
||||
|
||||
This is more ideal when you want to define a `LuceneIndex` on an existing Region with data at a later point
|
||||
during the application's lifecycle and as requirements demand. Where possible, {sdg-acronym} strives to adhere to
|
||||
strongly-typed objects. However, for the time being, you must use the `regionPath` property to specify the Region
|
||||
to which the `LuceneIndex` is applied.
|
||||
|
||||
NOTE: Additionally, in the preceding example, note the presence of Spring's `@DependsOn` annotation
|
||||
on the `Books` Region bean definition. This creates a dependency from the `Books` Region bean to the `bookTitleIndex`
|
||||
`LuceneIndex` bean definition, ensuring that the `LuceneIndex` is created before the Region on which it applies.
|
||||
|
||||
Now that once we have a `LuceneIndex`, we can perform Lucene-based data access operations, such as queries.
|
||||
|
||||
== Lucene Template Data Accessors
|
||||
|
||||
{sdg-name} provides two primary templates for Lucene data access operations, depending on how low of a level
|
||||
your application is prepared to deal with.
|
||||
|
||||
The `LuceneOperations` interface defines query operations by using {data-store-name}
|
||||
{x-data-store-javadoc}/org/apache/geode/cache/lucene/package-summary.html[Lucene types],
|
||||
which are defined in the following interface definition:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
public interface LuceneOperations {
|
||||
|
||||
<K, V> List<LuceneResultStruct<K, V>> query(String query, String defaultField [, int resultLimit]
|
||||
, String... projectionFields);
|
||||
|
||||
<K, V> PageableLuceneQueryResults<K, V> query(String query, String defaultField,
|
||||
int resultLimit, int pageSize, String... projectionFields);
|
||||
|
||||
<K, V> List<LuceneResultStruct<K, V>> query(LuceneQueryProvider queryProvider [, int resultLimit]
|
||||
, String... projectionFields);
|
||||
|
||||
<K, V> PageableLuceneQueryResults<K, V> query(LuceneQueryProvider queryProvider,
|
||||
int resultLimit, int pageSize, String... projectionFields);
|
||||
|
||||
<K> Collection<K> queryForKeys(String query, String defaultField [, int resultLimit]);
|
||||
|
||||
<K> Collection<K> queryForKeys(LuceneQueryProvider queryProvider [, int resultLimit]);
|
||||
|
||||
<V> Collection<V> queryForValues(String query, String defaultField [, int resultLimit]);
|
||||
|
||||
<V> Collection<V> queryForValues(LuceneQueryProvider queryProvider [, int resultLimit]);
|
||||
}
|
||||
----
|
||||
|
||||
NOTE: The `[, int resultLimit]` indicates that the `resultLimit` parameter is optional.
|
||||
|
||||
The operations in the `LuceneOperations` interface match the operations provided by the {data-store-name}'s
|
||||
{x-data-store-javadoc}/org/apache/geode/cache/lucene/LuceneQuery.html[LuceneQuery] interface.
|
||||
However, {sdg-acronym} has the added value of translating proprietary {data-store-name} or Apache Lucene `Exceptions`
|
||||
into Spring's highly consistent and expressive DAO
|
||||
https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#dao-exceptions[exception hierarchy],
|
||||
particularly as many modern data access operations involve more than one store or repository.
|
||||
|
||||
Additionally, {sdg-acronym}'s `LuceneOperations` interface can shield your application from interface-breaking changes
|
||||
introduced by the underlying {data-store-name} or Apache Lucene APIs when they occur.
|
||||
|
||||
However, it would be sad to offer a Lucene Data Access Object (DAO) that only uses {data-store-name} and Apache Lucene
|
||||
data types (such as {data-store-name}'s `LuceneResultStruct`). Therefore, {sdg-acronym} gives you the
|
||||
`ProjectingLuceneOperations` interface to remedy these important application concerns. The following listing shows
|
||||
the `ProjectingLuceneOperations` interface definition:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
public interface ProjectingLuceneOperations {
|
||||
|
||||
<T> List<T> query(String query, String defaultField [, int resultLimit], Class<T> projectionType);
|
||||
|
||||
<T> Page<T> query(String query, String defaultField, int resultLimit, int pageSize, Class<T> projectionType);
|
||||
|
||||
<T> List<T> query(LuceneQueryProvider queryProvider [, int resultLimit], Class<T> projectionType);
|
||||
|
||||
<T> Page<T> query(LuceneQueryProvider queryProvider, int resultLimit, int pageSize, Class<T> projectionType);
|
||||
}
|
||||
----
|
||||
|
||||
The `ProjectingLuceneOperations` interface primarily uses application domain object types that let you work with
|
||||
your application data. The `query` method variants accept a projection type, and the template applies the query results
|
||||
to instances of the given projection type by using the Spring Data Commons Projection infrastructure.
|
||||
|
||||
Additionally, the template wraps the paged Lucene query results in an instance of the Spring Data Commons
|
||||
`Page` abstraction. The same projection logic can still be applied to the results in the page and are lazily projected
|
||||
as each page in the collection is accessed.
|
||||
|
||||
By way of example, suppose you have a class representing a `Person`, as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
class Person {
|
||||
|
||||
Gender gender;
|
||||
|
||||
LocalDate birthDate;
|
||||
|
||||
String firstName;
|
||||
String lastName;
|
||||
|
||||
...
|
||||
|
||||
String getName() {
|
||||
return String.format("%1$s %2$s", getFirstName(), getLastName());
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Additionally, you might have a single interface to represent people as `Customers`, depending on your application view,
|
||||
as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
interface Customer {
|
||||
|
||||
String getName()
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
If I define the following `LuceneIndex`...
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
LuceneIndexFactoryBean personLastNameIndex(GemFireCache gemfireCache) {
|
||||
|
||||
LuceneIndexFactoryBean personLastNameIndex =
|
||||
new LuceneIndexFactoryBean();
|
||||
|
||||
personLastNameIndex.setCache(gemfireCache);
|
||||
personLastNameIndex.setFields("lastName");
|
||||
personLastNameIndex.setRegionPath("/People");
|
||||
|
||||
return personLastNameIndex;
|
||||
}
|
||||
----
|
||||
|
||||
Then you could query for people as `Person` objects, as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
List<Person> people = luceneTemplate.query("lastName: D*", "lastName", Person.class);
|
||||
----
|
||||
|
||||
Alternatively, you could query for a `Page` of type `Customer`, as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
Page<Customer> customers = luceneTemplate.query("lastName: D*", "lastName", 100, 20, Customer.class);
|
||||
----
|
||||
|
||||
The `Page` can then be used to fetch individual pages of the results, as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
List<Customer> firstPage = customers.getContent();
|
||||
----
|
||||
|
||||
Conveniently, the Spring Data Commons `Page` interface also implements `java.lang.Iterable<T>`, making it easy
|
||||
to iterate over the contents.
|
||||
|
||||
The only restriction to the Spring Data Commons Projection infrastructure is that the projection type must be
|
||||
an interface. However, it is possible to extend the provided SDC Projection infrastructure and provide a custom
|
||||
https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/projection/ProjectionFactory.html[`ProjectionFactory`]
|
||||
that uses https://github.com/cglib/cglib[CGLIB] to generate proxy classes as the projected entity.
|
||||
|
||||
You can use `setProjectionFactory(:ProjectionFactory)` to set a custom `ProjectionFactory` on a Lucene template.
|
||||
|
||||
== Annotation Configuration Support
|
||||
|
||||
Finally, {sdg-name} provides annotation configuration support for `LuceneIndexes`.
|
||||
|
||||
Eventually, the {sdg-acronym} Lucene support will finds its way into the Repository infrastructure extension for
|
||||
{data-store-name} so that Lucene queries can be expressed as methods on an application `Repository` interface,
|
||||
in much the same way as the <<gemfire-repositories.queries.executing,OQL support>> works today.
|
||||
|
||||
However, in the meantime, if you want to conveniently express `LuceneIndexes`, you can do so directly on
|
||||
your application domain objects, as the following example shows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@PartitionRegion("People")
|
||||
class Person {
|
||||
|
||||
Gender gender;
|
||||
|
||||
@Index
|
||||
LocalDate birthDate;
|
||||
|
||||
String firstName;
|
||||
|
||||
@LuceneIndex;
|
||||
String lastName;
|
||||
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
To enable this feature, you must use {sdg-acronym}'s annotation configuration support specifically with the
|
||||
`@EnableEntityDefineRegions` and `@EnableIndexing` annotations, as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@PeerCacheApplication
|
||||
@EnableEntityDefinedRegions
|
||||
@EnableIndexing
|
||||
class ApplicationConfiguration {
|
||||
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
NOTE: `LuceneIndexes` can only be created on {data-store-name} servers since `LuceneIndexes` only apply
|
||||
to `PARTITION` Regions.
|
||||
|
||||
Given our earlier definition of the `Person` class, the {sdg-acronym} annotation configuration support finds
|
||||
the `Person` entity class definition and determines that people are stored in a `PARTITION` Region called "`People`"
|
||||
and that the `Person` has an OQL `Index` on `birthDate` along with a `LuceneIndex` on `lastName`.
|
||||
@@ -1,443 +0,0 @@
|
||||
[[mapping]]
|
||||
= POJO Mapping
|
||||
|
||||
This section covers:
|
||||
|
||||
* <<mapping.entities>>
|
||||
* <<mapping.repositories>>
|
||||
* <<mapping.pdx-serializer>>
|
||||
|
||||
include::../{spring-data-commons-include}/object-mapping.adoc[leveloffset=+1]
|
||||
|
||||
[[mapping.entities]]
|
||||
== Entity Mapping
|
||||
|
||||
{sdg-name} provides support to map entities that are stored in a Region. The mapping metadata is defined by
|
||||
using annotations on application domain classes, as the following example shows:
|
||||
|
||||
.Mapping a domain class to a {data-store-name} Region
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Region("People")
|
||||
public class Person {
|
||||
|
||||
@Id Long id;
|
||||
|
||||
String firstname;
|
||||
String lastname;
|
||||
|
||||
@PersistenceConstructor
|
||||
public Person(String firstname, String lastname) {
|
||||
// …
|
||||
}
|
||||
|
||||
…
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The `@Region` annotation can be used to customize the Region in which an instance of the `Person` class is stored.
|
||||
The `@Id` annotation can be used to annotate the property that should be used as the cache Region key, identifying
|
||||
the Region entry. The `@PersistenceConstructor` annotation helps to disambiguate multiple potentially available
|
||||
constructors, taking parameters and explicitly marking the constructor annotated as the constructor to be used to
|
||||
construct entities. In an application domain class with no or only a single constructor, you can omit the annotation.
|
||||
|
||||
In addition to storing entities in top-level Regions, entities can be stored in Sub-Regions as well,
|
||||
as the following example shows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Region("/Users/Admin")
|
||||
public class Admin extends User {
|
||||
…
|
||||
}
|
||||
|
||||
@Region("/Users/Guest")
|
||||
public class Guest extends User {
|
||||
…
|
||||
}
|
||||
----
|
||||
|
||||
Be sure to use the full path of the {data-store-name} Region, as defined with the {sdg-name} XML namespace
|
||||
by using the `id` or `name` attributes of the `<*-region>` element.
|
||||
|
||||
[[mapping.entities.region]]
|
||||
=== Entity Mapping by Region Type
|
||||
|
||||
In addition to the `@Region` annotation, {sdg-name} also recognizes type-specific Region mapping annotations:
|
||||
`@ClientRegion`, `@LocalRegion`, `@PartitionRegion`, and `@ReplicateRegion`.
|
||||
|
||||
Functionally, these annotations are treated exactly the same as the generic `@Region` annotation in the {sdg-acronym}
|
||||
mapping infrastructure. However, these additional mapping annotations are useful in {sdg-name}'s
|
||||
annotation configuration model. When combined with the `@EnableEntityDefinedRegions` configuration annotation
|
||||
on a Spring `@Configuration` annotated class, it is possible to generate Regions in the local cache, whether
|
||||
the application is a client or peer.
|
||||
|
||||
These annotations let you be more specific about what type of Region your application entity class should be mapped to
|
||||
and also has an impact on the data management policies of the Region (for example, partition -- also known as sharding
|
||||
-- versus replicating data).
|
||||
|
||||
Using these type-specific Region mapping annotations with the {sdg-acronym} annotation configuration model saves you
|
||||
from having to explicitly define these Regions in configuration.
|
||||
|
||||
[[mapping.repositories]]
|
||||
== Repository Mapping
|
||||
|
||||
As an alternative to specifying the Region in which the entity is stored by using the `@Region` annotation
|
||||
on the entity class, you can also specify the `@Region` annotation on the entity's `Repository` interface.
|
||||
See <<gemfire-repositories>> for more details.
|
||||
|
||||
However, suppose you want to store a `Person` record in multiple {data-store-name} Regions (for example, `People`
|
||||
and `Customers`). Then you can define your corresponding `Repository` interface extensions as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Region("People")
|
||||
public interface PersonRepository extends GemfireRepository<Person, String> {
|
||||
…
|
||||
}
|
||||
|
||||
@Region("Customers")
|
||||
public interface CustomerRepository extends GemfireRepository<Person, String> {
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
Then, using each Repository individually, you can store the entity in multiple {data-store-name} Regions,
|
||||
as the following example shows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Service
|
||||
class CustomerService {
|
||||
|
||||
CustomerRepository customerRepo;
|
||||
|
||||
PersonRepository personRepo;
|
||||
|
||||
Customer update(Customer customer) {
|
||||
customerRepo.save(customer);
|
||||
personRepo.save(customer);
|
||||
return customer;
|
||||
}
|
||||
----
|
||||
|
||||
You can even wrap the `update` service method in a Spring managed transaction, either as a local cache transaction
|
||||
or a global transaction.
|
||||
|
||||
[[mapping.pdx-serializer]]
|
||||
== MappingPdxSerializer
|
||||
|
||||
{sdg-name} provides a custom {x-data-store-javadoc}/org/apache/geode/pdx/PdxSerializer.html[`PdxSerializer`]
|
||||
implementation, called `MappingPdxSerializer`, that uses Spring Data mapping metadata to customize entity serialization.
|
||||
|
||||
The serializer also lets you customize entity instantiation by using the Spring Data `EntityInstantiator` abstraction.
|
||||
By default, the serializer use the `ReflectionEntityInstantiator`, which uses the persistence constructor of
|
||||
the mapped entity. The persistence constructor is either the default constructor, a singly declared constructor,
|
||||
or a constructor explicitly annotated with `@PersistenceConstructor`.
|
||||
|
||||
To provide arguments for constructor parameters, the serializer reads fields with the named constructor parameter,
|
||||
explicitly identified by using Spring's `@Value` annotation, from the supplied
|
||||
{x-data-store-javadoc}/org/apache/geode/pdx/PdxReader.html[`PdxReader`],
|
||||
as shown in the following example:
|
||||
|
||||
.Using `@Value` on entity constructor parameters
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public class Person {
|
||||
|
||||
public Person(@Value("#root.thing") String firstName, @Value("bean") String lastName) {
|
||||
…
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
An entity class annotated in this way has the "`thing`" field read from the `PdxReader` and passed as the argument value
|
||||
for the constructor parameter, `firstname`. The value for `lastName` is a Spring bean with the name "`bean`".
|
||||
|
||||
In addition to the custom instantiation logic and strategy provided by `EntityInstantiators`,
|
||||
the `MappingPdxSerializer` also provides capabilities well beyond {data-store-name}'s own
|
||||
{x-data-store-javadoc}/org/apache/geode/pdx/ReflectionBasedAutoSerializer.html[`ReflectionBasedAutoSerializer`].
|
||||
|
||||
While {data-store-name}'s `ReflectionBasedAutoSerializer` conveniently uses Java Reflection to populate entities
|
||||
and uses regular expressions to identify types that should be handled (serialized and deserialized) by the serializer,
|
||||
it cannot, unlike `MappingPdxSerializer`, perform the following:
|
||||
|
||||
* Register custom `PdxSerializer` objects per entity field or property names and types.
|
||||
* Conveniently identifies ID properties.
|
||||
* Automatically handles read-only properties.
|
||||
* Automatically handles transient properties.
|
||||
* Allows more robust type filtering in a `null` and type-safe manner (for example, not limited to
|
||||
only expressing types with regex).
|
||||
|
||||
We now explore each feature of the `MappingPdxSerializer` in a bit more detail.
|
||||
|
||||
[[mapping.pdx-serializer.custom-serialization]]
|
||||
=== Custom PdxSerializer Registration
|
||||
|
||||
The `MappingPdxSerializer` gives you the ability to register custom `PdxSerializers` based on an entity's field
|
||||
or property names and types.
|
||||
|
||||
For example, suppose you have defined an entity type modeling a `User` as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
package example.app.security.auth.model;
|
||||
|
||||
public class User {
|
||||
|
||||
private String name;
|
||||
|
||||
private Password password;
|
||||
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
While the user's name probably does not require any special logic to serialize the value, serializing the password
|
||||
on the other hand might require additional logic to handle the sensitive nature of the field or property.
|
||||
|
||||
Perhaps you want to protect the password when sending the value over the network, between a client and a server,
|
||||
beyond TLS alone, and you only want to store the salted hash. When using the `MappingPdxSerializer`, you can register
|
||||
a custom `PdxSerializer` to handle the user's password, as follows:
|
||||
|
||||
.Registering custom `PdxSerializers` by POJO field/property type
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
Map<?, PdxSerializer> customPdxSerializers = new HashMap<>();
|
||||
|
||||
customPdxSerializers.put(Password.class, new SaltedHashPasswordPdxSerializer());
|
||||
|
||||
mappingPdxSerializer.setCustomPdxSerializers(customPdxSerializers);
|
||||
----
|
||||
====
|
||||
|
||||
After registering the application-defined `SaltedHashPasswordPdxSerializer` instance with the `Password`
|
||||
application domain model type, the `MappingPdxSerializer` will then consult the custom `PdxSerializer`
|
||||
to serialize and deserialize all `Password` objects regardless of the containing object (for example, `User`).
|
||||
|
||||
However, suppose you want to customize the serialization of `Passwords` only on `User` objects.
|
||||
To do so, you can register the custom `PdxSerializer` for the `User` type by specifying the fully qualified name
|
||||
of the `Class's` field or property, as the following example shows:
|
||||
|
||||
.Registering custom `PdxSerializers` by POJO field/property name
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
Map<?, PdxSerializer> customPdxSerializers = new HashMap<>();
|
||||
|
||||
customPdxSerializers.put("example.app.security.auth.model.User.password", new SaltedHashPasswordPdxSerializer());
|
||||
|
||||
mappingPdxSerializer.setCustomPdxSerializers(customPdxSerializers);
|
||||
----
|
||||
====
|
||||
|
||||
Notice the use of the fully-qualified field or property name (that is `example.app.security.auth.model.User.password`)
|
||||
as the custom `PdxSerializer` registration key.
|
||||
|
||||
NOTE: You could construct the registration key by using a more logical code snippet, such as the following:
|
||||
`User.class.getName().concat(".password");`. We recommended this over the example shown earlier.
|
||||
The preceding example tried to be as explicit as possible about the semantics of registration.
|
||||
|
||||
[[mapping.pdx-serializer.id-properties]]
|
||||
=== Mapping ID Properties
|
||||
|
||||
Like {data-store-name}'s `ReflectionBasedAutoSerializer`, {sdg-acronym}'s `MappingPdxSerializer` is also able to
|
||||
determine the identifier of the entity. However, `MappingPdxSerializer` does so by using Spring Data's mapping metadata,
|
||||
specifically by finding the entity property designated as the identifier using Spring Data's
|
||||
{spring-data-commons-javadoc}/org/springframework/data/annotation/Id.html[`@Id`] annotation.
|
||||
Alternatively, any field or property named "`id`", not explicitly annotated with `@Id`, is also designated as
|
||||
the entity's identifier.
|
||||
|
||||
For example:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
class Customer {
|
||||
|
||||
@Id
|
||||
Long id;
|
||||
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
In this case, the `Customer` `id` field is marked as the identifier field in the PDX type metadata by using
|
||||
{x-data-store-javadoc}/org/apache/geode/pdx/PdxWriter.html#markIdentityField-java.lang.String-[`PdxWriter.markIdentifierField(:String)`]
|
||||
when the `PdxSerializer.toData(..)` method is called during serialization.
|
||||
|
||||
[[mapping.pdx-serializer.read-only-properties]]
|
||||
=== Mapping Read-only Properties
|
||||
|
||||
What happens when your entity defines a read-only property?
|
||||
|
||||
First, it is important to understand what a "`read-only`" property is. If you define a POJO by following the
|
||||
https://www.oracle.com/technetwork/java/javase/documentation/spec-136004.html[JavaBeans] specification (as Spring does),
|
||||
you might define a POJO with a read-only property, as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
package example;
|
||||
|
||||
class ApplicationDomainType {
|
||||
|
||||
private AnotherType readOnly;
|
||||
|
||||
public AnotherType getReadOnly() [
|
||||
this.readOnly;
|
||||
}
|
||||
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
The `readOnly` property is read-only because it does not provide a setter method. It only has a getter method.
|
||||
In this case, the `readOnly` property (not to be confused with the `readOnly` `DomainType` field)
|
||||
is considered read-only.
|
||||
|
||||
As a result, the `MappingPdxSerializer` will not try to set a value for this property when populating an instance of
|
||||
`ApplicationDomainType` in the `PdxSerializer.fromData(:Class<ApplicationDomainType>, :PdxReader)` method
|
||||
during deserialization, particularly if a value is present in the PDX serialized bytes.
|
||||
|
||||
This is useful in situations where you might be returning a view or projection of some entity type and you only want
|
||||
to set state that is writable. Perhaps the view or projection of the entity is based on authorization or some other
|
||||
criteria. The point is, you can leverage this feature as is appropriate for your application's use cases
|
||||
and requirements. If you want the field or property to always be written, simply define a setter method.
|
||||
|
||||
[[mapping.pdx-serializer.transient-properties]]
|
||||
=== Mapping Transient Properties
|
||||
|
||||
Likewise, what happens when your entity defines `transient` properties?
|
||||
|
||||
You would expect the `transient` fields or properties of your entity not to be serialized to PDX when serializing
|
||||
the entity. That is exactly what happens, unlike {data-store-name}'s own `ReflectionBasedAutoSerializer`,
|
||||
which serializes everything accessible from the object through Java Reflection.
|
||||
|
||||
The `MappingPdxSerializer` will not serialize any fields or properties that are qualified as being transient, either
|
||||
by using Java's own `transient` keyword (in the case of class instance fields) or by using the
|
||||
{spring-data-commons-javadoc}/org/springframework/data/annotation/Transient.html[`@Transient`]
|
||||
Spring Data annotation on either fields or properties.
|
||||
|
||||
For example, you might define an entity with transient fields and properties as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
package example;
|
||||
|
||||
class Process {
|
||||
|
||||
private transient int id;
|
||||
|
||||
private File workingDirectory;
|
||||
|
||||
private String name;
|
||||
|
||||
private Type type;
|
||||
|
||||
@Transient
|
||||
public String getHostname() {
|
||||
...
|
||||
}
|
||||
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
Neither the `Process` `id` field nor the readable `hostname` property are written to PDX.
|
||||
|
||||
[[mapping.pdx-serializer.type-filtering]]
|
||||
=== Filtering by Class Type
|
||||
|
||||
Similar to {data-store-name}'s `ReflectionBasedAutoSerializer`, {sdg-acronym}'s `MappingPdxSerializer` lets you filter
|
||||
the types of objects that are serialized and deserialized.
|
||||
|
||||
However, unlike {data-store-name}'s `ReflectionBasedAutoSerializer`, which uses complex regular expressions to express
|
||||
which types the serializer handles, {sdg-acronym}'s `MappingPdxSerializer` uses the much more robust
|
||||
https://docs.oracle.com/javase/8/docs/api/java/util/function/Predicate.html[`java.util.function.Predicate`] interface
|
||||
and API to express type-matching criteria.
|
||||
|
||||
TIP: If you like to use regular expressions, you can implement a `Predicate` using Java's
|
||||
https://docs.oracle.com/javase/8/docs/api/java/util/regex/package-summary.html[regular expression support].
|
||||
|
||||
The nice part about Java's `Predicate` interface is that you can compose `Predicates` by using convenient
|
||||
and appropriate API methods, including:
|
||||
https://docs.oracle.com/javase/8/docs/api/java/util/function/Predicate.html#and-java.util.function.Predicate-[`and(:Predicate)`],
|
||||
https://docs.oracle.com/javase/8/docs/api/java/util/function/Predicate.html#or-java.util.function.Predicate-[`or(:Predicate)`],
|
||||
and https://docs.oracle.com/javase/8/docs/api/java/util/function/Predicate.html#negate--[`negate()`].
|
||||
|
||||
The following example shows the `Predicate` API in action:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
Predicate<Class<?>> customerTypes =
|
||||
type -> Customer.class.getPackage().getName().startsWith(type.getName()); // Include all types in the same package as `Customer`
|
||||
|
||||
Predicate includedTypes = customerTypes
|
||||
.or(type -> User.class.isAssignble(type)); // Additionally, include User sub-types (e.g. Admin, Guest, etc)
|
||||
|
||||
mappingPdxSerializer.setIncludeTypeFilters(includedTypes);
|
||||
|
||||
mappingPdxSerializer.setExcludeTypeFilters(
|
||||
type -> !Reference.class.getPackage(type.getPackage()); // Exclude Reference types
|
||||
----
|
||||
|
||||
NOTE: Any `Class` object passed to your `Predicate` is guaranteed not to be `null`.
|
||||
|
||||
{sdg-acronym}'s `MappingPdxSerializer` includes support for both include and exclude class type filters.
|
||||
|
||||
[[mapping.pdx-serializer.type-filtering.execludes]]
|
||||
==== Exclude Type Filtering
|
||||
|
||||
By default, {sdg-acronym}'s `MappingPdxSerializer` registers pre-defined `Predicates` that filter, or exclude types
|
||||
from the folliowing packages:
|
||||
|
||||
* `java.*`
|
||||
* `com.gemstone.gemfire.*`
|
||||
* `org.apache.geode.*`
|
||||
* `org.springframework.*`
|
||||
|
||||
In addition, the `MappingPdxSerializer` filters `null` objects when calling `PdxSerializer.toData(:Object, :PdxWriter)`
|
||||
and `null` class types when calling `PdxSerializer.fromData(:Class<?>, :PdxReader)` methods.
|
||||
|
||||
It is very easy to add exclusions for other class types, or an entire package of types, by simply defining a `Predicate`
|
||||
and adding it to the `MappingPdxSerializer` as shown earlier.
|
||||
|
||||
The `MappingPdxSerializer.setExcludeTypeFilters(:Predicate<Class<?>>)` method is additive, meaning it composes
|
||||
your application-defined type filters with the existing, pre-defined type filter `Predicates` indicated above
|
||||
using the `Predicate.and(:Predicate<Class<?>>)` method.
|
||||
|
||||
However, what if you want to include a class type (for example, `java.security Principal`) implicitly excluded by
|
||||
the exclude type filters? See <<mapping.pdx-serializer.type-filtering.includes>>.
|
||||
|
||||
[[mapping.pdx-serializer.type-filtering.includes]]
|
||||
==== Include Type Filtering
|
||||
|
||||
If you want to include a class type explicitly, or override a class type filter that implicitly excludes a class type
|
||||
required by your application (for example, `java.security.Principal`, which is excluded by default with the `java.*`
|
||||
package exclude type filter on `MappingPdxSerializer`), then just define the appropriate `Predicate` and add it to
|
||||
the serializer using `MappingPdxSerializer.setIncludeTypeFilters(:Predicate<Class<?>>)` method, as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
Predicate<Class<?>> principalTypeFilter =
|
||||
type -> java.security.Principal.class.isAssignableFrom(type);
|
||||
|
||||
mappingPdxSerializer.setIncludeTypeFilters(principalTypeFilters);
|
||||
----
|
||||
|
||||
Again, the `MappingPdxSerializer.setIncludeTypeFilters(:Predicate<Class<?>>)` method,
|
||||
like `setExcludeTypeFilters(:Predicate<Class<?>>)`, is additive and therefore composes any passed type filter
|
||||
using `Predicate.or(:Predicate<Class<?>>)`. This means you may call `setIncludeTypeFilters(:Predicate<Class<?>>)`
|
||||
as many time as necessary.
|
||||
|
||||
When include type filters are present, then the `MappingPdxSerializer` makes a decision of whether to de/serialize
|
||||
an instance of a class type when the class type is either not implicitly excluded OR when the class type
|
||||
is explicitly included, whichever returns true. Then, an instance of the class type will be serialized
|
||||
or deserialized appropriately.
|
||||
|
||||
For example, when a type filter of `Predicate<Class<Principal>>` is explicitly registered as shown previously,
|
||||
it cancels out the implicit exclude type filter on `java.*` package types.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,531 +0,0 @@
|
||||
[[gemfire-repositories]]
|
||||
= {sdg-name} Repositories
|
||||
|
||||
{sdg-name} provides support for using the Spring Data Repository abstraction to easily persist entities into
|
||||
{data-store-name} along with executing queries. A general introduction to the Repository programming model
|
||||
is provided https://docs.spring.io/spring-data/data-commons/docs/current/reference/html/#repositories[here].
|
||||
|
||||
[[gemfire-repositories.spring-configuration-xml]]
|
||||
== Spring XML Configuration
|
||||
|
||||
To bootstrap Spring Data Repositories, use the `<repositories/>` element from the {sdg-name} Data namespace,
|
||||
as the following example shows:
|
||||
|
||||
.Bootstrap {sdg-name} Repositories in XML
|
||||
====
|
||||
[source,xml]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:gfe-data="{spring-data-access-schema-namespace}"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
{spring-data-access-schema-namespace} {spring-data-access-schema-location}
|
||||
">
|
||||
|
||||
<gfe-data:repositories base-package="com.example.acme.repository"/>
|
||||
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
The preceding configuration snippet looks for interfaces below the configured base package and creates Repository instances
|
||||
for those interfaces backed by a https://docs.spring.io/spring-data/geode/docs/current/api/org/springframework/data/gemfire/repository/support/SimpleGemfireRepository.html[`SimpleGemFireRepository`].
|
||||
|
||||
IMPORTANT: The bootstrap process fails unless you have your application domain classes correctly mapped
|
||||
to configured Regions.
|
||||
|
||||
[[gemfire-repositories.spring-configuration-java]]
|
||||
== Spring Java-based Configuration
|
||||
|
||||
Alternatively, many developers prefer to use Spring's {spring-framework-docs}/core.html#beans-java[Java-based container configuration].
|
||||
|
||||
Using this approach, you can bootstrap Spring Data Repositories by using the {sdg-acronym} `@EnableGemfireRepositories`
|
||||
annotation, as the following example shows:
|
||||
|
||||
.Bootstrap {sdg-name} Repositories with `@EnableGemfireRepositories`
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@SpringBootApplication
|
||||
@EnableGemfireRepositories(basePackages = "com.example.acme.repository")
|
||||
class SpringDataApplication {
|
||||
...
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Rather than use the `basePackages` attribute, you may prefer to use the type-safe `basePackageClasses` attribute instead.
|
||||
The `basePackageClasses` lets you specify the package that contains all your application Repository classes by
|
||||
specifying only one of your application Repository interface types. Consider creating a special no-op marker class
|
||||
or interface in each package that serves no purpose other than to identify the location of application Repositories
|
||||
referenced by this attribute.
|
||||
|
||||
In addition to the `basePackages and basePackageClasses` attributes, like Spring's
|
||||
{spring-framework-javadoc}/org/springframework/context/annotation/ComponentScan.html[`@ComponentScan`] annotation,
|
||||
the `@EnableGemfireRepositories` annotation provides include and exclude filters, based on Spring's
|
||||
{spring-framework-javadoc}/org/springframework/context/annotation/ComponentScan.Filter.html[`ComponentScan.Filter`] type.
|
||||
You can use the `filterType` attribute to filter by different aspects, such as whether an application Repository type
|
||||
is annotated with a particular annotation or extends a particular class type and so on. See the
|
||||
{spring-framework-javadoc}/org/springframework/context/annotation/FilterType.html[`FilterType` Javadoc]
|
||||
for more details.
|
||||
|
||||
The `@EnableGemfireRepositories` annotation also lets you specify the location of named OQL queries, which reside in
|
||||
a Java `Properties` file, by using the `namedQueriesLocation` attribute. The property name must match the name
|
||||
of a Repository query method and the property value is the OQL query you want executed when the Repository query method
|
||||
is called.
|
||||
|
||||
The `repositoryImplementationPostfix` attribute can be set to an alternate value (defaults to `Impl`) if your
|
||||
application requires one or more {spring-data-commons-docs-html}/#repositories.custom-implementations[custom repository implementations].
|
||||
This feature is commonly used to extend the Spring Data Repository infrastructure to implement a feature not provided by
|
||||
the data store (for example, {sdg-acronym}).
|
||||
|
||||
One example of where custom repository implementations are needed with {data-store-name} is when performing joins.
|
||||
Joins are not supported by {sdg-acronym} Repositories. With a {data-store-name} `PARTITION` Region, the join must be
|
||||
performed on collocated `PARTITION` Regions, since {data-store-name} does not support "`distributed`" joins.
|
||||
In addition, the Equi-Join OQL Query must be performed inside a {data-store-name} Function.
|
||||
See https://gemfire91.docs.pivotal.io/geode/developing/partitioned_regions/join_query_partitioned_regions.html[here]
|
||||
for more details on {data-store-name} _Equi-Join Queries_.
|
||||
|
||||
Many other aspects of the {sdg-acronym}'s Repository infrastructure extension may be customized as well. See the
|
||||
https://docs.spring.io/spring-data/gemfire/docs/current/api/org/springframework/data/gemfire/repository/config/EnableGemfireRepositories.html[`@EnableGemfireRepositories`]
|
||||
Javadoc for more details on all configuration settings.
|
||||
|
||||
[[gemfire-repositories.queries.executing]]
|
||||
== Executing OQL Queries
|
||||
|
||||
{sdg-name} Repositories enable the definition of query methods to easily execute {data-store-name} OQL queries
|
||||
against the Region the managed entity maps to, as the following example shows:
|
||||
|
||||
.Sample Repository
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Region("People")
|
||||
public class Person { … }
|
||||
----
|
||||
|
||||
[source,java]
|
||||
----
|
||||
public interface PersonRepository extends CrudRepository<Person, Long> {
|
||||
|
||||
Person findByEmailAddress(String emailAddress);
|
||||
|
||||
Collection<Person> findByFirstname(String firstname);
|
||||
|
||||
@Query("SELECT * FROM /People p WHERE p.firstname = $1")
|
||||
Collection<Person> findByFirstnameAnnotated(String firstname);
|
||||
|
||||
@Query("SELECT * FROM /People p WHERE p.firstname IN SET $1")
|
||||
Collection<Person> findByFirstnamesAnnotated(Collection<String> firstnames);
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The first query method listed in the preceding example causes the following OQL query to be derived:
|
||||
`SELECT x FROM /People x WHERE x.emailAddress = $1`. The second query method works the same way except
|
||||
it returns all entities found, whereas the first query method expects a single result to be found.
|
||||
|
||||
If the supported keywords are not sufficient to declare and express your OQL query, or the method name becomes too
|
||||
verbose, then you can annotate the query methods with `@Query` as shown on the third and fourth methods.
|
||||
|
||||
The following table gives brief samples of the supported keywords that you can use in query methods:
|
||||
|
||||
[cols="1,2,2", options="header"]
|
||||
.Supported keywords for query methods
|
||||
|===
|
||||
| Keyword
|
||||
| Sample
|
||||
| Logical result
|
||||
|
||||
| `GreaterThan`
|
||||
| `findByAgeGreaterThan(int age)`
|
||||
| `x.age > $1`
|
||||
|
||||
| `GreaterThanEqual`
|
||||
| `findByAgeGreaterThanEqual(int age)`
|
||||
| `x.age >= $1`
|
||||
|
||||
| `LessThan`
|
||||
| `findByAgeLessThan(int age)`
|
||||
| `x.age < $1`
|
||||
|
||||
| `LessThanEqual`
|
||||
| `findByAgeLessThanEqual(int age)`
|
||||
| `x.age <= $1`
|
||||
|
||||
| `IsNotNull`, `NotNull`
|
||||
| `findByFirstnameNotNull()`
|
||||
| `x.firstname =! NULL`
|
||||
|
||||
| `IsNull`, `Null`
|
||||
| `findByFirstnameNull()`
|
||||
| `x.firstname = NULL`
|
||||
|
||||
| `In`
|
||||
| `findByFirstnameIn(Collection<String> x)`
|
||||
| `x.firstname IN SET $1`
|
||||
|
||||
| `NotIn`
|
||||
| `findByFirstnameNotIn(Collection<String> x)`
|
||||
| `x.firstname NOT IN SET $1`
|
||||
|
||||
| `IgnoreCase`
|
||||
| `findByFirstnameIgnoreCase(String firstName)`
|
||||
| `x.firstname.equalsIgnoreCase($1)`
|
||||
|
||||
| (No keyword)
|
||||
| `findByFirstname(String name)`
|
||||
| `x.firstname = $1`
|
||||
|
||||
| `Like`
|
||||
| `findByFirstnameLike(String name)`
|
||||
| `x.firstname LIKE $1`
|
||||
|
||||
| `Not`
|
||||
| `findByFirstnameNot(String name)`
|
||||
| `x.firstname != $1`
|
||||
|
||||
| `IsTrue`, `True`
|
||||
| `findByActiveIsTrue()`
|
||||
| `x.active = true`
|
||||
|
||||
| `IsFalse`, `False`
|
||||
| `findByActiveIsFalse()`
|
||||
| `x.active = false`
|
||||
|===
|
||||
|
||||
[[gemfire-repositories.queries.oql-extensions]]
|
||||
== OQL Query Extensions Using Annotations
|
||||
|
||||
Many query languages, such as {data-store-name}'s OQL (Object Query Language), have extensions that are not directly
|
||||
supported by Spring Data Commons' Repository infrastructure.
|
||||
|
||||
One of Spring Data Commons' Repository infrastructure goals is to function as the lowest common denominator to maintain
|
||||
support for and portability across the widest array of data stores available and in use for application development
|
||||
today. Technically, this means developers can access multiple different data stores supported by Spring Data Commons
|
||||
within their applications by reusing their existing application-specific Repository interfaces -- a convenient
|
||||
and powerful abstraction.
|
||||
|
||||
To support {data-store-name}'s OQL Query language extensions and preserve portability across different data stores,
|
||||
{sdg-name} adds support for OQL Query extensions by using Java annotations. These annotations are ignored by other
|
||||
Spring Data Repository implementations (such as Spring Data JPA or Spring Data Redis) that do not have similar
|
||||
query language features.
|
||||
|
||||
For instance, many data stores most likely do not implement {data-store-name}'s OQL `IMPORT` keyword. Implementing `IMPORT`
|
||||
as an annotation (that is, `@Import`) rather than as part of the query method signature (specifically, the method 'name')
|
||||
does not interfere with the parsing infrastructure when evaluating the query method name to construct another data store
|
||||
language appropriate query.
|
||||
|
||||
Currently, the set of {data-store-name} OQL Query language extensions that are supported by {sdg-name} include:
|
||||
|
||||
[cols="1,2,2,2", options="header"]
|
||||
.Supported {data-store-name} OQL extensions for Repository query methods
|
||||
|===
|
||||
| Keyword
|
||||
| Annotation
|
||||
| Description
|
||||
| Arguments
|
||||
|
||||
| {x-data-store-docs}/developing/query_index/query_index_hints.html#topic_cfb_mxn_jq[HINT]
|
||||
| `@Hint`
|
||||
| OQL query index hints
|
||||
| `String[]` (e.g. @Hint({ "IdIdx", "TxDateIdx" }))
|
||||
|
||||
| {x-data-store-docs}/developing/query_select/the_import_statement.html#concept_2E9F15B2FE9041238B54736103396BF7[IMPORT]
|
||||
| `@Import`
|
||||
| Qualify application-specific types.
|
||||
| `String` (e.g. @Import("org.example.app.domain.Type"))
|
||||
|
||||
| {x-data-store-docs}/developing/query_select/the_select_statement.html#concept_85AE7D6B1E2941ED8BD2A8310A81753E__section_25D7055B33EC47B19B1B70264B39212F[LIMIT]
|
||||
| `@Limit`
|
||||
| Limit the returned query result set.
|
||||
| `Integer` (e.g. @Limit(10); default is Integer.MAX_VALUE)
|
||||
|
||||
| {x-data-store-docs}/developing/query_additional/query_debugging.html#concept_2D557E24AAB24044A3DB36B3124F6748[TRACE]
|
||||
| `@Trace`
|
||||
| Enable OQL query-specific debugging.
|
||||
| NA
|
||||
|===
|
||||
|
||||
As an example, suppose you have a `Customers` application domain class and corresponding {data-store-name} Region
|
||||
along with a `CustomerRepository` and a query method to lookup `Customers` by last name, as follows:
|
||||
|
||||
.Sample Customers Repository
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
package ...;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.gemfire.mapping.annotation.Region;
|
||||
...
|
||||
|
||||
@Region("Customers")
|
||||
public class Customer ... {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
[source,java]
|
||||
----
|
||||
package ...;
|
||||
|
||||
import org.springframework.data.gemfire.repository.GemfireRepository;
|
||||
...
|
||||
|
||||
public interface CustomerRepository extends GemfireRepository<Customer, Long> {
|
||||
|
||||
@Trace
|
||||
@Limit(10)
|
||||
@Hint("LastNameIdx")
|
||||
@Import("org.example.app.domain.Customer")
|
||||
List<Customer> findByLastName(String lastName);
|
||||
|
||||
...
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The preceding example results in the following OQL Query:
|
||||
|
||||
`<TRACE> <HINT 'LastNameIdx'> IMPORT org.example.app.domain.Customer; SELECT * FROM /Customers x WHERE x.lastName = $1 LIMIT 10`
|
||||
|
||||
{sdg-name}'s Repository extension is careful not to create conflicting declarations when the OQL annotation extensions
|
||||
are used in combination with the `@Query` annotation.
|
||||
|
||||
As another example, suppose you have a raw `@Query` annotated query method defined in your `CustomerRepository`,
|
||||
as follows:
|
||||
|
||||
.CustomerRepository
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public interface CustomerRepository extends GemfireRepository<Customer, Long> {
|
||||
|
||||
@Trace
|
||||
@Limit(10)
|
||||
@Hint("CustomerIdx")
|
||||
@Import("org.example.app.domain.Customer")
|
||||
@Query("<TRACE> <HINT 'ReputationIdx'> SELECT DISTINCT * FROM /Customers c WHERE c.reputation > $1 ORDER BY c.reputation DESC LIMIT 5")
|
||||
List<Customer> findDistinctCustomersByReputationGreaterThanOrderByReputationDesc(Integer reputation);
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The preceding query method results in the following OQL query:
|
||||
|
||||
`IMPORT org.example.app.domain.Customer; <TRACE> <HINT 'ReputationIdx'> SELECT DISTINCT * FROM /Customers x
|
||||
WHERE x.reputation > $1 ORDER BY c.reputation DESC LIMIT 5`
|
||||
|
||||
The `@Limit(10)` annotation does not override the `LIMIT` explicitly defined in the raw query.
|
||||
Also, the `@Hint("CustomerIdx")` annotation does not override the `HINT` explicitly defined in the raw query.
|
||||
Finally, the `@Trace` annotation is redundant and has no additional effect.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
The `ReputationIdx` index is probably not the most sensible index, given the number of customers who may possibly have
|
||||
the same value for their reputation, which reduces the effectiveness of the index. Please choose indexes and other
|
||||
optimizations wisely, as an improper or poorly chosen index can have the opposite effect on your performance because
|
||||
of the overhead in maintaining the index. The `ReputationIdx` was used only to serve the purpose of the example.
|
||||
====
|
||||
|
||||
[[gemfire-repositories.queries.post-processing]]
|
||||
== Query Post Processing
|
||||
|
||||
Thanks to using the Spring Data Repository abstraction, the query method convention for defining data store specific
|
||||
queries (e.g. OQL) is easy and convenient. However, it is sometimes desirable to still want to inspect or even possibly
|
||||
modify the query generated from the Repository query method.
|
||||
|
||||
Since 2.0.x, {sdg-name} includes the `o.s.d.gemfire.repository.query.QueryPostProcessor` functional interface.
|
||||
The interface is loosely defined as follows:
|
||||
|
||||
.QueryPostProcessor
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
package org.springframework.data.gemfire.repository.query;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
import ...;
|
||||
|
||||
@FunctionalInterface
|
||||
interface QueryPostProcessor<T extends Repository, QUERY> extends Ordered {
|
||||
|
||||
QUERY postProcess(QueryMethod queryMethod, QUERY query, Object... arguments);
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
There are additional default methods provided that let you compose instances of `QueryPostProcessor` similar to how
|
||||
https://docs.oracle.com/javase/8/docs/api/java/util/function/Function.html#compose-java.util.function.Function-[java.util.function.Function.andThen(:Function)]
|
||||
and https://docs.oracle.com/javase/8/docs/api/java/util/function/Function.html#compose-java.util.function.Function-[java.util.function.Function.compose(:Function)]
|
||||
work.
|
||||
|
||||
Additionally, the `QueryPostProcessor` interface implements the
|
||||
{spring-framework-javadoc}/org/springframework/core/Ordered.html[`org.springframework.core.Ordered`] interface,
|
||||
which is useful when multiple `QueryPostProcessors` are declared and registered in the Spring container and used to
|
||||
create a pipeline of processing for a group of generated query method queries.
|
||||
|
||||
Finally, the `QueryPostProcessor` accepts type arguments corresponding to the type parameters, `T` and `QUERY`,
|
||||
respectively. Type `T` extends the Spring Data Commons marker interface,
|
||||
{spring-data-commons-javadoc}/org/springframework/data/repository/Repository.html[`org.springframework.data.repository.Repository`].
|
||||
We discuss this further later in this section. All `QUERY` type parameter arguments in {sdg-name}'s case are of type
|
||||
`java.lang.String`.
|
||||
|
||||
NOTE: It is useful to define the query as type `QUERY`, since this `QueryPostProcessor` interface may be ported to
|
||||
Spring Data Commons and therefore must handle all forms of queries by different data stores (such as JPA, MongoDB,
|
||||
or Redis).
|
||||
|
||||
You can implement this interface to receive a callback with the query that was generated from the application
|
||||
`Repository` interface method when the method is called.
|
||||
|
||||
For example, you might want to log all queries from all application Repository interface definitions. You could do so
|
||||
by using the following `QueryPostProcessor` implementation:
|
||||
|
||||
.LoggingQueryPostProcessor
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
package example;
|
||||
|
||||
import ...;
|
||||
|
||||
class LoggingQueryPostProcessor implements QueryPostProcessor<Repository, String> {
|
||||
|
||||
private Logger logger = Logger.getLogger("someLoggerName");
|
||||
|
||||
@Override
|
||||
public String postProcess(QueryMethod queryMethod, String query, Object... arguments) {
|
||||
|
||||
String message = String.format("Executing query [%s] with arguments [%s]", query, Arrays.toString(arguments));
|
||||
|
||||
this.logger.info(message);
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The `LoggingQueryPostProcessor` was typed to the Spring Data `org.springframework.data.repository.Repository`
|
||||
marker interface, and, therefore, logs all application Repository interface query method generated queries.
|
||||
|
||||
You could limit the scope of this logging to queries only from certain types of application Repository interfaces,
|
||||
such as, say, a `CustomerRepository`, as the following example shows:
|
||||
|
||||
.CustomerRepository
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
interface CustomerRepository extends CrudRepository<Customer, Long> {
|
||||
|
||||
Customer findByAccountNumber(String accountNumber);
|
||||
|
||||
List<Customer> findByLastNameLike(String lastName);
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Then you could have typed the `LoggingQueryPostProcessor` specifically to the `CustomerRepository`, as follows:
|
||||
|
||||
.CustomerLoggingQueryPostProcessor
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
class LoggingQueryPostProcessor implements QueryPostProcessor<CustomerRepository, String> { .. }
|
||||
----
|
||||
====
|
||||
|
||||
As a result, only queries defined in the `CustomerRepository` interface, such as `findByAccountNumber`, are logged.
|
||||
|
||||
You might want to create a `QueryPostProcessor` for a specific query defined by a Repository query method. For example,
|
||||
suppose you want to limit the OQL query generated from the `CustomerRepository.findByLastNameLike(:String)` query method
|
||||
to only return five results along with ordering the `Customers` by `firstName`, in ascending order . To do so,
|
||||
you can define a custom `QueryPostProcessor`, as the following example shows:
|
||||
|
||||
.OrderedLimitedCustomerByLastNameQueryPostProcessor
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
class OrderedLimitedCustomerByLastNameQueryPostProcessor implements QueryPostProcessor<CustomerRepository, String> {
|
||||
|
||||
private final int limit;
|
||||
|
||||
public OrderedLimitedCustomerByLastNameQueryPostProcessor(int limit) {
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String postProcess(QueryMethod queryMethod, String query, Object... arguments) {
|
||||
|
||||
return "findByLastNameLike".equals(queryMethod.getName())
|
||||
? query.trim()
|
||||
.replace("SELECT", "SELECT DISTINCT")
|
||||
.concat(" ORDER BY firstName ASC")
|
||||
.concat(String.format(" LIMIT %d", this.limit))
|
||||
: query;
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
While the preceding example works, you can achieve the same effect by using the Spring Data Repository convention
|
||||
provided by {sdg-name}. For instance, the same query could be defined as follows:
|
||||
|
||||
.CustomerRepository using the convention
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
interface CustomerRepository extends CrudRepository<Customer, Long> {
|
||||
|
||||
@Limit(5)
|
||||
List<Customer> findDistinctByLastNameLikeOrderByFirstNameDesc(String lastName);
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
However, if you do not have control over the application `CustomerRepository` interface definition,
|
||||
then the `QueryPostProcessor` (that is, `OrderedLimitedCustomerByLastNameQueryPostProcessor`) is convenient.
|
||||
|
||||
If you want to ensure that the `LoggingQueryPostProcessor` always comes after the other application-defined
|
||||
`QueryPostProcessors` that may have bean declared and registered in the Spring `ApplicationContext`, you can set
|
||||
the `order` property by overriding the `o.s.core.Ordered.getOrder()` method, as the following example shows:
|
||||
|
||||
.Defining the `order` property
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
class LoggingQueryPostProcessor implements QueryPostProcessor<Repository, String> {
|
||||
|
||||
@Override
|
||||
int getOrder() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
class CustomerQueryPostProcessor implements QueryPostProcessor<CustomerRepository, String> {
|
||||
|
||||
@Override
|
||||
int getOrder() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
This ensures that you always see the effects of the post processing applied by other `QueryPostProcessors`
|
||||
before the `LoggingQueryPostProcessor` logs the query.
|
||||
|
||||
You can define as many `QueryPostProcessors` in the Spring `ApplicationContext` as you like and apply them in any order,
|
||||
to all or specific application Repository interfaces, and be as granular as you like by using the provided arguments
|
||||
to the `postProcess(..)` method callback.
|
||||
@@ -1,113 +0,0 @@
|
||||
[[samples]]
|
||||
= Sample Applications
|
||||
|
||||
NOTE: Sample applications are now maintained in the
|
||||
https://github.com/spring-projects/spring-gemfire-examples[Spring {data-store-name} Examples] repository.
|
||||
|
||||
The {sdg-name} project also includes one sample application. Named "`Hello World`", the sample application
|
||||
demonstrates how to configure and use {data-store-name} inside a Spring application. At run time, the sample offers
|
||||
a shell that lets you run various commands against the data grid. It provides an excellent
|
||||
starting point for developers who are unfamiliar with the essential components or with Spring and {data-store-name} concepts.
|
||||
|
||||
The sample is bundled with the distribution and is Maven-based. You can import it into any
|
||||
Maven-aware IDE (such as the https://spring.io/tools/sts[Spring Tool Suite]) or run them from the command-line.
|
||||
|
||||
[[samples:hello-world]]
|
||||
== Hello World
|
||||
|
||||
The "`Hello World`" sample application demonstrates the core functionality of the {sdg-name} project.
|
||||
It bootstraps {data-store-name}, configures it, executes arbitrary commands against the cache, and shuts it down
|
||||
when the application exits. Multiple instances of the application can be started at the same time
|
||||
and work together, sharing data without any user intervention.
|
||||
|
||||
.Running under Linux
|
||||
NOTE: If you experience networking problems when starting {data-store-name} or the samples, try adding the following
|
||||
system property `java.net.preferIPv4Stack=true` to the command line (for example, `-Djava.net.preferIPv4Stack=true`).
|
||||
For an alternative (global) fix (especially on Ubuntu), see https://jira.spring.io/browse/SGF-28[SGF-28].
|
||||
|
||||
[[samples:hello-world:start-stop]]
|
||||
=== Starting and Stopping the Sample
|
||||
|
||||
The "`Hello World`" sample application is designed as a stand-alone Java application. It features a `main` class that can be started
|
||||
either from your IDE (in Eclipse or STS, through `Run As/Java Application`) or from the command line
|
||||
through Maven with `mvn exec:java`. If the classpath is properly set, you can also use `java` directly on the resulting artifact.
|
||||
|
||||
To stop the sample, type `exit` at the command line or press `Ctrl+C` to stop the JVM and shutdown
|
||||
the Spring container.
|
||||
|
||||
[[samples:hello-world:run]]
|
||||
=== Using the Sample
|
||||
|
||||
Once started, the sample creates a shared data grid and lets you issue commands against it.
|
||||
The output should resemble the following:
|
||||
|
||||
[source]
|
||||
----
|
||||
INFO: Created {data-store-name} Cache [Spring {data-store-name} World] v. X.Y.Z
|
||||
INFO: Created new cache region [myWorld]
|
||||
INFO: Member xxxxxx:50694/51611 connecting to region [myWorld]
|
||||
Hello World!
|
||||
Want to interact with the world ? ...
|
||||
Supported commands are:
|
||||
|
||||
get <key> - retrieves an entry (by key) from the grid
|
||||
put <key> <value> - puts a new entry into the grid
|
||||
remove <key> - removes an entry (by key) from the grid
|
||||
...
|
||||
----
|
||||
|
||||
For example, to add new items to the grid, you can use the following commands:
|
||||
|
||||
[source]
|
||||
----
|
||||
-> Bold Section qName:emphasis level:5, chunks:[put 1 unu] attrs:[role:bold]
|
||||
INFO: Added [1=unu] to the cache
|
||||
null
|
||||
-> Bold Section qName:emphasis level:5, chunks:[put 1 one] attrs:[role:bold]
|
||||
INFO: Updated [1] from [unu] to [one]
|
||||
unu
|
||||
-> Bold Section qName:emphasis level:5, chunks:[size] attrs:[role:bold]
|
||||
1
|
||||
-> Bold Section qName:emphasis level:5, chunks:[put 2 two] attrs:[role:bold]
|
||||
INFO: Added [2=two] to the cache
|
||||
null
|
||||
-> Bold Section qName:emphasis level:5, chunks:[size] attrs:[role:bold]
|
||||
2
|
||||
----
|
||||
|
||||
Multiple instances can be ran at the same time. Once started, the new VMs automatically see the existing region
|
||||
and its information, as the following example shows:
|
||||
|
||||
[source]
|
||||
----
|
||||
INFO: Connected to Distributed System ['Spring {data-store-name} World'=xxxx:56218/49320@yyyyy]
|
||||
Hello World!
|
||||
...
|
||||
|
||||
-> Bold Section qName:emphasis level:5, chunks:[size] attrs:[role:bold]
|
||||
2
|
||||
-> Bold Section qName:emphasis level:5, chunks:[map] attrs:[role:bold]
|
||||
[2=two] [1=one]
|
||||
-> Bold Section qName:emphasis level:5, chunks:[query length = 3] attrs:[role:bold]
|
||||
[one, two]
|
||||
----
|
||||
|
||||
We encourage you to experiment with the example, start (and stop) as many instances as you want, and run various commands in one instance
|
||||
and see how the others react. To preserve data, at least one instance needs to be alive all times. If all instances
|
||||
are shutdown, the grid data is completely destroyed.
|
||||
|
||||
[[samples:hello-world:explained]]
|
||||
=== Hello World Sample Explained
|
||||
|
||||
The "`Hello World`" sample uses both Spring XML and annotations for its configuration. The initial bootstrapping configuration is
|
||||
`app-context.xml`, which includes the cache configuration defined in the `cache-context.xml` file
|
||||
and performs classpath
|
||||
https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-classpath-scanning[component scanning]
|
||||
for Spring
|
||||
https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-annotation-config[components].
|
||||
|
||||
The cache configuration defines the {data-store-name} cache, a region, and for illustrative purposes, a `CacheListener`
|
||||
that acts as a logger.
|
||||
|
||||
The main beans are `HelloWorld` and `CommandProcessor`, which rely on the `GemfireTemplate` to interact with
|
||||
the distributed fabric. Both classes use annotations to define their dependency and life-cycle callbacks.
|
||||
@@ -1,71 +0,0 @@
|
||||
[[serialization]]
|
||||
= Working with {data-store-name} Serialization
|
||||
|
||||
To improve overall performance of the {data-store-name} In-memory Data Grid, {data-store-name} supports a dedicated
|
||||
serialization protocol, called PDX, that is both faster and offers more compact results over standard Java serialization
|
||||
in addition to working transparently across various language platforms (Java, C++, and .NET).
|
||||
|
||||
See {x-data-store-docs}/developing/data_serialization/PDX_Serialization_Features.html[PDX Serialization Features]
|
||||
and {x-data-store-wiki}/PDX+Serialization+Internals[PDX Serialization Internals] for more details.
|
||||
|
||||
This chapter discusses the various ways in which {sdg-name} simplifies and improves {data-store-name}'s
|
||||
custom serialization in Java.
|
||||
|
||||
[[serialization:wiring]]
|
||||
== Wiring deserialized instances
|
||||
|
||||
It is fairly common for serialized objects to have transient data. Transient data is often dependent on the system
|
||||
or environment where it lives at a certain point in time. For instance, a `DataSource` is environment specific.
|
||||
Serializing such information is useless and potentially even dangerous, since it is local to a certain VM or machine.
|
||||
For such cases, {sdg-name} offers a special {x-data-store-javadoc}/org/apache/geode/Instantiator.html[`Instantiator`]
|
||||
that performs wiring for each new instance created by {data-store-name} during deserialization.
|
||||
|
||||
Through such a mechanism, you can rely on the Spring container to inject and manage certain dependencies, making it easy
|
||||
to split transient from persistent data and have rich domain objects in a transparent manner.
|
||||
|
||||
Spring users might find this approach similar to that of {spring-framework-docs}/#aop-atconfigurable[`@Configurable`]).
|
||||
The `WiringInstantiator` works similarly to `WiringDeclarableSupport`, trying to first locate a bean definition
|
||||
as a wiring template and otherwise falling back to auto-wiring.
|
||||
|
||||
See the previous section (<<apis:declarable>>) for more details on wiring functionality.
|
||||
|
||||
To use the {sdg-acronym} `Instantiator`, declare it as a bean, as the following example shows:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<bean id="instantiator" class="org.springframework.data.gemfire.serialization.WiringInstantiator">
|
||||
<!-- DataSerializable type -->
|
||||
<constructor-arg>org.pkg.SomeDataSerializableClass</constructor-arg>
|
||||
<!-- type id -->
|
||||
<constructor-arg>95</constructor-arg>
|
||||
</bean>
|
||||
----
|
||||
|
||||
During the Spring container startup, once it has been initialized, the `Instantiator`, by default, registers itself with
|
||||
the {data-store-name} serialization system and performs wiring on all instances of `SomeDataSerializableClass` created
|
||||
by {data-store-name} during deserialization.
|
||||
|
||||
[[serialization:instance-generator]]
|
||||
== Auto-generating Custom `Instantiators`
|
||||
|
||||
For data intensive applications, a large number of instances might be created on each machine as data flows in.
|
||||
{data-store-name} uses reflection to create new types, but, for some scenarios, this might prove to be expensive.
|
||||
As always, it is good to perform profiling to quantify whether this is the case or not. For such cases, {sdg-name}
|
||||
allows the automatic generation of `Instatiator` classes, which instantiate a new type (using the default constructor)
|
||||
without the use of reflection. The following example shows how to create an instantiator:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<bean id="instantiatorFactory" class="org.springframework.data.gemfire.serialization.InstantiatorFactoryBean">
|
||||
<property name="customTypes">
|
||||
<map>
|
||||
<entry key="org.pkg.CustomTypeA" value="1025"/>
|
||||
<entry key="org.pkg.CustomTypeB" value="1026"/>
|
||||
</map>
|
||||
</property>
|
||||
</bean>
|
||||
----
|
||||
|
||||
The preceding definition automatically generates two `Instantiators` for two classes (`CustomTypeA` and `CustomTypeB`)
|
||||
and registers them with {data-store-name} under user ID `1025` and `1026`. The two `Instantiators` avoid the use of
|
||||
reflection and create the instances directly through Java code.
|
||||
@@ -1,255 +0,0 @@
|
||||
[[bootstrap:snapshot]]
|
||||
= Configuring the Snapshot Service
|
||||
|
||||
{sdg-name} supports cache and Region snapshots by using
|
||||
{x-data-store-docs}/managing/cache_snapshots/chapter_overview.html[{data-store-name}'s Snapshot Service].
|
||||
The out-of-the-box Snapshot Service support offers several convenient features to simplify the use of {data-store-name}'s
|
||||
{x-data-store-javadoc}/org/apache/geode/cache/snapshot/CacheSnapshotService.html[Cache]
|
||||
and {x-data-store-javadoc}/org/apache/geode/cache/snapshot/RegionSnapshotService.html[Region]
|
||||
Snapshot Service APIs.
|
||||
|
||||
As the {x-data-store-docs}/managing/cache_snapshots/chapter_overview.html[{data-store-name} documentation] explains,
|
||||
snapshots let you save and subsequently reload the cached data later, which can be useful for moving data between
|
||||
environments, such as from production to a staging or test environment in order to reproduce data-related issues
|
||||
in a controlled context. You can combine {sdg-name}'s Snapshot Service support
|
||||
with https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-definition-profiles[Spring's bean definition profiles]
|
||||
to load snapshot data specific to the environment as necessary.
|
||||
|
||||
{sdg-name}'s support for {data-store-name}'s Snapshot Service begins with the `<gfe-data:snapshot-service>` element
|
||||
from the `<gfe-data>` XML namespace.
|
||||
|
||||
For example, you can define cache-wide snapshots to be loaded as well as saved by using a couple of snapshot imports
|
||||
and a data export definition, as follows:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<gfe-data:snapshot-service id="gemfireCacheSnapshotService">
|
||||
<gfe-data:snapshot-import location="/absolute/filesystem/path/to/import/fileOne.snapshot"/>
|
||||
<gfe-data:snapshot-import location="relative/filesystem/path/to/import/fileTwo.snapshot"/>
|
||||
<gfe-data:snapshot-export
|
||||
location="/absolute/or/relative/filesystem/path/to/export/directory"/>
|
||||
</gfe-data:snapshot-service>
|
||||
----
|
||||
|
||||
You can define as many imports and exports as you like. You can define only imports or only exports. The file locations
|
||||
and directory paths can be absolute or relative to the {sdg-name} application, which is the JVM process's
|
||||
working directory.
|
||||
|
||||
The preceding example is pretty simple, and the Snapshot Service defined in this case refers to the {data-store-name}
|
||||
cache instance with the default name of `gemfireCache` (as described in <<bootstrap:cache>>). If you name your cache
|
||||
bean definition something other than the default, you can use the `cache-ref` attribute to refer to the cache bean
|
||||
by name, as follows:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<gfe:cache id="myCache"/>
|
||||
...
|
||||
<gfe-data:snapshot-service id="mySnapshotService" cache-ref="myCache">
|
||||
...
|
||||
</gfe-data:snapshot-service>
|
||||
----
|
||||
|
||||
You can also define a Snapshot Service for a particular Region by specifying the `region-ref` attribute, as follows:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<gfe:partitioned-region id="Example" persistent="false" .../>
|
||||
...
|
||||
<gfe-data:snapshot-service id="gemfireCacheRegionSnapshotService" region-ref="Example">
|
||||
<gfe-data:snapshot-import location="relative/path/to/import/example.snapshot/>
|
||||
<gfe-data:snapshot-export location="/absolute/path/to/export/example.snapshot/>
|
||||
</gfe-data:snapshot-service>
|
||||
----
|
||||
|
||||
When the `region-ref` attribute is specified, {sdg-name}'s `SnapshotServiceFactoryBean` resolves the `region-ref`
|
||||
attribute value to a Region bean defined in the Spring container and creates a
|
||||
{x-data-store-javadoc}/org/apache/geode/cache/snapshot/RegionSnapshotService.html[`RegionSnapshotService`].
|
||||
The snapshot import and export definitions function the same way. However, the `location` must refer to a file
|
||||
on an export.
|
||||
|
||||
NOTE: {data-store-name} is strict about imported snapshot files actually existing before they are referenced.
|
||||
For exports, {data-store-name} creates the snapshot file. If the snapshot file for export already exists,
|
||||
the data is overwritten.
|
||||
|
||||
TIP: {sdg-name} includes a `suppress-import-on-init` attribute on the `<gfe-data:snapshot-service>` element
|
||||
to suppress the configured Snapshot Service from trying to import data into the cache or Region on initialization.
|
||||
Doing so is useful, for example, when data exported from one Region is used to feed the import of another Region.
|
||||
|
||||
[[bootstrap:snapshot:location]]
|
||||
== Snapshot Location
|
||||
|
||||
With the cache-based Snapshot Service
|
||||
(that is, a {x-data-store-javadoc}/org/apache/geode/cache/snapshot/CacheSnapshotService.html[`CacheSnapshotService`])
|
||||
you would typically pass it a directory containing all the snapshot files to load rather than individual snapshot files,
|
||||
as the overloaded {x-data-store-javadoc}/org/apache/geode/cache/snapshot/CacheSnapshotService.html#load-java.io.File-org.apache.geode.cache.snapshot.SnapshotOptions.SnapshotFormat[`load`]
|
||||
method in the `CacheSnapshotService` API indicates.
|
||||
|
||||
NOTE: Of course, you can use the overloaded `load(:File[], :SnapshotFormat, :SnapshotOptions)` method to get specific
|
||||
about which snapshot files to load into the {data-store-name} cache.
|
||||
|
||||
However, {sdg-name} recognizes that a typical developer workflow might be to extract and export data
|
||||
from one environment into several snapshot files, zip all of them up, and then conveniently move the zip file
|
||||
to another environment for import.
|
||||
|
||||
Therefore, {sdg-name} lets you specify a jar or zip file on import for a `cache`-based Snapshot Service, as follows:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<gfe-data:snapshot-service id="cacheBasedSnapshotService" cache-ref="gemfireCache">
|
||||
<gfe-data:snapshot-import location="/path/to/snapshots.zip"/>
|
||||
</gfe-data:snapshot-service>
|
||||
----
|
||||
|
||||
{sdg-name} conveniently extracts the provided zip file and treats it as a directory import (load).
|
||||
|
||||
[[bootstrap:snapshot:filters]]
|
||||
== Snapshot Filters
|
||||
|
||||
The real power of defining multiple snapshot imports and exports is realized through the use of snapshot filters.
|
||||
Snapshot filters implement {data-store-name}'s {x-data-store-javadoc}/org/apache/geode/cache/snapshot/SnapshotFilter.html[`SnapshotFilter`] interface
|
||||
and are used to filter Region entries for inclusion into the Region on import and for inclusion into the snapshot
|
||||
on export.
|
||||
|
||||
{sdg-name} lets you use snapshot filters on import and export by using the `filter-ref` attribute or an anonymous,
|
||||
nested bean definition, as the following example shows:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<gfe:cache/>
|
||||
|
||||
<gfe:partitioned-region id="Admins" persistent="false"/>
|
||||
<gfe:partitioned-region id="Guests" persistent="false"/>
|
||||
|
||||
<bean id="activeUsersFilter" class="example.gemfire.snapshot.filter.ActiveUsersFilter/>
|
||||
|
||||
<gfe-data:snapshot-service id="adminsSnapshotService" region-ref="Admins">
|
||||
<gfe-data:snapshot-import location="/path/to/import/users.snapshot">
|
||||
<bean class="example.gemfire.snapshot.filter.AdminsFilter/>
|
||||
</gfe-data:snapshot-import>
|
||||
<gfe-data:snapshot-export location="/path/to/export/active/admins.snapshot" filter-ref="activeUsersFilter"/>
|
||||
</gfe-data:snapshot-service>
|
||||
|
||||
<gfe-data:snapshot-service id="guestsSnapshotService" region-ref="Guests">
|
||||
<gfe-data:snapshot-import location="/path/to/import/users.snapshot">
|
||||
<bean class="example.gemfire.snapshot.filter.GuestsFilter/>
|
||||
</gfe-data:snapshot-import>
|
||||
<gfe-data:snapshot-export location="/path/to/export/active/guests.snapshot" filter-ref="activeUsersFilter"/>
|
||||
</gfe-data:snapshot-service>
|
||||
----
|
||||
|
||||
In addition, you can express more complex snapshot filters by using the `ComposableSnapshotFilter` class.
|
||||
This class implements {data-store-name}'s {x-data-store-javadoc}/org/apache/geode/cache/snapshot/SnapshotFilter.html[SnapshotFilter] interface
|
||||
as well as the https://en.wikipedia.org/wiki/Composite_pattern[Composite] software design pattern.
|
||||
|
||||
In a nutshell, the https://en.wikipedia.org/wiki/Composite_pattern[Composite] software design pattern lets you
|
||||
compose multiple objects of the same type and treat the aggregate as single instance of the object type -- a
|
||||
powerful and useful abstraction.
|
||||
|
||||
`ComposableSnapshotFilter` has two factory methods, `and` and `or`. They let you logically combine individual snapshot
|
||||
filters using the AND and OR logical operators, respectively. The factory methods take a list of `SnapshotFilters`.
|
||||
|
||||
The following example shows a definition for a `ComposableSnapshotFilter`:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<bean id="activeUsersSinceFilter" class="org.springframework.data.gemfire.snapshot.filter.ComposableSnapshotFilter"
|
||||
factory-method="and">
|
||||
<constructor-arg index="0">
|
||||
<list>
|
||||
<bean class="org.example.app.gemfire.snapshot.filter.ActiveUsersFilter"/>
|
||||
<bean class="org.example.app.gemfire.snapshot.filter.UsersSinceFilter"
|
||||
p:since="2015-01-01"/>
|
||||
</list>
|
||||
</constructor-arg>
|
||||
</bean>
|
||||
----
|
||||
|
||||
You could then go on to combine the `activesUsersSinceFilter` with another filter by using `or`, as follows:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<bean id="covertOrActiveUsersSinceFilter" class="org.springframework.data.gemfire.snapshot.filter.ComposableSnapshotFilter"
|
||||
factory-method="or">
|
||||
<constructor-arg index="0">
|
||||
<list>
|
||||
<ref bean="activeUsersSinceFilter"/>
|
||||
<bean class="example.gemfire.snapshot.filter.CovertUsersFilter"/>
|
||||
</list>
|
||||
</constructor-arg>
|
||||
</bean>
|
||||
----
|
||||
|
||||
[[bootstrap::snapshot::events]]
|
||||
== Snapshot Events
|
||||
|
||||
By default, {sdg-name} uses {data-store-name}'s Snapshot Services on startup to import data and on shutdown
|
||||
to export data. However, you may want to trigger periodic, event-based snapshots, for either import or export,
|
||||
from within your Spring application.
|
||||
|
||||
For this purpose, {sdg-name} defines two additional Spring application events, extending Spring's
|
||||
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/ApplicationEvent.html[`ApplicationEvent`]
|
||||
class for imports and exports, respectively: `ImportSnapshotApplicationEvent` and `ExportSnapshotApplicationEvent`.
|
||||
|
||||
The two application events can be targeted for the entire {data-store-name} cache or for individual {data-store-name}
|
||||
Regions. The constructors in these classes accept an optional Region pathname (such as `/Example`) as well as zero
|
||||
or more `SnapshotMetadata` instances.
|
||||
|
||||
The array of `SnapshotMetadata` overrides the snapshot metadata defined by `<gfe-data:snapshot-import>`
|
||||
and `<gfe-data:snapshot-export>` sub-elements, which are used in cases where snapshot application events do not
|
||||
explicitly provide `SnapshotMetadata`. Each individual `SnapshotMetadata` instance can define its own `location`
|
||||
and `filters` properties.
|
||||
|
||||
All snapshot service beans defined in the Spring `ApplicationContext` receive import and export snapshot
|
||||
application events. However, only matching Snapshot Service beans process import and export events.
|
||||
|
||||
A Region-based `[Import|Export]SnapshotApplicationEvent` matches if the Snapshot Service bean defined
|
||||
is a `RegionSnapshotService` and its Region reference (as determined by the `region-ref` attribute) matches
|
||||
the Region's pathname, as specified by the snapshot application event.
|
||||
|
||||
A Cache-based `[Import|Export]SnapshotApplicationEvent` (that is, a snapshot application event without a Region pathname)
|
||||
triggers all Snapshot Service beans, including any `RegionSnapshotService` beans, to perform either an import or export,
|
||||
respectively.
|
||||
|
||||
You can use Spring's
|
||||
{spring-framework-javadoc}/org/springframework/context/ApplicationEventPublisher.html[`ApplicationEventPublisher`]
|
||||
interface to fire import and export snapshot application events from your application as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Component
|
||||
public class ExampleApplicationComponent {
|
||||
|
||||
@Autowired
|
||||
private ApplicationEventPublisher eventPublisher;
|
||||
|
||||
@Resource(name = "Example")
|
||||
private Region<?, ?> example;
|
||||
|
||||
public void someMethod() {
|
||||
|
||||
...
|
||||
|
||||
File dataSnapshot = new File(System.getProperty("user.dir"), "/path/to/export/data.snapshot");
|
||||
|
||||
SnapshotFilter myFilter = ...;
|
||||
|
||||
SnapshotMetadata exportSnapshotMetadata =
|
||||
new SnapshotMetadata(dataSnapshot, myFilter, null);
|
||||
|
||||
ExportSnapshotApplicationEvent exportSnapshotEvent =
|
||||
new ExportSnapshotApplicationEvent(this, example.getFullPath(), exportSnapshotMetadata)
|
||||
|
||||
eventPublisher.publishEvent(exportSnapshotEvent);
|
||||
|
||||
...
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
In the preceding example, only the `/Example` Region's Snapshot Service bean picks up and handles the export event,
|
||||
saving the filtered, "`/Example`" Region's data to the `data.snapshot` file in a sub-directory of the application's
|
||||
working directory.
|
||||
|
||||
Using the Spring application events and messaging subsystem is a good way to keep your application loosely coupled.
|
||||
You can also use Spring's {spring-framework-docs}/#scheduling-task-scheduler[Scheduling] services to fire
|
||||
snapshot application events on a periodic basis.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,155 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
|
||||
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeCollection;
|
||||
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeIterable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import org.apache.geode.cache.Region;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.data.gemfire.client.ClientRegionFactoryBean;
|
||||
import org.springframework.data.gemfire.config.annotation.RegionConfigurer;
|
||||
|
||||
/**
|
||||
* {@link ConfigurableRegionFactoryBean} is an abstract base class encapsulating functionality common
|
||||
* to all configurable {@link Region} {@link FactoryBean FactoryBeans}.
|
||||
*
|
||||
* A {@literal configurable} {@link Region} {@link FactoryBean} includes all {@link FactoryBean FactoryBeans}
|
||||
* that create a {@link Region} and allow additional configuration to be applied via a {@link RegionConfigurer}.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.apache.geode.cache.Region
|
||||
* @see org.springframework.beans.factory.FactoryBean
|
||||
* @see org.springframework.data.gemfire.ResolvableRegionFactoryBean
|
||||
* @see org.springframework.data.gemfire.config.annotation.RegionConfigurer
|
||||
* @since 2.1.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class ConfigurableRegionFactoryBean<K, V> extends ResolvableRegionFactoryBean<K, V> {
|
||||
|
||||
private List<RegionConfigurer> regionConfigurers = Collections.emptyList();
|
||||
|
||||
private RegionConfigurer compositeRegionConfigurer = new RegionConfigurer() {
|
||||
|
||||
@Override
|
||||
public void configure(String beanName, ClientRegionFactoryBean<?, ?> bean) {
|
||||
nullSafeCollection(regionConfigurers)
|
||||
.forEach(regionConfigurer -> regionConfigurer.configure(beanName, bean));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(String beanName, PeerRegionFactoryBean<?, ?> bean) {
|
||||
nullSafeCollection(regionConfigurers)
|
||||
.forEach(regionConfigurer -> regionConfigurer.configure(beanName, bean));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a reference to the Composite {@link RegionConfigurer} used to apply additional configuration
|
||||
* to this {@link ClientRegionFactoryBean} on Spring container initialization.
|
||||
*
|
||||
* @return the Composite {@link RegionConfigurer}.
|
||||
* @see org.springframework.data.gemfire.config.annotation.RegionConfigurer
|
||||
*/
|
||||
protected RegionConfigurer getCompositeRegionConfigurer() {
|
||||
return this.compositeRegionConfigurer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe operation to set an array of {@link RegionConfigurer RegionConfigurers} used to apply
|
||||
* additional configuration to this {@link ClientRegionFactoryBean} when using Annotation-based configuration.
|
||||
*
|
||||
* @param regionConfigurers array of {@link RegionConfigurer RegionConfigurers} used to apply
|
||||
* additional configuration to this {@link ClientRegionFactoryBean}.
|
||||
* @see org.springframework.data.gemfire.config.annotation.RegionConfigurer
|
||||
* @see #setRegionConfigurers(List)
|
||||
*/
|
||||
public void setRegionConfigurers(RegionConfigurer... regionConfigurers) {
|
||||
setRegionConfigurers(Arrays.asList(nullSafeArray(regionConfigurers, RegionConfigurer.class)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe operation to set an {@link Iterable} of {@link RegionConfigurer RegionConfigurers} used to apply
|
||||
* additional configuration to this {@link ClientRegionFactoryBean} when using Annotation-based configuration.
|
||||
*
|
||||
* @param regionConfigurers {@link Iterable} of {@link RegionConfigurer RegionConfigurers} used to apply
|
||||
* additional configuration to this {@link ClientRegionFactoryBean}.
|
||||
* @see org.springframework.data.gemfire.config.annotation.RegionConfigurer
|
||||
*/
|
||||
public void setRegionConfigurers(List<RegionConfigurer> regionConfigurers) {
|
||||
|
||||
this.regionConfigurers = regionConfigurers != null
|
||||
? regionConfigurers
|
||||
: Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe operation to apply the composite {@link RegionConfigurer RegionConfigurers}
|
||||
* to this {@link ConfigurableRegionFactoryBean}.
|
||||
*
|
||||
* @param regionName {@link String} containing the name of the {@link Region}.
|
||||
* to this {@link ConfigurableRegionFactoryBean}.
|
||||
* @see org.springframework.data.gemfire.config.annotation.RegionConfigurer
|
||||
* @see #applyRegionConfigurers(String, Iterable)
|
||||
* @see #getCompositeRegionConfigurer()
|
||||
*/
|
||||
protected void applyRegionConfigurers(String regionName) {
|
||||
applyRegionConfigurers(regionName, getCompositeRegionConfigurer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe operation to apply the given array of {@link RegionConfigurer RegionConfigurers}
|
||||
* to this {@link ConfigurableRegionFactoryBean}.
|
||||
*
|
||||
* @param regionName {@link String} containing the name of the {@link Region}.
|
||||
* @param regionConfigurers array of {@link RegionConfigurer RegionConfigurers} applied
|
||||
* to this {@link ConfigurableRegionFactoryBean}.
|
||||
* @see org.springframework.data.gemfire.config.annotation.RegionConfigurer
|
||||
* @see #applyRegionConfigurers(String, Iterable)
|
||||
*/
|
||||
protected void applyRegionConfigurers(String regionName, RegionConfigurer... regionConfigurers) {
|
||||
applyRegionConfigurers(regionName, Arrays.asList(nullSafeArray(regionConfigurers, RegionConfigurer.class)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe operation to apply the given {@link Iterable} of {@link RegionConfigurer RegionConfigurers}
|
||||
* to this {@link ConfigurableRegionFactoryBean}.
|
||||
*
|
||||
* @param regionName {@link String} containing the name of the {@link Region}.
|
||||
* @param regionConfigurers {@link Iterable} of {@link RegionConfigurer RegionConfigurers} applied
|
||||
* to this {@link ConfigurableRegionFactoryBean}.
|
||||
* @see org.springframework.data.gemfire.config.annotation.RegionConfigurer
|
||||
* @see #applyRegionConfigurers(String, RegionConfigurer...)
|
||||
*/
|
||||
protected void applyRegionConfigurers(String regionName, Iterable<RegionConfigurer> regionConfigurers) {
|
||||
|
||||
if (this instanceof ClientRegionFactoryBean) {
|
||||
StreamSupport.stream(nullSafeIterable(regionConfigurers).spliterator(), false)
|
||||
.forEach(regionConfigurer -> regionConfigurer.configure(regionName, (ClientRegionFactoryBean<K, V>) this));
|
||||
}
|
||||
else if (this instanceof PeerRegionFactoryBean) {
|
||||
StreamSupport.stream(nullSafeIterable(regionConfigurers).spliterator(), false)
|
||||
.forEach(regionConfigurer -> regionConfigurer.configure(regionName, (PeerRegionFactoryBean<K, V>) this));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import org.apache.geode.cache.DataPolicy;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
/**
|
||||
* The DataPolicyConverter class converts String values into GemFire DataPolicy enumerated values.
|
||||
*
|
||||
* @author David Turanski
|
||||
* @author John Blum
|
||||
* @see org.springframework.core.convert.converter.Converter
|
||||
* @see org.apache.geode.cache.DataPolicy
|
||||
*/
|
||||
public class DataPolicyConverter implements Converter<String, DataPolicy> {
|
||||
|
||||
static enum Policy {
|
||||
DEFAULT, EMPTY, NORMAL, PRELOADED, PARTITION, PERSISTENT_PARTITION, REPLICATE, PERSISTENT_REPLICATE;
|
||||
|
||||
private static String toUpperCase(String value) {
|
||||
return (value == null ? null : value.toUpperCase());
|
||||
}
|
||||
|
||||
public static Policy getValue(String value) {
|
||||
try {
|
||||
return valueOf(toUpperCase(value));
|
||||
}
|
||||
catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public DataPolicy toDataPolicy() {
|
||||
switch (this) {
|
||||
case EMPTY:
|
||||
return DataPolicy.EMPTY;
|
||||
case NORMAL:
|
||||
return DataPolicy.NORMAL;
|
||||
case PRELOADED:
|
||||
return DataPolicy.PRELOADED;
|
||||
case PARTITION :
|
||||
return DataPolicy.PARTITION;
|
||||
case PERSISTENT_PARTITION:
|
||||
return DataPolicy.PERSISTENT_PARTITION;
|
||||
case REPLICATE:
|
||||
return DataPolicy.REPLICATE;
|
||||
case PERSISTENT_REPLICATE:
|
||||
return DataPolicy.PERSISTENT_REPLICATE;
|
||||
case DEFAULT:
|
||||
default:
|
||||
return DataPolicy.DEFAULT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataPolicy convert(String policyValue) {
|
||||
Policy policy = Policy.getValue(policyValue);
|
||||
return (policy == null ? null : policy.toDataPolicy());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,347 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import static java.util.stream.StreamSupport.stream;
|
||||
import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
|
||||
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeCollection;
|
||||
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeIterable;
|
||||
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.geode.cache.DiskStore;
|
||||
import org.apache.geode.cache.DiskStoreFactory;
|
||||
import org.apache.geode.cache.GemFireCache;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.data.gemfire.config.annotation.DiskStoreConfigurer;
|
||||
import org.springframework.data.gemfire.support.AbstractFactoryBeanSupport;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Spring {@link FactoryBean} used to create {@link DiskStore}.
|
||||
*
|
||||
* @author David Turanski
|
||||
* @author John Blum
|
||||
* @see org.apache.geode.cache.DiskStore
|
||||
* @see org.apache.geode.cache.DiskStoreFactory
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
* @see org.springframework.beans.factory.FactoryBean
|
||||
* @see org.springframework.beans.factory.InitializingBean
|
||||
* @see org.springframework.data.gemfire.config.annotation.DiskStoreConfigurer
|
||||
* @see org.springframework.data.gemfire.support.AbstractFactoryBeanSupport
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class DiskStoreFactoryBean extends AbstractFactoryBeanSupport<DiskStore> implements InitializingBean {
|
||||
|
||||
private Boolean allowForceCompaction;
|
||||
private Boolean autoCompact;
|
||||
|
||||
private DiskStore diskStore;
|
||||
|
||||
private GemFireCache cache;
|
||||
|
||||
private Integer compactionThreshold;
|
||||
private Integer queueSize;
|
||||
private Integer writeBufferSize;
|
||||
|
||||
private Float diskUsageCriticalPercentage;
|
||||
private Float diskUsageWarningPercentage;
|
||||
|
||||
private Long maxOplogSize;
|
||||
private Long timeInterval;
|
||||
|
||||
private List<DiskStoreConfigurer> diskStoreConfigurers = Collections.emptyList();
|
||||
|
||||
private DiskStoreConfigurer compositeDiskStoreConfigurer = (beanName, bean) ->
|
||||
nullSafeCollection(diskStoreConfigurers).forEach(diskStoreConfigurer ->
|
||||
diskStoreConfigurer.configure(beanName, bean));
|
||||
|
||||
private List<DiskDir> diskDirs;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
|
||||
String diskStoreName = resolveDiskStoreName();
|
||||
|
||||
applyDiskStoreConfigurers(diskStoreName);
|
||||
|
||||
GemFireCache cache = resolveCache(diskStoreName);
|
||||
|
||||
DiskStoreFactory diskStoreFactory = postProcess(configure(createDiskStoreFactory(cache)));
|
||||
|
||||
this.diskStore = postProcess(newDiskStore(diskStoreFactory, diskStoreName));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
private void applyDiskStoreConfigurers(String diskStoreName) {
|
||||
applyDiskStoreConfigurers(diskStoreName, getCompositeDiskStoreConfigurer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe operation to apply the given array of {@link DiskStoreConfigurer DiskStoreConfigurers}
|
||||
* to this {@link DiskStoreFactoryBean}.
|
||||
*
|
||||
* @param diskStoreName {@link String} containing the name of the {@link DiskStore}.
|
||||
* @param diskStoreConfigurers array of {@link DiskStoreConfigurer DiskStoreConfigurers} applied
|
||||
* to this {@link DiskStoreFactoryBean}.
|
||||
* @see org.springframework.data.gemfire.config.annotation.DiskStoreConfigurer
|
||||
* @see #applyDiskStoreConfigurers(String, Iterable)
|
||||
*/
|
||||
protected void applyDiskStoreConfigurers(String diskStoreName, DiskStoreConfigurer... diskStoreConfigurers) {
|
||||
applyDiskStoreConfigurers(diskStoreName,
|
||||
Arrays.asList(nullSafeArray(diskStoreConfigurers, DiskStoreConfigurer.class)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe operation to apply the given {@link Iterable} of {@link DiskStoreConfigurer DiskStoreConfigurers}
|
||||
* to this {@link DiskStoreFactoryBean}.
|
||||
*
|
||||
* @param diskStoreName {@link String} containing the name of the {@link DiskStore}.
|
||||
* @param diskStoreConfigurers {@link Iterable} of {@link DiskStoreConfigurer DiskStoreConfigurers} applied
|
||||
* to this {@link DiskStoreFactoryBean}.
|
||||
* @see org.springframework.data.gemfire.config.annotation.DiskStoreConfigurer
|
||||
*/
|
||||
protected void applyDiskStoreConfigurers(String diskStoreName, Iterable<DiskStoreConfigurer> diskStoreConfigurers) {
|
||||
stream(nullSafeIterable(diskStoreConfigurers).spliterator(), false)
|
||||
.forEach(diskStoreConfigurer -> diskStoreConfigurer.configure(diskStoreName, this));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
private GemFireCache resolveCache(String diskStoreName) {
|
||||
return Optional.ofNullable(this.cache)
|
||||
.orElseThrow(() -> newIllegalStateException("Cache is required to create DiskStore [%s]", diskStoreName));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
final String resolveDiskStoreName() {
|
||||
return Optional.ofNullable(getBeanName()).filter(StringUtils::hasText)
|
||||
.orElse(DiskStoreFactory.DEFAULT_DISK_STORE_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of {@link DiskStoreFactory} using the given {@link GemFireCache} in order to
|
||||
* construct, configure and initialize a new {@link DiskStore}.
|
||||
*
|
||||
* @param cache reference to the {@link GemFireCache} used to create the {@link DiskStoreFactory}.
|
||||
* @return a new instance of {@link DiskStoreFactory}.
|
||||
* @see org.apache.geode.cache.GemFireCache#createDiskStoreFactory()
|
||||
* @see org.apache.geode.cache.DiskStoreFactory
|
||||
*/
|
||||
protected DiskStoreFactory createDiskStoreFactory(GemFireCache cache) {
|
||||
return cache.createDiskStoreFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the given {@link DiskStoreFactory} with the configuration settings present
|
||||
* on this {@link DiskStoreFactoryBean}
|
||||
*
|
||||
* @param diskStoreFactory {@link DiskStoreFactory} to configure.
|
||||
* @return the given {@link DiskStoreFactory}
|
||||
* @see org.apache.geode.cache.DiskStoreFactory
|
||||
*/
|
||||
protected DiskStoreFactory configure(DiskStoreFactory diskStoreFactory) {
|
||||
|
||||
Optional.ofNullable(this.allowForceCompaction).ifPresent(diskStoreFactory::setAllowForceCompaction);
|
||||
Optional.ofNullable(this.autoCompact).ifPresent(diskStoreFactory::setAutoCompact);
|
||||
Optional.ofNullable(this.compactionThreshold).ifPresent(diskStoreFactory::setCompactionThreshold);
|
||||
Optional.ofNullable(this.diskUsageCriticalPercentage).ifPresent(diskStoreFactory::setDiskUsageCriticalPercentage);
|
||||
Optional.ofNullable(this.diskUsageWarningPercentage).ifPresent(diskStoreFactory::setDiskUsageWarningPercentage);
|
||||
Optional.ofNullable(this.maxOplogSize).ifPresent(diskStoreFactory::setMaxOplogSize);
|
||||
Optional.ofNullable(this.queueSize).ifPresent(diskStoreFactory::setQueueSize);
|
||||
Optional.ofNullable(this.timeInterval).ifPresent(diskStoreFactory::setTimeInterval);
|
||||
Optional.ofNullable(this.writeBufferSize).ifPresent(diskStoreFactory::setWriteBufferSize);
|
||||
|
||||
Optional.ofNullable(this.diskDirs).filter(diskDirs -> !CollectionUtils.isEmpty(diskDirs))
|
||||
.ifPresent(diskDirs -> {
|
||||
|
||||
File[] diskDirFiles = new File[diskDirs.size()];
|
||||
int[] diskDirSizes = new int[diskDirs.size()];
|
||||
|
||||
for (int index = 0; index < diskDirs.size(); index++) {
|
||||
DiskDir diskDir = diskDirs.get(index);
|
||||
diskDirFiles[index] = new File(diskDir.location);
|
||||
diskDirSizes[index] = Optional.ofNullable(diskDir.maxSize)
|
||||
.orElse(DiskStoreFactory.DEFAULT_DISK_DIR_SIZE);
|
||||
}
|
||||
|
||||
diskStoreFactory.setDiskDirsAndSizes(diskDirFiles, diskDirSizes);
|
||||
});
|
||||
|
||||
return diskStoreFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance of {@link DiskStore} with the given {@link String name}
|
||||
* using the provided {@link DiskStoreFactory}
|
||||
*
|
||||
* @param diskStoreFactory {@link DiskStoreFactory} used to create the {@link DiskStore}.
|
||||
* @param diskStoreName {@link String} containing the name of the new {@link DiskStore}.
|
||||
* @return a new instance of {@link DiskStore} with the given {@link String name}.
|
||||
* @see org.apache.geode.cache.DiskStoreFactory
|
||||
* @see org.apache.geode.cache.DiskStore
|
||||
*/
|
||||
protected DiskStore newDiskStore(DiskStoreFactory diskStoreFactory, String diskStoreName) {
|
||||
return diskStoreFactory.create(diskStoreName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Post-process the {@link DiskStoreFactory} with any custom {@link DiskStoreFactory} or {@link DiskStore}
|
||||
* configuration settings as required by the application.
|
||||
*
|
||||
* @param diskStoreFactory {@link DiskStoreFactory} to process.
|
||||
* @return the given {@link DiskStoreFactory}.
|
||||
* @see org.apache.geode.cache.DiskStoreFactory
|
||||
*/
|
||||
protected DiskStoreFactory postProcess(DiskStoreFactory diskStoreFactory) {
|
||||
return diskStoreFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Post-process the provided {@link DiskStore} constructed, configured and initialized
|
||||
* by this {@link DiskStoreFactoryBean}.
|
||||
*
|
||||
* @param diskStore {@link DiskStore} to process.
|
||||
* @return the given {@link DiskStore}.
|
||||
* @see org.apache.geode.cache.DiskStore
|
||||
*/
|
||||
protected DiskStore postProcess(DiskStore diskStore) {
|
||||
return diskStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the Composite {@link DiskStoreConfigurer} used to apply additional configuration
|
||||
* to this {@link DiskStoreFactoryBean} on Spring container initialization.
|
||||
*
|
||||
* @return the Composite {@link DiskStoreConfigurer}.
|
||||
* @see org.springframework.data.gemfire.config.annotation.DiskStoreConfigurer
|
||||
*/
|
||||
protected DiskStoreConfigurer getCompositeDiskStoreConfigurer() {
|
||||
return this.compositeDiskStoreConfigurer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DiskStore getObject() throws Exception {
|
||||
return this.diskStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Class<?> getObjectType() {
|
||||
return Optional.ofNullable(this.diskStore).map(DiskStore::getClass).orElse((Class) DiskStore.class);
|
||||
}
|
||||
|
||||
public void setCache(GemFireCache cache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
public void setAllowForceCompaction(Boolean allowForceCompaction) {
|
||||
this.allowForceCompaction = allowForceCompaction;
|
||||
}
|
||||
|
||||
public void setAutoCompact(Boolean autoCompact) {
|
||||
this.autoCompact = autoCompact;
|
||||
}
|
||||
|
||||
public void setCompactionThreshold(Integer compactionThreshold) {
|
||||
validateCompactionThreshold(compactionThreshold);
|
||||
this.compactionThreshold = compactionThreshold;
|
||||
}
|
||||
|
||||
protected void validateCompactionThreshold(Integer compactionThreshold) {
|
||||
Assert.isTrue(compactionThreshold == null || (compactionThreshold >= 0 && compactionThreshold <= 100),
|
||||
String.format("The DiskStore's (%1$s) compaction threshold (%2$d) must be an integer value between 0 and 100 inclusive.",
|
||||
resolveDiskStoreName(), compactionThreshold));
|
||||
}
|
||||
|
||||
public void setDiskDirs(List<DiskDir> diskDirs) {
|
||||
this.diskDirs = diskDirs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe operation to set an array of {@link DiskStoreConfigurer DiskStoreConfigurers} used to
|
||||
* apply additional configuration to this {@link DiskStoreFactoryBean} when using Annotation-based configuration.
|
||||
*
|
||||
* @param diskStoreConfigurers array of {@link DiskStoreConfigurer DiskStoreConfigurers} used to apply
|
||||
* additional configuration to this {@link DiskStoreFactoryBean}.
|
||||
* @see org.springframework.data.gemfire.config.annotation.DiskStoreConfigurer
|
||||
* @see #setDiskStoreConfigurers(List)
|
||||
*/
|
||||
public void setDiskStoreConfigurers(DiskStoreConfigurer... diskStoreConfigurers) {
|
||||
setDiskStoreConfigurers(Arrays.asList(nullSafeArray(diskStoreConfigurers, DiskStoreConfigurer.class)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe operation to set an {@link Iterable} of {@link DiskStoreConfigurer DiskStoreConfigurers}
|
||||
* used to apply additional configuration to this {@link DiskStoreFactoryBean}
|
||||
* when using Annotation-based configuration.
|
||||
*
|
||||
* @param diskStoreConfigurers {@link Iterable } of {@link DiskStoreConfigurer DiskStoreConfigurers} used to
|
||||
* apply additional configuration to this {@link DiskStoreFactoryBean}.
|
||||
* @see org.springframework.data.gemfire.config.annotation.DiskStoreConfigurer
|
||||
*/
|
||||
public void setDiskStoreConfigurers(List<DiskStoreConfigurer> diskStoreConfigurers) {
|
||||
this.diskStoreConfigurers = Optional.ofNullable(diskStoreConfigurers).orElseGet(Collections::emptyList);
|
||||
}
|
||||
|
||||
public void setDiskUsageCriticalPercentage(Float diskUsageCriticalPercentage) {
|
||||
this.diskUsageCriticalPercentage = diskUsageCriticalPercentage;
|
||||
}
|
||||
|
||||
public void setDiskUsageWarningPercentage(Float diskUsageWarningPercentage) {
|
||||
this.diskUsageWarningPercentage = diskUsageWarningPercentage;
|
||||
}
|
||||
|
||||
public void setMaxOplogSize(Long maxOplogSize) {
|
||||
this.maxOplogSize = maxOplogSize;
|
||||
}
|
||||
|
||||
public void setQueueSize(Integer queueSize) {
|
||||
this.queueSize = queueSize;
|
||||
}
|
||||
|
||||
public void setTimeInterval(Long timeInterval) {
|
||||
this.timeInterval = timeInterval;
|
||||
}
|
||||
|
||||
public void setWriteBufferSize(Integer writeBufferSize) {
|
||||
this.writeBufferSize = writeBufferSize;
|
||||
}
|
||||
|
||||
public static class DiskDir {
|
||||
|
||||
final Integer maxSize;
|
||||
final String location;
|
||||
|
||||
public DiskDir(String location) {
|
||||
this.location = location;
|
||||
this.maxSize = null;
|
||||
}
|
||||
|
||||
public DiskDir(String location, int maxSize) {
|
||||
this.location = location;
|
||||
this.maxSize = maxSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import org.apache.geode.cache.FixedPartitionAttributes;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Spring {@link FactoryBean} to create a instance of the {@link FixedPartitionAttributes}.
|
||||
*
|
||||
* @author David Turanski
|
||||
* @author John Blum
|
||||
* @see org.springframework.beans.factory.FactoryBean
|
||||
* @see org.springframework.beans.factory.InitializingBean
|
||||
* @see org.apache.geode.cache.FixedPartitionAttributes
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class FixedPartitionAttributesFactoryBean implements FactoryBean<FixedPartitionAttributes>, InitializingBean {
|
||||
|
||||
private Boolean primary;
|
||||
|
||||
private FixedPartitionAttributes fixedPartitionAttributes;
|
||||
|
||||
private Integer numBuckets;
|
||||
|
||||
private String partitionName;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("all")
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.hasText(this.partitionName, "partitionName must be specified");
|
||||
|
||||
if (this.primary == null && this.numBuckets == null){
|
||||
this.fixedPartitionAttributes = FixedPartitionAttributes.createFixedPartition(this.partitionName);
|
||||
}
|
||||
else if (this.primary == null && this.numBuckets != null){
|
||||
this.fixedPartitionAttributes = FixedPartitionAttributes.createFixedPartition(
|
||||
this.partitionName, this.numBuckets);
|
||||
}
|
||||
else if (this.primary != null && this.numBuckets == null) {
|
||||
this.fixedPartitionAttributes = FixedPartitionAttributes.createFixedPartition(
|
||||
this.partitionName, this.primary);
|
||||
}
|
||||
else {
|
||||
this.fixedPartitionAttributes = FixedPartitionAttributes.createFixedPartition(
|
||||
this.partitionName, this.primary, this.numBuckets);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public FixedPartitionAttributes getObject() throws Exception {
|
||||
return this.fixedPartitionAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return (this.fixedPartitionAttributes != null ? this.fixedPartitionAttributes.getClass()
|
||||
: FixedPartitionAttributes.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of buckets in the Partition Region.
|
||||
*
|
||||
* @param numBuckets integer value indicating the number of buckets in the Partition Region.
|
||||
*/
|
||||
public void setNumBuckets(Integer numBuckets) {
|
||||
this.numBuckets = numBuckets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the partition in the Partition Region.
|
||||
*
|
||||
* @param partitionName name of the partition.
|
||||
*/
|
||||
public void setPartitionName(String partitionName) {
|
||||
this.partitionName = partitionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether this particular PARTITION Region is the primary (i.e. not secondary).
|
||||
*
|
||||
* @param primary a boolean value to indicate whether this PARTITION Region is the primary.
|
||||
*/
|
||||
public void setPrimary(boolean primary) {
|
||||
this.primary = primary;
|
||||
}
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import org.apache.geode.GemFireCheckedException;
|
||||
import org.apache.geode.GemFireException;
|
||||
import org.apache.geode.cache.Region;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link GemfireAccessor} is a base class for {@link GemfireTemplate} defining common operations and properties,
|
||||
* such as {@link Region}.
|
||||
*
|
||||
* This class is not intended to be used directly.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author John Blum
|
||||
* @see org.springframework.beans.factory.InitializingBean
|
||||
* @see org.apache.geode.cache.Region
|
||||
*/
|
||||
public class GemfireAccessor implements InitializingBean {
|
||||
|
||||
protected final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private Region region;
|
||||
|
||||
/**
|
||||
* Returns the template GemFire Cache Region.
|
||||
*
|
||||
* @param <K> the Region key class type.
|
||||
* @param <V> the Region value class type.
|
||||
* @return the GemFire Cache Region.
|
||||
* @see org.apache.geode.cache.Region
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <K, V> Region<K, V> getRegion() {
|
||||
return region;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the template GemFire Cache Region.
|
||||
*
|
||||
* @param region the GemFire Cache Region used by this template.
|
||||
* @see org.apache.geode.cache.Region
|
||||
*/
|
||||
public void setRegion(Region<?, ?> region) {
|
||||
this.region = region;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
Assert.notNull(getRegion(), "Region is required");
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given {@link GemFireCheckedException} to an appropriate exception from the
|
||||
* <code>org.springframework.dao</code> hierarchy.
|
||||
* May be overridden in subclasses.
|
||||
* @param ex GemFireCheckedException that occurred
|
||||
* @return the corresponding DataAccessException instance
|
||||
*/
|
||||
public DataAccessException convertGemFireAccessException(GemFireCheckedException ex) {
|
||||
return GemfireCacheUtils.convertGemfireAccessException(ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given {@link GemFireException} to an appropriate exception from the
|
||||
* <code>org.springframework.dao</code> hierarchy.
|
||||
* May be overridden in subclasses.
|
||||
* @param ex GemFireException that occurred
|
||||
* @return the corresponding DataAccessException instance
|
||||
*/
|
||||
public DataAccessException convertGemFireAccessException(GemFireException ex) {
|
||||
return GemfireCacheUtils.convertGemfireAccessException(ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given GemFire exception to an appropriate exception from the
|
||||
* <code>org.springframework.dao</code> hierarchy. Note that this particular implementation
|
||||
* is called only for GemFire querying exception that do <b>NOT</b> extend from GemFire exception.
|
||||
* May be overridden in subclasses.
|
||||
*
|
||||
* @param ex GemFireException that occurred
|
||||
* @return the corresponding DataAccessException instance
|
||||
*/
|
||||
public DataAccessException convertGemFireQueryException(RuntimeException ex) {
|
||||
return GemfireCacheUtils.convertQueryExceptions(ex);
|
||||
}
|
||||
}
|
||||
@@ -1,347 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import org.apache.geode.CancelException;
|
||||
import org.apache.geode.CopyException;
|
||||
import org.apache.geode.GemFireCacheException;
|
||||
import org.apache.geode.GemFireCheckedException;
|
||||
import org.apache.geode.GemFireConfigException;
|
||||
import org.apache.geode.GemFireException;
|
||||
import org.apache.geode.GemFireIOException;
|
||||
import org.apache.geode.IncompatibleSystemException;
|
||||
import org.apache.geode.InternalGemFireException;
|
||||
import org.apache.geode.InvalidValueException;
|
||||
import org.apache.geode.NoSystemException;
|
||||
import org.apache.geode.SystemConnectException;
|
||||
import org.apache.geode.SystemIsRunningException;
|
||||
import org.apache.geode.UnmodifiableException;
|
||||
import org.apache.geode.admin.AdminException;
|
||||
import org.apache.geode.cache.CacheException;
|
||||
import org.apache.geode.cache.CacheExistsException;
|
||||
import org.apache.geode.cache.CacheLoaderException;
|
||||
import org.apache.geode.cache.CacheRuntimeException;
|
||||
import org.apache.geode.cache.CacheWriterException;
|
||||
import org.apache.geode.cache.CacheXmlException;
|
||||
import org.apache.geode.cache.CommitConflictException;
|
||||
import org.apache.geode.cache.CommitIncompleteException;
|
||||
import org.apache.geode.cache.DiskAccessException;
|
||||
import org.apache.geode.cache.EntryDestroyedException;
|
||||
import org.apache.geode.cache.EntryExistsException;
|
||||
import org.apache.geode.cache.EntryNotFoundException;
|
||||
import org.apache.geode.cache.FailedSynchronizationException;
|
||||
import org.apache.geode.cache.OperationAbortedException;
|
||||
import org.apache.geode.cache.PartitionedRegionDistributionException;
|
||||
import org.apache.geode.cache.PartitionedRegionStorageException;
|
||||
import org.apache.geode.cache.RegionDestroyedException;
|
||||
import org.apache.geode.cache.RegionExistsException;
|
||||
import org.apache.geode.cache.ResourceException;
|
||||
import org.apache.geode.cache.RoleException;
|
||||
import org.apache.geode.cache.StatisticsDisabledException;
|
||||
import org.apache.geode.cache.SynchronizationCommitConflictException;
|
||||
import org.apache.geode.cache.VersionException;
|
||||
import org.apache.geode.cache.client.ServerConnectivityException;
|
||||
import org.apache.geode.cache.execute.FunctionException;
|
||||
import org.apache.geode.cache.query.CqClosedException;
|
||||
import org.apache.geode.cache.query.IndexInvalidException;
|
||||
import org.apache.geode.cache.query.IndexMaintenanceException;
|
||||
import org.apache.geode.cache.query.QueryException;
|
||||
import org.apache.geode.cache.query.QueryExecutionTimeoutException;
|
||||
import org.apache.geode.cache.query.QueryInvalidException;
|
||||
import org.apache.geode.distributed.LeaseExpiredException;
|
||||
import org.apache.geode.security.GemFireSecurityException;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.dao.DataRetrievalFailureException;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.dao.InvalidDataAccessResourceUsageException;
|
||||
import org.springframework.dao.PermissionDeniedDataAccessException;
|
||||
import org.springframework.dao.PessimisticLockingFailureException;
|
||||
import org.springframework.dao.TypeMismatchDataAccessException;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Abstract utility class featuring methods for Apache Geode / Pivotal GemFire Cache or Region handling.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author John Blum
|
||||
*/
|
||||
public abstract class GemfireCacheUtils {
|
||||
|
||||
private static Class<?> CQ_EXCEPTION_CLASS;
|
||||
|
||||
static {
|
||||
|
||||
Class<?> type = null;
|
||||
|
||||
try {
|
||||
type = ClassUtils.resolveClassName("org.apache.geode.cache.query.CqInvalidException",
|
||||
GemfireCacheUtils.class.getClassLoader());
|
||||
}
|
||||
catch (IllegalArgumentException ignore) { }
|
||||
|
||||
CQ_EXCEPTION_CLASS = type;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts the given (unchecked) Gemfire exception to an appropriate one from the
|
||||
* <code>org.springframework.dao</code> hierarchy.
|
||||
*
|
||||
* @param cause Gemfire unchecked exception
|
||||
* @return new the corresponding DataAccessException instance
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public static DataAccessException convertGemfireAccessException(GemFireException cause) {
|
||||
|
||||
if (cause instanceof CacheException) {
|
||||
if (cause instanceof CacheExistsException) {
|
||||
return new DataIntegrityViolationException(cause.getMessage(), cause);
|
||||
}
|
||||
if (cause instanceof CommitConflictException) {
|
||||
return new DataIntegrityViolationException(cause.getMessage(), cause);
|
||||
}
|
||||
if (cause instanceof CommitIncompleteException) {
|
||||
return new DataIntegrityViolationException(cause.getMessage(), cause);
|
||||
}
|
||||
if (cause instanceof EntryExistsException) {
|
||||
return new DuplicateKeyException(cause.getMessage(), cause);
|
||||
}
|
||||
if (cause instanceof EntryNotFoundException) {
|
||||
return new DataRetrievalFailureException(cause.getMessage(), cause);
|
||||
}
|
||||
if (cause instanceof RegionExistsException) {
|
||||
return new DataIntegrityViolationException(cause.getMessage(), cause);
|
||||
}
|
||||
}
|
||||
|
||||
if (cause instanceof CacheRuntimeException) {
|
||||
if (cause instanceof CacheXmlException) {
|
||||
return new GemfireSystemException(cause);
|
||||
}
|
||||
if (cause instanceof CancelException) {
|
||||
// all cancellations go wrapped by this exception
|
||||
return new GemfireCancellationException((CancelException) cause);
|
||||
}
|
||||
if (cause instanceof CqClosedException) {
|
||||
return new InvalidDataAccessApiUsageException(cause.getMessage(), cause);
|
||||
}
|
||||
if (cause instanceof DiskAccessException) {
|
||||
return new DataAccessResourceFailureException(cause.getMessage(), cause);
|
||||
}
|
||||
if (cause instanceof EntryDestroyedException) {
|
||||
return new InvalidDataAccessApiUsageException(cause.getMessage(), cause);
|
||||
}
|
||||
if (cause instanceof FailedSynchronizationException) {
|
||||
return new PessimisticLockingFailureException(cause.getMessage(), cause);
|
||||
}
|
||||
if (cause instanceof IndexMaintenanceException) {
|
||||
return new GemfireIndexException((IndexMaintenanceException) cause);
|
||||
}
|
||||
if (cause instanceof OperationAbortedException) {
|
||||
// treat user exceptions first
|
||||
if (cause instanceof CacheLoaderException) {
|
||||
return new GemfireSystemException(cause);
|
||||
}
|
||||
if (cause instanceof CacheWriterException) {
|
||||
return new GemfireSystemException(cause);
|
||||
}
|
||||
// the rest are treated as resource failures
|
||||
return new DataAccessResourceFailureException(cause.getMessage(), cause);
|
||||
}
|
||||
if (cause instanceof PartitionedRegionDistributionException) {
|
||||
return new DataAccessResourceFailureException(cause.getMessage(), cause);
|
||||
}
|
||||
if (cause instanceof PartitionedRegionStorageException) {
|
||||
return new DataAccessResourceFailureException(cause.getMessage(), cause);
|
||||
}
|
||||
if (cause instanceof QueryExecutionTimeoutException) {
|
||||
return new GemfireQueryException((QueryExecutionTimeoutException) cause);
|
||||
}
|
||||
if (cause instanceof RegionDestroyedException) {
|
||||
return new InvalidDataAccessResourceUsageException(cause.getMessage(), cause);
|
||||
}
|
||||
if (cause instanceof org.apache.geode.admin.RegionNotFoundException) {
|
||||
return new InvalidDataAccessResourceUsageException(cause.getMessage(), cause);
|
||||
}
|
||||
if (cause instanceof ResourceException) {
|
||||
return new DataAccessResourceFailureException(cause.getMessage(), cause);
|
||||
}
|
||||
if (cause instanceof RoleException) {
|
||||
return new GemfireSystemException(cause);
|
||||
}
|
||||
if (cause instanceof StatisticsDisabledException) {
|
||||
return new GemfireSystemException(cause);
|
||||
}
|
||||
if (cause instanceof SynchronizationCommitConflictException) {
|
||||
return new PessimisticLockingFailureException(cause.getMessage(), cause);
|
||||
}
|
||||
}
|
||||
|
||||
if (cause instanceof CopyException) {
|
||||
return new GemfireSystemException(cause);
|
||||
}
|
||||
if (cause instanceof FunctionException) {
|
||||
return new InvalidDataAccessApiUsageException(cause.getMessage(), cause);
|
||||
}
|
||||
if (cause instanceof GemFireCacheException) {
|
||||
return convertGemfireAccessException(((GemFireCacheException) cause).getCacheException());
|
||||
}
|
||||
if (cause instanceof GemFireConfigException) {
|
||||
return new GemfireSystemException(cause);
|
||||
}
|
||||
if (cause instanceof GemFireIOException) {
|
||||
return new DataAccessResourceFailureException(cause.getMessage(), cause);
|
||||
}
|
||||
if (cause instanceof GemFireSecurityException) {
|
||||
return new PermissionDeniedDataAccessException(cause.getMessage(), cause);
|
||||
}
|
||||
if (cause instanceof IncompatibleSystemException) {
|
||||
return new GemfireSystemException(cause);
|
||||
}
|
||||
if (cause instanceof InternalGemFireException) {
|
||||
return new GemfireSystemException(cause);
|
||||
}
|
||||
if (cause instanceof InvalidValueException) {
|
||||
return new TypeMismatchDataAccessException(cause.getMessage(), cause);
|
||||
}
|
||||
if (cause instanceof LeaseExpiredException) {
|
||||
return new PessimisticLockingFailureException(cause.getMessage(), cause);
|
||||
}
|
||||
if (cause instanceof NoSystemException) {
|
||||
return new GemfireSystemException(cause);
|
||||
}
|
||||
if (cause instanceof org.apache.geode.admin.RuntimeAdminException) {
|
||||
return new GemfireSystemException(cause);
|
||||
}
|
||||
if (cause instanceof ServerConnectivityException) {
|
||||
return new DataAccessResourceFailureException(cause.getMessage(), cause);
|
||||
}
|
||||
if (cause instanceof SystemConnectException) {
|
||||
return new DataAccessResourceFailureException(cause.getMessage(), cause);
|
||||
}
|
||||
if (cause instanceof SystemIsRunningException) {
|
||||
return new GemfireSystemException(cause);
|
||||
}
|
||||
if (cause instanceof UnmodifiableException) {
|
||||
return new GemfireSystemException(cause);
|
||||
}
|
||||
|
||||
// for exceptions that had their parent changed in 6.5
|
||||
DataAccessException dataAccessException = convertQueryExceptions(cause);
|
||||
|
||||
if (dataAccessException != null) {
|
||||
return dataAccessException;
|
||||
}
|
||||
|
||||
return new GemfireSystemException(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given (checked) Gemfire exception to an appropriate one from the
|
||||
* <code>org.springframework.dao</code> hierarchy.
|
||||
*
|
||||
* @param cause Gemfire unchecked exception
|
||||
* @return new the corresponding DataAccessException instance
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public static DataAccessException convertGemfireAccessException(GemFireCheckedException cause) {
|
||||
|
||||
if (cause instanceof AdminException) {
|
||||
return new GemfireSystemException(cause);
|
||||
}
|
||||
|
||||
if (cause instanceof QueryException) {
|
||||
return new GemfireQueryException((QueryException) cause);
|
||||
}
|
||||
|
||||
if (cause instanceof VersionException) {
|
||||
return new DataAccessResourceFailureException(cause.getMessage(), cause);
|
||||
}
|
||||
|
||||
return new GemfireSystemException(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given (unchecked) Gemfire exception to an appropriate one from the
|
||||
* <code>org.springframework.dao</code> hierarchy. This method exists to handle backwards compatibility
|
||||
* for exceptions that had their parents changed in GemFire 6.5.
|
||||
*
|
||||
* @param cause Gemfire unchecked exception
|
||||
* @return new the corresponding DataAccessException instance
|
||||
*/
|
||||
public static DataAccessException convertGemfireAccessException(IndexInvalidException cause) {
|
||||
return new GemfireIndexException(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given (unchecked) Gemfire exception to an appropriate one from the
|
||||
* <code>org.springframework.dao</code> hierarchy. This method exists to handle backwards compatibility
|
||||
* for exceptions that had their parents changed in GemFire 6.5.
|
||||
*
|
||||
* @param cause Gemfire unchecked exception
|
||||
* @return new the corresponding DataAccessException instance
|
||||
*/
|
||||
public static DataAccessException convertGemfireAccessException(QueryInvalidException cause) {
|
||||
return new GemfireQueryException(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Package protected method for detecting CqInvalidException which has been removed in GemFire 6.5 GA.
|
||||
*/
|
||||
static boolean isCqInvalidException(RuntimeException cause) {
|
||||
return CQ_EXCEPTION_CLASS != null && CQ_EXCEPTION_CLASS.isInstance(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dedicated method for converting exceptions changed in 6.5 that had their
|
||||
* parent changed. This method exists to 'fool' the compiler type checks
|
||||
* by loosening the type so the code compiles on both 6.5 (pre and current) branches.
|
||||
*/
|
||||
static DataAccessException convertQueryExceptions(RuntimeException cause) {
|
||||
|
||||
if (cause instanceof IndexInvalidException) {
|
||||
return convertGemfireAccessException((IndexInvalidException) cause);
|
||||
}
|
||||
|
||||
if (cause instanceof QueryInvalidException) {
|
||||
return convertGemfireAccessException((QueryInvalidException) cause);
|
||||
}
|
||||
|
||||
if (isCqInvalidException(cause)) {
|
||||
return convertCqInvalidException(cause);
|
||||
}
|
||||
|
||||
return new GemfireSystemException(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given (unchecked) Gemfire exception to an appropriate one from the
|
||||
* <code>org.springframework.dao</code> hierarchy. This method exists to handle backwards compatibility
|
||||
* for exceptions that have been removed in 6.5.
|
||||
*
|
||||
* @param cause Gemfire unchecked exception
|
||||
* @return new the corresponding DataAccessException instance
|
||||
*/
|
||||
static DataAccessException convertCqInvalidException(RuntimeException cause) {
|
||||
return new GemfireQueryException(cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import org.apache.geode.GemFireCheckedException;
|
||||
import org.apache.geode.GemFireException;
|
||||
import org.apache.geode.cache.Region;
|
||||
|
||||
/**
|
||||
* Callback interface for GemFire code. To be used with {@link GemfireTemplate}'s execution methods, often as anonymous
|
||||
* classes within a method implementation. A typical implementation will call Region.get/put/query to perform some
|
||||
* operations on stored objects.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author John Blum
|
||||
* @see org.apache.geode.cache.Region
|
||||
*/
|
||||
public interface GemfireCallback<T> {
|
||||
|
||||
/**
|
||||
* Gets called by {@link GemfireTemplate#execute(GemfireCallback)}. Does not need to care about
|
||||
* handling transactions or exceptions.
|
||||
*
|
||||
* Allows a result object created within the callback to be returned, i.e. a domain object
|
||||
* or a collection of domain objects.
|
||||
*
|
||||
* A thrown custom RuntimeException is treated as an application exception: it gets propagated to
|
||||
* the caller of the template.
|
||||
*
|
||||
* @param region the GemFire Cache Region upon which the operation of this callback will be performed.
|
||||
* @return a result object, or <tt>null</tt> if no result.
|
||||
* @throws GemFireCheckedException for checked Exceptions occurring in GemFire.
|
||||
* @throws GemFireException for runtime Exceptions occurring in GemFire.
|
||||
* @see org.springframework.data.gemfire.GemfireTemplate
|
||||
* @see org.apache.geode.cache.Region
|
||||
*/
|
||||
T doInGemfire(Region<?,?> region) throws GemFireCheckedException, GemFireException;
|
||||
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import org.apache.geode.CancelException;
|
||||
|
||||
import org.springframework.dao.InvalidDataAccessResourceUsageException;
|
||||
|
||||
/**
|
||||
* GemFire-specific class for exceptions caused by system cancellations.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class GemfireCancellationException extends InvalidDataAccessResourceUsageException {
|
||||
|
||||
public GemfireCancellationException(CancelException ex) {
|
||||
super(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import org.apache.geode.cache.query.IndexCreationException;
|
||||
import org.apache.geode.cache.query.IndexExistsException;
|
||||
import org.apache.geode.cache.query.IndexInvalidException;
|
||||
import org.apache.geode.cache.query.IndexMaintenanceException;
|
||||
import org.apache.geode.cache.query.IndexNameConflictException;
|
||||
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
|
||||
/**
|
||||
* Gemfire-specific subclass thrown on Index management.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author John Blum
|
||||
* @see org.apache.geode.cache.query.IndexCreationException
|
||||
* @see org.apache.geode.cache.query.IndexExistsException
|
||||
* @see org.apache.geode.cache.query.IndexInvalidException
|
||||
* @see org.apache.geode.cache.query.IndexMaintenanceException
|
||||
* @see org.apache.geode.cache.query.IndexNameConflictException
|
||||
*/
|
||||
@SuppressWarnings({ "serial", "unused" })
|
||||
public class GemfireIndexException extends DataIntegrityViolationException {
|
||||
|
||||
public GemfireIndexException(Exception cause) {
|
||||
this(cause.getMessage(), cause);
|
||||
}
|
||||
|
||||
public GemfireIndexException(String message, Exception cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public GemfireIndexException(IndexCreationException cause) {
|
||||
this(cause.getMessage(), cause);
|
||||
}
|
||||
|
||||
public GemfireIndexException(String message, IndexCreationException cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public GemfireIndexException(IndexExistsException cause) {
|
||||
this(cause.getMessage(), cause);
|
||||
}
|
||||
|
||||
public GemfireIndexException(String message, IndexExistsException cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public GemfireIndexException(IndexInvalidException cause) {
|
||||
this(cause.getMessage(), cause);
|
||||
}
|
||||
|
||||
public GemfireIndexException(String message, IndexInvalidException cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public GemfireIndexException(IndexMaintenanceException cause) {
|
||||
this(cause.getMessage(), cause);
|
||||
}
|
||||
|
||||
public GemfireIndexException(String message, IndexMaintenanceException cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public GemfireIndexException(IndexNameConflictException cause) {
|
||||
this(cause.getMessage(), cause);
|
||||
}
|
||||
|
||||
public GemfireIndexException(String message, IndexNameConflictException cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.geode.cache.Region;
|
||||
import org.apache.geode.cache.query.Query;
|
||||
import org.apache.geode.cache.query.QueryService;
|
||||
import org.apache.geode.cache.query.SelectResults;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
|
||||
/**
|
||||
* @author David Turanski
|
||||
* @author John Blum
|
||||
*/
|
||||
public interface GemfireOperations {
|
||||
|
||||
boolean containsKey(Object key);
|
||||
|
||||
boolean containsKeyOnServer(Object key);
|
||||
|
||||
boolean containsValue(Object value);
|
||||
|
||||
boolean containsValueForKey(Object key);
|
||||
|
||||
<K, V> void create(K key, V value);
|
||||
|
||||
<K, V> V get(K key);
|
||||
|
||||
<K, V> Map<K, V> getAll(Collection<?> keys);
|
||||
|
||||
<K, V> V put(K key, V value);
|
||||
|
||||
<K, V> void putAll(Map<? extends K, ? extends V> map);
|
||||
|
||||
<K, V> V putIfAbsent(K key, V value);
|
||||
|
||||
<K, V> V remove(K key);
|
||||
|
||||
<K, V> V replace(K key, V value);
|
||||
|
||||
<K, V> boolean replace(K key, V oldValue, V newValue);
|
||||
|
||||
/**
|
||||
* Executes a GemFire query with the given (optional) parameters and returns the result. Note this method expects the query to return multiple results; for queries that return only one
|
||||
* element use {@link #findUnique(String, Object...)}.
|
||||
*
|
||||
* As oppose, to the {@link #query(String)} method, this method allows for more generic queries (against multiple regions even) to be executed.
|
||||
*
|
||||
* Note that the local query service is used if the region is configured as a client without any pool configuration or server connectivity - otherwise the query service on the default pool
|
||||
* is being used.
|
||||
*
|
||||
* @param <E> type parameter specifying the type of the select results.
|
||||
* @param query the OQL query statement to execute.
|
||||
* @param params an array of Object values used as arguments to bind to the OQL query parameters (such as $1).
|
||||
* @return A {@link SelectResults} instance holding the objects matching the query
|
||||
* @throws InvalidDataAccessApiUsageException in case the query returns a single result (not a {@link SelectResults}).
|
||||
* @see QueryService#newQuery(String)
|
||||
* @see Query#execute(Object[])
|
||||
* @see SelectResults
|
||||
*/
|
||||
<E> SelectResults<E> find(String query, Object... params) throws InvalidDataAccessApiUsageException;
|
||||
|
||||
/**
|
||||
* Executes a GemFire query with the given (optional) parameters and returns the result. Note this method expects the query to return a single result; for queries that return multiple
|
||||
* elements use {@link #find(String, Object...)}.
|
||||
*
|
||||
* As oppose, to the {@link #query(String)} method, this method allows for more generic queries (against multiple regions even) to be executed.
|
||||
*
|
||||
* Note that the local query service is used if the region is configured as a client without any pool configuration or server connectivity - otherwise the query service on the default pool
|
||||
* is being used.
|
||||
*
|
||||
* @param <T> type parameter specifying the returned result type.
|
||||
* @param query the OQL query statement to execute.
|
||||
* @param params an array of Object values used as arguments to bind to the OQL query parameters (such as $1).
|
||||
* @return The (single) object that represents the result of the query.
|
||||
* @throws InvalidDataAccessApiUsageException in case the query returns multiple objects (through {@link SelectResults}).
|
||||
* @see QueryService#newQuery(String)
|
||||
* @see Query#execute(Object[])
|
||||
*/
|
||||
<T> T findUnique(String query, Object... params) throws InvalidDataAccessApiUsageException;
|
||||
|
||||
/**
|
||||
* Shortcut for {@link Region#query(String)} method. Filters the values of this region using the predicate given as a string with the syntax of the WHERE clause of the query language.
|
||||
* The predefined variable this may be used inside the predicate to denote the current element being filtered.
|
||||
* This method evaluates the passed in where clause and returns results. It is supported on servers as well as clients.
|
||||
* When executed on a client, this method always runs on the server and returns results.
|
||||
* When invoking this method from the client, applications can pass in a where clause or a complete query.
|
||||
*
|
||||
* @param <E> type parameter specifying the type of the select results.
|
||||
* @param query an OQL Query language boolean query predicate.
|
||||
* @return A SelectResults containing the values of this Region that match the predicate.
|
||||
* @see Region#query(String)
|
||||
*/
|
||||
<E> SelectResults<E> query(String query);
|
||||
|
||||
/**
|
||||
* Execute the action specified by the given action object within a Region.
|
||||
*
|
||||
* @param <T> type parameter specifying the returned result type.
|
||||
* @param action callback object that specifies the Gemfire action to execute.
|
||||
* @return a result object returned by the action, or <code>null</code>.
|
||||
* @throws org.springframework.dao.DataAccessException in case of GemFire errors.
|
||||
*/
|
||||
<T> T execute(GemfireCallback<T> action) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute the action specified by the given action object within a Region.
|
||||
*
|
||||
* @param <T> type parameter specifying the returned result type.
|
||||
* @param action callback object that specifies the Gemfire action to execute.
|
||||
* @param exposeNativeRegion whether to expose the native GemFire region to callback code.
|
||||
* @return a result object returned by the action, or <code>null</code>.
|
||||
* @throws org.springframework.dao.DataAccessException in case of GemFire errors.
|
||||
*/
|
||||
<T> T execute(GemfireCallback<T> action, boolean exposeNativeRegion) throws DataAccessException;
|
||||
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import org.apache.geode.cache.query.QueryException;
|
||||
import org.apache.geode.cache.query.QueryExecutionTimeoutException;
|
||||
import org.apache.geode.cache.query.QueryInvalidException;
|
||||
|
||||
import org.springframework.dao.InvalidDataAccessResourceUsageException;
|
||||
|
||||
/**
|
||||
* GemFire-specific subclass of {@link InvalidDataAccessResourceUsageException} thrown on invalid
|
||||
* OQL query syntax.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class GemfireQueryException extends InvalidDataAccessResourceUsageException {
|
||||
|
||||
public GemfireQueryException(String message, QueryException ex) {
|
||||
super(message, ex);
|
||||
}
|
||||
|
||||
public GemfireQueryException(QueryException ex) {
|
||||
super(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
public GemfireQueryException(String message, QueryExecutionTimeoutException ex) {
|
||||
super(message, ex);
|
||||
}
|
||||
|
||||
public GemfireQueryException(QueryExecutionTimeoutException ex) {
|
||||
super(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
public GemfireQueryException(String message, QueryInvalidException ex) {
|
||||
super(message, ex);
|
||||
}
|
||||
|
||||
public GemfireQueryException(QueryInvalidException ex) {
|
||||
super(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
public GemfireQueryException(String message, RuntimeException ex) {
|
||||
super(message, ex);
|
||||
}
|
||||
|
||||
public GemfireQueryException(RuntimeException ex) {
|
||||
super(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import org.apache.geode.GemFireCheckedException;
|
||||
import org.apache.geode.GemFireException;
|
||||
|
||||
import org.springframework.dao.UncategorizedDataAccessException;
|
||||
|
||||
/**
|
||||
* GemFire-specific subclass of UncategorizedDataAccessException, for GemFire system errors that do not match any concrete <code>org.springframework.dao</code> exceptions.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class GemfireSystemException extends UncategorizedDataAccessException {
|
||||
|
||||
public GemfireSystemException(GemFireCheckedException ex) {
|
||||
super(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
public GemfireSystemException(GemFireException ex) {
|
||||
super(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
public GemfireSystemException(RuntimeException ex) {
|
||||
super(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
@@ -1,543 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.geode.GemFireCheckedException;
|
||||
import org.apache.geode.GemFireException;
|
||||
import org.apache.geode.cache.Region;
|
||||
import org.apache.geode.cache.Scope;
|
||||
import org.apache.geode.cache.client.ClientCache;
|
||||
import org.apache.geode.cache.query.IndexInvalidException;
|
||||
import org.apache.geode.cache.query.Query;
|
||||
import org.apache.geode.cache.query.QueryInvalidException;
|
||||
import org.apache.geode.cache.query.QueryService;
|
||||
import org.apache.geode.cache.query.SelectResults;
|
||||
import org.apache.geode.internal.cache.LocalRegion;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Helper class that simplifies GemFire data access code and converts {@link GemFireCheckedException} and
|
||||
* {@link GemFireException} into Spring {@link DataAccessException}, following the <tt>org.springframework.dao</tt>
|
||||
* exception hierarchy.
|
||||
*
|
||||
* The central method is <tt>execute</tt>, supporting GemFire access code implementing the GemfireCallback interface.
|
||||
* It provides dedicated handling such that neither the GemfireCallback implementation nor the calling code needs to
|
||||
* explicitly care about handling {@link Region} life-cycle exceptions.
|
||||
* Typically used to implement data access or business logic services that use GemFire within their implementation but
|
||||
* are GemFire-agnostic in their interface. The latter or code calling the latter only have to deal with business
|
||||
* objects, query objects, and <tt>org.springframework.dao</tt> exceptions.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author John Blum
|
||||
* @see java.util.Map
|
||||
* @see org.springframework.data.gemfire.GemfireAccessor
|
||||
* @see org.springframework.data.gemfire.GemfireOperations
|
||||
* @see org.apache.geode.cache.Region
|
||||
* @see org.apache.geode.cache.query.Query
|
||||
* @see org.apache.geode.cache.query.QueryService
|
||||
* @see org.apache.geode.cache.query.SelectResults
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class GemfireTemplate extends GemfireAccessor implements GemfireOperations {
|
||||
|
||||
private boolean exposeNativeRegion = false;
|
||||
|
||||
private Region<?, ?> regionProxy;
|
||||
|
||||
public GemfireTemplate() { }
|
||||
|
||||
public <K, V> GemfireTemplate(Region<K, V> region) {
|
||||
setRegion(region);
|
||||
afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
|
||||
super.afterPropertiesSet();
|
||||
|
||||
this.regionProxy = createRegionProxy(getRegion());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether to expose the native Gemfire Region to GemfireCallback code. Default is "false": a Region proxy
|
||||
* will be returned, suppressing <code>close</code> calls.
|
||||
* <p>As there is often a need to cast to a interface, the exposed proxy implements all interfaces
|
||||
* implemented by the original {@link Region}. If this is not sufficient, turn this flag to "true".
|
||||
*
|
||||
* @param exposeNativeRegion a boolean value to indicate whether the native GemFire Cache Region should be exposed
|
||||
* to the GemfireCallback.
|
||||
* @see org.springframework.data.gemfire.GemfireCallback
|
||||
*/
|
||||
public void setExposeNativeRegion(boolean exposeNativeRegion) {
|
||||
this.exposeNativeRegion = exposeNativeRegion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether to expose the native GemFire Cache Region or a Region proxy to the GemfireCallback code.
|
||||
*
|
||||
* @return a boolean value indicating whether the native GemFire Cache Region or Region proxy is exposed
|
||||
* to the GemfireCallback code.
|
||||
*/
|
||||
public boolean isExposeNativeRegion() {
|
||||
return this.exposeNativeRegion;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.gemfire.GemfireOperations#containsKey(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return getRegion().containsKey(key);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.gemfire.GemfireOperations#containsKeyOnServer(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean containsKeyOnServer(Object key) {
|
||||
return getRegion().containsKeyOnServer(key);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.gemfire.GemfireOperations#containsValue(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
return getRegion().containsValue(value);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.gemfire.GemfireOperations#containsValueForKey(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean containsValueForKey(Object key) {
|
||||
return getRegion().containsValueForKey(key);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.gemfire.GemfireOperations#create(K, V)
|
||||
*/
|
||||
@Override
|
||||
public <K, V> void create(K key, V value) {
|
||||
|
||||
try {
|
||||
getRegion().create(key, value);
|
||||
}
|
||||
catch (GemFireException cause) {
|
||||
throw convertGemFireAccessException(cause);
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.gemfire.GemfireOperations#get(K)
|
||||
*/
|
||||
@Override
|
||||
public <K, V> V get(K key) {
|
||||
|
||||
try {
|
||||
return this.<K, V>getRegion().get(key);
|
||||
}
|
||||
catch (GemFireException cause) {
|
||||
throw convertGemFireAccessException(cause);
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.gemfire.GemfireOperations#getAll(java.util.Collection)
|
||||
*/
|
||||
@Override
|
||||
public <K, V> Map<K, V> getAll(Collection<?> keys) {
|
||||
|
||||
try {
|
||||
return this.<K, V>getRegion().getAll(keys);
|
||||
}
|
||||
catch (GemFireException cause) {
|
||||
throw convertGemFireAccessException(cause);
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.gemfire.GemfireOperations#put(K, V)
|
||||
*/
|
||||
@Override
|
||||
public <K, V> V put(K key, V value) {
|
||||
|
||||
try {
|
||||
return this.<K, V>getRegion().put(key, value);
|
||||
}
|
||||
catch (GemFireException cause) {
|
||||
throw convertGemFireAccessException(cause);
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.gemfire.GemfireOperations#putAll(java.util.Map)
|
||||
*/
|
||||
@Override
|
||||
public <K, V> void putAll(Map<? extends K, ? extends V> map) {
|
||||
|
||||
try {
|
||||
this.<K, V>getRegion().putAll(map);
|
||||
}
|
||||
catch (GemFireException cause) {
|
||||
throw convertGemFireAccessException(cause);
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.gemfire.GemfireOperations#putIfAbsent(K, V)
|
||||
*/
|
||||
@Override
|
||||
public <K, V> V putIfAbsent(K key, V value) {
|
||||
|
||||
try {
|
||||
return this.<K, V>getRegion().putIfAbsent(key, value);
|
||||
}
|
||||
catch (GemFireException cause) {
|
||||
throw convertGemFireAccessException(cause);
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.gemfire.GemfireOperations#remove(K)
|
||||
*/
|
||||
@Override
|
||||
public <K, V> V remove(K key) {
|
||||
|
||||
try {
|
||||
return this.<K, V>getRegion().remove(key);
|
||||
}
|
||||
catch (GemFireException cause) {
|
||||
throw convertGemFireAccessException(cause);
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.gemfire.GemfireOperations#replace(K, V)
|
||||
*/
|
||||
@Override
|
||||
public <K, V> V replace(K key, V value) {
|
||||
|
||||
try {
|
||||
return this.<K, V>getRegion().replace(key, value);
|
||||
}
|
||||
catch (GemFireException cause) {
|
||||
throw convertGemFireAccessException(cause);
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.gemfire.GemfireOperations#replace(K, V, V)
|
||||
*/
|
||||
@Override
|
||||
public <K, V> boolean replace(K key, V oldValue, V newValue) {
|
||||
|
||||
try {
|
||||
return this.<K, V>getRegion().replace(key, oldValue, newValue);
|
||||
}
|
||||
catch (GemFireException cause) {
|
||||
throw convertGemFireAccessException(cause);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.gemfire.GemfireOperations#query(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public <E> SelectResults<E> query(String query) {
|
||||
|
||||
try {
|
||||
return this.getRegion().query(query);
|
||||
}
|
||||
catch (IndexInvalidException | QueryInvalidException cause) {
|
||||
throw convertGemFireQueryException(cause);
|
||||
}
|
||||
catch (GemFireCheckedException cause) {
|
||||
throw convertGemFireAccessException(cause);
|
||||
}
|
||||
catch (GemFireException cause) {
|
||||
throw convertGemFireAccessException(cause);
|
||||
}
|
||||
catch (RuntimeException cause) {
|
||||
|
||||
if (GemfireCacheUtils.isCqInvalidException(cause)) {
|
||||
throw GemfireCacheUtils.convertCqInvalidException(cause);
|
||||
}
|
||||
|
||||
throw cause;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.gemfire.GemfireOperations#find(java.lang.String, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <E> SelectResults<E> find(String queryString, Object... params) throws InvalidDataAccessApiUsageException {
|
||||
|
||||
try {
|
||||
|
||||
QueryService queryService = resolveQueryService(getRegion());
|
||||
Query query = queryService.newQuery(queryString);
|
||||
Object result = query.execute(params);
|
||||
|
||||
if (result instanceof SelectResults) {
|
||||
return (SelectResults<E>) result;
|
||||
}
|
||||
else {
|
||||
throw new InvalidDataAccessApiUsageException(String.format(
|
||||
"The result from executing query [%1$s] was not an instance of SelectResults [%2$s]",
|
||||
queryString, result));
|
||||
}
|
||||
}
|
||||
catch (IndexInvalidException | QueryInvalidException cause) {
|
||||
throw convertGemFireQueryException(cause);
|
||||
}
|
||||
catch (GemFireCheckedException cause) {
|
||||
throw convertGemFireAccessException(cause);
|
||||
}
|
||||
catch (GemFireException caue) {
|
||||
throw convertGemFireAccessException(caue);
|
||||
}
|
||||
catch (RuntimeException cause) {
|
||||
|
||||
if (GemfireCacheUtils.isCqInvalidException(cause)) {
|
||||
throw GemfireCacheUtils.convertCqInvalidException(cause);
|
||||
}
|
||||
|
||||
throw cause;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.gemfire.GemfireOperations#findUnique(java.lang.String, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T findUnique(String queryString, Object... params) throws InvalidDataAccessApiUsageException {
|
||||
|
||||
try {
|
||||
|
||||
QueryService queryService = resolveQueryService(getRegion());
|
||||
Query query = queryService.newQuery(queryString);
|
||||
Object result = query.execute(params);
|
||||
|
||||
if (result instanceof SelectResults) {
|
||||
|
||||
SelectResults<T> selectResults = (SelectResults<T>) result;
|
||||
|
||||
List<T> results = selectResults.asList();
|
||||
|
||||
if (results.size() == 1) {
|
||||
result = results.get(0);
|
||||
}
|
||||
else {
|
||||
throw new InvalidDataAccessApiUsageException(String.format(
|
||||
"The result returned from query [%1$s]) was not unique [%2$s]", queryString, result));
|
||||
}
|
||||
}
|
||||
|
||||
return (T) result;
|
||||
}
|
||||
catch (IndexInvalidException | QueryInvalidException cause) {
|
||||
throw convertGemFireQueryException(cause);
|
||||
}
|
||||
catch (GemFireCheckedException cause) {
|
||||
throw convertGemFireAccessException(cause);
|
||||
}
|
||||
catch (GemFireException cause) {
|
||||
throw convertGemFireAccessException(cause);
|
||||
}
|
||||
catch (RuntimeException cause) {
|
||||
|
||||
if (GemfireCacheUtils.isCqInvalidException(cause)) {
|
||||
throw GemfireCacheUtils.convertCqInvalidException(cause);
|
||||
}
|
||||
|
||||
throw cause;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link QueryService} used by this template in its query/finder methods.
|
||||
*
|
||||
* @param region {@link Region} used to acquire the {@link QueryService}.
|
||||
* @return the {@link QueryService} that will perform the query.
|
||||
* @see org.apache.geode.cache.Region
|
||||
* @see org.apache.geode.cache.Region#getRegionService()
|
||||
* @see org.apache.geode.cache.RegionService#getQueryService()
|
||||
* @see org.apache.geode.cache.client.ClientCache#getLocalQueryService()
|
||||
*/
|
||||
protected QueryService resolveQueryService(Region<?, ?> region) {
|
||||
|
||||
return region.getRegionService() instanceof ClientCache
|
||||
? resolveClientQueryService(region)
|
||||
: queryServiceFrom(region);
|
||||
}
|
||||
|
||||
QueryService resolveClientQueryService(Region<?, ?> region) {
|
||||
|
||||
ClientCache clientCache = (ClientCache) region.getRegionService();
|
||||
|
||||
return requiresLocalQueryService(region) ? clientCache.getLocalQueryService()
|
||||
: (requiresPooledQueryService(region) ? clientCache.getQueryService(poolNameFrom(region))
|
||||
: queryServiceFrom(region));
|
||||
}
|
||||
|
||||
boolean requiresLocalQueryService(Region<?, ?> region) {
|
||||
return Scope.LOCAL.equals(region.getAttributes().getScope()) && isLocalWithNoServerProxy(region);
|
||||
}
|
||||
|
||||
boolean isLocalWithNoServerProxy(Region<?, ?> region) {
|
||||
return region instanceof LocalRegion && !((LocalRegion) region).hasServerProxy();
|
||||
}
|
||||
|
||||
boolean requiresPooledQueryService(Region<?, ?> region) {
|
||||
return StringUtils.hasText(poolNameFrom(region));
|
||||
}
|
||||
|
||||
QueryService queryServiceFrom(Region<?, ?> region) {
|
||||
return region.getRegionService().getQueryService();
|
||||
}
|
||||
|
||||
String poolNameFrom(Region<?, ?> region) {
|
||||
return region.getAttributes().getPoolName();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.gemfire.GemfireOperations#execute(org.springframework.data.gemfire.GemfireCallback)
|
||||
*/
|
||||
@Override
|
||||
public <T> T execute(GemfireCallback<T> action) throws DataAccessException {
|
||||
return execute(action, isExposeNativeRegion());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.gemfire.GemfireOperations#execute(org.springframework.data.gemfire.GemfireCallback, boolean)
|
||||
*/
|
||||
@Override
|
||||
public <T> T execute(GemfireCallback<T> action, boolean exposeNativeRegion) throws DataAccessException {
|
||||
|
||||
Assert.notNull(action, "Callback object must not be null");
|
||||
|
||||
try {
|
||||
|
||||
Region<?, ?> regionArgument = (exposeNativeRegion ? getRegion() : regionProxy);
|
||||
|
||||
return action.doInGemfire(regionArgument);
|
||||
}
|
||||
catch (IndexInvalidException | QueryInvalidException cause) {
|
||||
throw convertGemFireQueryException(cause);
|
||||
}
|
||||
catch (GemFireCheckedException cause) {
|
||||
throw convertGemFireAccessException(cause);
|
||||
}
|
||||
catch (GemFireException cause) {
|
||||
throw convertGemFireAccessException(cause);
|
||||
}
|
||||
catch (RuntimeException cause) {
|
||||
|
||||
if (GemfireCacheUtils.isCqInvalidException(cause)) {
|
||||
throw GemfireCacheUtils.convertCqInvalidException(cause);
|
||||
}
|
||||
|
||||
throw cause;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a close-suppressing proxy for the given GemFire Cache {@link Region}.
|
||||
* Called by the <code>execute</code> method.
|
||||
*
|
||||
* @param <K> the Region key class type.
|
||||
* @param <V> the Region value class type.
|
||||
* @param region the GemFire Cache Region to create a proxy for.
|
||||
* @return the Region proxy implementing all interfaces implemented by the passed-in Region object.
|
||||
* @see org.apache.geode.cache.Region#close()
|
||||
* @see #execute(GemfireCallback, boolean)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <K, V> Region<K, V> createRegionProxy(Region<K, V> region) {
|
||||
|
||||
Class<?> regionType = region.getClass();
|
||||
|
||||
return (Region<K, V>) Proxy.newProxyInstance(regionType.getClassLoader(),
|
||||
ClassUtils.getAllInterfacesForClass(regionType, getClass().getClassLoader()),
|
||||
new RegionCloseSuppressingInvocationHandler(region));
|
||||
}
|
||||
|
||||
/**
|
||||
* InvocationHandler that suppresses close calls on GemFire Cache Regions.
|
||||
*
|
||||
* @see org.apache.geode.cache.Region#close()
|
||||
* @see java.lang.reflect.InvocationHandler
|
||||
*/
|
||||
private static class RegionCloseSuppressingInvocationHandler implements InvocationHandler {
|
||||
|
||||
private final Region<?, ?> target;
|
||||
|
||||
public RegionCloseSuppressingInvocationHandler(Region<?, ?> target) {
|
||||
|
||||
Assert.notNull(target, "Target Region must not be null");
|
||||
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
|
||||
if ("equals".equals(method.getName())) {
|
||||
// only consider equal when proxies are identical
|
||||
return proxy == args[0];
|
||||
}
|
||||
else if ("hashCode".equals(method.getName())) {
|
||||
// use hashCode of Region proxy
|
||||
return System.identityHashCode(proxy);
|
||||
}
|
||||
else if ("close".equals(method.getName())) {
|
||||
// suppress Region.close()
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
try {
|
||||
return method.invoke(this.target, args);
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
throw ex.getTargetException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import org.apache.geode.cache.CacheFactory;
|
||||
import org.apache.geode.internal.GemFireVersion;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.data.gemfire.config.support.GemfireFeature;
|
||||
import org.springframework.data.gemfire.util.RegionUtils;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
<<<<<<< HEAD
|
||||
* {@link GemfireUtils} is an abstract utility class encapsulating common functionality to access features
|
||||
* and capabilities of GemFire based on version and other configuration meta-data.
|
||||
=======
|
||||
* {@link GemfireUtils} is an abstract utility class encapsulating common functionality for accessing features
|
||||
* and capabilities of Apache Geode or Pivotal GemFire based on version as well as other configuration meta-data.
|
||||
>>>>>>> 284e248b... SGF-826 - Move away from Spring Data Commons deprecations.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.apache.geode.cache.CacheFactory
|
||||
* @see org.apache.geode.internal.GemFireVersion
|
||||
* @see org.springframework.data.gemfire.config.support.GemfireFeature
|
||||
* @see org.springframework.data.gemfire.util.RegionUtils
|
||||
* @since 1.3.3
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class GemfireUtils extends RegionUtils {
|
||||
|
||||
public final static String APACHE_GEODE_NAME = "Apache Geode";
|
||||
public final static String GEMFIRE_NAME = apacheGeodeProductName();
|
||||
public final static String GEMFIRE_VERSION = apacheGeodeVersion();
|
||||
public final static String UNKNOWN = "unknown";
|
||||
|
||||
private static final String ASYNC_EVENT_QUEUE_ELEMENT_NAME = "async-event-queue";
|
||||
private static final String ASYNC_EVENT_QUEUE_TYPE_NAME = "org.apache.geode.cache.asyncqueue.AsyncEventQueue";
|
||||
private static final String CQ_ELEMENT_NAME = "cq-listener-container";
|
||||
private static final String CQ_TYPE_NAME = "org.apache.geode.cache.query.internal.cq.CqServiceFactoryImpl";
|
||||
private static final String GATEWAY_RECEIVER_ELEMENT_NAME = "gateway-receiver";
|
||||
private static final String GATEWAY_RECEIVER_TYPE_NAME = "org.apache.geode.internal.cache.wan.GatewayReceiverFactoryImpl";
|
||||
private static final String GATEWAY_SENDER_ELEMENT_NAME = "gateway-sender";
|
||||
private static final String GATEWAY_SENDER_TYPE_NAME = "org.apache.geode.internal.cache.wan.GatewaySenderFactoryImpl";
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static String apacheGeodeProductName() {
|
||||
|
||||
try {
|
||||
return GemFireVersion.getProductName();
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return APACHE_GEODE_NAME;
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static String apacheGeodeVersion() {
|
||||
|
||||
try {
|
||||
return CacheFactory.getVersion();
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static boolean isClassAvailable(String fullyQualifiedClassName) {
|
||||
return ClassUtils.isPresent(fullyQualifiedClassName, GemfireUtils.class.getClassLoader());
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static boolean isGemfireFeatureAvailable(GemfireFeature feature) {
|
||||
|
||||
boolean featureAvailable = (!GemfireFeature.AEQ.equals(feature) || isAsyncEventQueueAvailable());
|
||||
|
||||
featureAvailable &= (!GemfireFeature.CONTINUOUS_QUERY.equals(feature) || isContinuousQueryAvailable());
|
||||
featureAvailable &= (!GemfireFeature.WAN.equals(feature) || isGatewayAvailable());
|
||||
|
||||
return featureAvailable;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static boolean isGemfireFeatureAvailable(Element element) {
|
||||
|
||||
boolean featureAvailable = (!isAsyncEventQueue(element) || isAsyncEventQueueAvailable());
|
||||
|
||||
featureAvailable &= (!isContinuousQuery(element) || isContinuousQueryAvailable());
|
||||
featureAvailable &= (!isGateway(element) || isGatewayAvailable());
|
||||
|
||||
return featureAvailable;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static boolean isGemfireFeatureUnavailable(GemfireFeature feature) {
|
||||
return !isGemfireFeatureAvailable(feature);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static boolean isGemfireFeatureUnavailable(Element element) {
|
||||
return !isGemfireFeatureAvailable(element);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
private static boolean isAsyncEventQueue(Element element) {
|
||||
return ASYNC_EVENT_QUEUE_ELEMENT_NAME.equals(element.getLocalName());
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
private static boolean isAsyncEventQueueAvailable() {
|
||||
return isClassAvailable(ASYNC_EVENT_QUEUE_TYPE_NAME);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
private static boolean isContinuousQuery(Element element) {
|
||||
return CQ_ELEMENT_NAME.equals(element.getLocalName());
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
private static boolean isContinuousQueryAvailable() {
|
||||
return isClassAvailable(CQ_TYPE_NAME);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
private static boolean isGateway(Element element) {
|
||||
|
||||
String elementLocalName = element.getLocalName();
|
||||
|
||||
return (GATEWAY_RECEIVER_ELEMENT_NAME.equals(elementLocalName)
|
||||
|| GATEWAY_SENDER_ELEMENT_NAME.equals(elementLocalName));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
private static boolean isGatewayAvailable() {
|
||||
return isClassAvailable(GATEWAY_SENDER_TYPE_NAME);
|
||||
}
|
||||
|
||||
public static void main(final String... args) {
|
||||
System.out.printf("Product Name [%1$s] Version [%2$s]%n", GEMFIRE_NAME, GEMFIRE_VERSION);
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
/**
|
||||
* The GenericRegionFactoryBean class is an extension of the abstract, base PeerRegionFactoryBean class enabling developers
|
||||
* to define a GemFire Cache Region with defaults.
|
||||
*
|
||||
* The defaults for DataPolicy is NORMAL and Scope is DISTRIBUTED_NO_ACK, effectively creating a "non-replicate",
|
||||
* Distributed Region.
|
||||
*
|
||||
* This class enables developers to create various non-strongly-typed Regions (e.g. PARTITION, REPLICATE) based on
|
||||
* various combinations of the DataPolicy, Scope and Subscription settings as defined in the Region Types section
|
||||
* of the GemFire User Guide (see link below). How GemFire Regions receive and distribute entry updates
|
||||
* is defined in the Storage and Distribution Options section.
|
||||
*
|
||||
* Note, it is generally better to define strong-typed Regions (e.g. PARTITION with PartitionedRegionFactoryBean)
|
||||
* in your applications. However, different forms of distribution patterns and updates are desired
|
||||
* in certain use cases.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see PeerRegionFactoryBean
|
||||
* @link https://gemfire.docs.pivotal.io/latest/userguide/index.html#developing/region_options/region_types.html
|
||||
* @link https://gemfire.docs.pivotal.io/latest/userguide/index.html#developing/region_options/storage_distribution_options.html
|
||||
* @since 1.7.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class GenericRegionFactoryBean<K, V> extends PeerRegionFactoryBean<K, V> {
|
||||
|
||||
}
|
||||
@@ -1,955 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import static java.util.stream.StreamSupport.stream;
|
||||
import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
|
||||
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeCollection;
|
||||
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeIterable;
|
||||
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalArgumentException;
|
||||
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.apache.geode.cache.Region;
|
||||
import org.apache.geode.cache.RegionService;
|
||||
import org.apache.geode.cache.client.ClientCache;
|
||||
import org.apache.geode.cache.query.Index;
|
||||
import org.apache.geode.cache.query.IndexExistsException;
|
||||
import org.apache.geode.cache.query.IndexNameConflictException;
|
||||
import org.apache.geode.cache.query.IndexStatistics;
|
||||
import org.apache.geode.cache.query.QueryService;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.data.gemfire.config.annotation.IndexConfigurer;
|
||||
import org.springframework.data.gemfire.config.xml.GemfireConstants;
|
||||
import org.springframework.data.gemfire.support.AbstractFactoryBeanSupport;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Spring {@link FactoryBean} used to construct, configure and initialize {@link Index Indexes}
|
||||
* using a declarative approach.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author David Turanski
|
||||
* @author John Blum
|
||||
* @see org.apache.geode.cache.Region
|
||||
* @see org.apache.geode.cache.RegionService
|
||||
* @see org.apache.geode.cache.query.Index
|
||||
* @see org.apache.geode.cache.query.QueryService
|
||||
* @see org.springframework.beans.factory.FactoryBean
|
||||
* @see org.springframework.beans.factory.InitializingBean
|
||||
* @see org.springframework.beans.factory.config.ConfigurableBeanFactory
|
||||
* @see org.springframework.data.gemfire.IndexFactoryBean.IndexWrapper
|
||||
* @see org.springframework.data.gemfire.config.annotation.IndexConfigurer
|
||||
* @see org.springframework.data.gemfire.support.AbstractFactoryBeanSupport
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class IndexFactoryBean extends AbstractFactoryBeanSupport<Index> implements InitializingBean {
|
||||
|
||||
public static final String BASIC_INDEX_DEFINITION = "{ expression = '%1$s', from = '%2$s', type = %3$s }";
|
||||
|
||||
public static final String DETAILED_INDEX_DEFINITION =
|
||||
"{ name = '%1$s', expression = '%2$s', from = '%3$s', imports = '%4$s', type = %5$s }";
|
||||
|
||||
private boolean define = false;
|
||||
private boolean ignoreIfExists = false;
|
||||
private boolean override = false;
|
||||
|
||||
private Index index;
|
||||
|
||||
private IndexType indexType;
|
||||
|
||||
//@Autowired(required = false)
|
||||
private List<IndexConfigurer> indexConfigurers = Collections.emptyList();
|
||||
|
||||
private final IndexConfigurer compositeIndexConfigurer = new IndexConfigurer() {
|
||||
|
||||
@Override
|
||||
public void configure(String beanName, IndexFactoryBean bean) {
|
||||
nullSafeCollection(indexConfigurers).forEach(indexConfigurer -> indexConfigurer.configure(beanName, bean));
|
||||
}
|
||||
};
|
||||
|
||||
private QueryService queryService;
|
||||
|
||||
private RegionService cache;
|
||||
|
||||
private String expression;
|
||||
private String from;
|
||||
private String imports;
|
||||
private String indexName;
|
||||
private String name;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
|
||||
this.indexName = resolveIndexName();
|
||||
|
||||
applyIndexConfigurers(this.indexName);
|
||||
|
||||
this.cache = resolveCache();
|
||||
this.queryService = resolveQueryService();
|
||||
|
||||
assertIndexDefinitionConfiguration();
|
||||
|
||||
this.index = createIndex(this.queryService, this.indexName);
|
||||
|
||||
registerAlias(getBeanName(), this.indexName);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
private void applyIndexConfigurers(String indexName) {
|
||||
applyIndexConfigurers(indexName, getCompositeRegionConfigurer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe operation to apply the given array of {@link IndexConfigurer IndexConfigurers}
|
||||
* to this {@link IndexFactoryBean}.
|
||||
*
|
||||
* @param indexName {@link String} containing the name of the {@link Index}.
|
||||
* @param indexConfigurers array of {@link IndexConfigurer IndexConfigurers} applied
|
||||
* to this {@link IndexFactoryBean}.
|
||||
* @see org.springframework.data.gemfire.config.annotation.RegionConfigurer
|
||||
* @see #applyIndexConfigurers(String, Iterable)
|
||||
*/
|
||||
protected void applyIndexConfigurers(String indexName, IndexConfigurer... indexConfigurers) {
|
||||
applyIndexConfigurers(indexName, Arrays.asList(nullSafeArray(indexConfigurers, IndexConfigurer.class)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe operation to apply the given {@link Iterable} of {@link IndexConfigurer IndexConfigurers}
|
||||
* to this {@link IndexFactoryBean}.
|
||||
*
|
||||
* @param indexName {@link String} containing the name of the {@link Index}.
|
||||
* @param indexConfigurers {@link Iterable} of {@link IndexConfigurer IndexConfigurers} applied
|
||||
* to this {@link IndexFactoryBean}.
|
||||
* @see org.springframework.data.gemfire.config.annotation.RegionConfigurer
|
||||
*/
|
||||
protected void applyIndexConfigurers(String indexName, Iterable<IndexConfigurer> indexConfigurers) {
|
||||
stream(nullSafeIterable(indexConfigurers).spliterator(), false)
|
||||
.forEach(indexConfigurer -> indexConfigurer.configure(indexName, this));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
private void assertIndexDefinitionConfiguration() {
|
||||
|
||||
Assert.hasText(this.expression, "Index expression is required");
|
||||
Assert.hasText(this.from, "Index from clause is required");
|
||||
|
||||
if (IndexType.isKey(this.indexType)) {
|
||||
Assert.isTrue(StringUtils.isEmpty(this.imports), "Imports are not supported with a KEY Index");
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
RegionService resolveCache() {
|
||||
|
||||
return Optional.ofNullable(this.cache)
|
||||
.orElseGet(() -> Optional.ofNullable(GemfireUtils.resolveGemFireCache())
|
||||
.orElseThrow(() -> newIllegalStateException("Cache is required")));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
String resolveIndexName() {
|
||||
|
||||
return Optional.ofNullable(this.name).filter(StringUtils::hasText)
|
||||
.orElseGet(() -> Optional.ofNullable(getBeanName()).filter(StringUtils::hasText)
|
||||
.orElseThrow(() -> newIllegalArgumentException("Index name is required")));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
QueryService resolveQueryService() {
|
||||
|
||||
return Optional.ofNullable(this.queryService)
|
||||
.orElseGet(() -> Optional.ofNullable(lookupQueryService())
|
||||
.orElseThrow(() -> newIllegalStateException("QueryService is required to create an Index")));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
QueryService lookupQueryService() {
|
||||
|
||||
String queryServiceBeanName = GemfireConstants.DEFAULT_GEMFIRE_INDEX_DEFINITION_QUERY_SERVICE;
|
||||
|
||||
return Optional.ofNullable(getBeanFactory())
|
||||
.filter(beanFactory -> beanFactory.containsBean(queryServiceBeanName))
|
||||
.map(beanFactory -> beanFactory.getBean(queryServiceBeanName, QueryService.class))
|
||||
.orElseGet(() -> registerQueryServiceBean(queryServiceBeanName, doLookupQueryService()));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
QueryService doLookupQueryService() {
|
||||
|
||||
return Optional.ofNullable(this.queryService).orElseGet(() ->
|
||||
(this.cache instanceof ClientCache ? ((ClientCache) this.cache).getLocalQueryService()
|
||||
: this.cache.getQueryService()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* (non-Javadoc) */
|
||||
QueryService registerQueryServiceBean(String beanName, QueryService queryService) {
|
||||
|
||||
if (isDefine()) {
|
||||
((ConfigurableBeanFactory) getBeanFactory()).registerSingleton(beanName, queryService);
|
||||
}
|
||||
|
||||
return queryService;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
void registerAlias(String beanName, String indexName) {
|
||||
|
||||
Optional.ofNullable(getBeanFactory()).filter(it -> it instanceof ConfigurableBeanFactory)
|
||||
.filter(it -> (beanName != null && !beanName.equals(indexName)))
|
||||
.ifPresent(it -> ((ConfigurableBeanFactory) it).registerAlias(beanName, indexName));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
Index createIndex(QueryService queryService, String indexName) throws Exception {
|
||||
return createIndex(queryService, indexName, false);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
private Index createIndex(QueryService queryService, String indexName, boolean retryAttempted) throws Exception {
|
||||
|
||||
IndexType indexType = this.indexType;
|
||||
|
||||
String expression = this.expression;
|
||||
String from = this.from;
|
||||
String imports = this.imports;
|
||||
|
||||
try {
|
||||
if (IndexType.isKey(indexType)) {
|
||||
return createKeyIndex(queryService, indexName, expression, from);
|
||||
}
|
||||
else if (IndexType.isHash(indexType)) {
|
||||
return createHashIndex(queryService, indexName, expression, from, imports);
|
||||
}
|
||||
else {
|
||||
return createFunctionalIndex(queryService, indexName, expression, from, imports);
|
||||
}
|
||||
}
|
||||
catch (IndexExistsException cause) {
|
||||
|
||||
// Same definition, different name
|
||||
|
||||
Optional<Index> existingIndexByDefinition =
|
||||
tryToFindExistingIndexByDefinition(queryService, expression, from, indexType);
|
||||
|
||||
return existingIndexByDefinition.filter(existingIndex -> isIgnoreIfExists())
|
||||
.map(existingIndex -> {
|
||||
|
||||
logWarning("WARNING! You are choosing to ignore this Index [%1$s] and return the existing"
|
||||
+ " Index having the same basic definition [%2$s] but with a different name [%3$s];"
|
||||
+ " Make sure no OQL Query Hints refer to this Index by name [%1$s]",
|
||||
indexName, toBasicIndexDefinition(), existingIndex.getName());
|
||||
|
||||
return handleIgnore(existingIndex);
|
||||
|
||||
}).orElseGet(() ->
|
||||
|
||||
existingIndexByDefinition.filter(it -> !retryAttempted && isOverride())
|
||||
.map(existingIndex -> {
|
||||
|
||||
// Log an informational warning to caution the user about using the override
|
||||
logWarning("WARNING! You are attempting to 'override' an existing Index [%1$s]"
|
||||
+ " having the same basic definition [%2$s] as the Index that will be created"
|
||||
+ " by this IndexFactoryBean [%3$s]; 'Override' effectively 'renames' the existing"
|
||||
+ " Index [%1$s] by removing it then recreating it under the new name [%3$s] with"
|
||||
+ " the same definition; You should be careful to update any existing OQL Query"
|
||||
+ " Hints referring to the old Index name [%1$s] to now use the new name [%3$s]",
|
||||
existingIndex.getName(), toBasicIndexDefinition(), indexName);
|
||||
|
||||
return handleOverride(existingIndex, queryService, indexName);
|
||||
|
||||
}).orElseThrow(() -> {
|
||||
|
||||
String existingIndexName = existingIndexByDefinition.map(Index::getName)
|
||||
.orElse("unknown");
|
||||
|
||||
return new GemfireIndexException(String.format(
|
||||
"An Index with a different name [%1$s] having the same definition [%2$s] already exists;"
|
||||
+ " You may attempt to override the existing Index [%1$s] with the new name [%3$s]"
|
||||
+ " by setting the 'override' property to 'true'",
|
||||
existingIndexName, toBasicIndexDefinition(), indexName), cause);
|
||||
|
||||
})
|
||||
);
|
||||
}
|
||||
catch (IndexNameConflictException cause) {
|
||||
|
||||
// Same name; possibly different definition
|
||||
|
||||
Optional<Index> existingIndexByName = tryToFindExistingIndexByName(queryService, indexName);
|
||||
|
||||
return existingIndexByName.filter(existingIndex -> isIgnoreIfExists())
|
||||
.map(existingIndex ->
|
||||
|
||||
handleIgnore(warnOnIndexDefinitionMismatch(existingIndex, indexName, "Returning"))
|
||||
|
||||
).orElseGet(() ->
|
||||
|
||||
existingIndexByName.filter(it -> !retryAttempted && isOverride())
|
||||
.map(existingIndex ->
|
||||
|
||||
handleSmartOverride(warnOnIndexDefinitionMismatch(existingIndex, indexName,
|
||||
"Overriding"), queryService, indexName)
|
||||
|
||||
).orElseThrow(() -> {
|
||||
|
||||
String existingIndexDefinition = existingIndexByName
|
||||
.map(it -> String.format(DETAILED_INDEX_DEFINITION, it.getName(),
|
||||
it.getIndexedExpression(), it.getFromClause(), "unknown", it.getType()))
|
||||
.orElse("unknown");
|
||||
|
||||
return new GemfireIndexException(String.format(
|
||||
"An Index with the same name [%1$s] having possibly a different definition already exists;"
|
||||
+ " you may choose to ignore this Index definition [%2$s] and use the existing Index"
|
||||
+ " definition [%3$s] by setting the 'ignoreIfExists' property to 'true'",
|
||||
indexName, toDetailedIndexDefinition(), existingIndexDefinition), cause);
|
||||
|
||||
})
|
||||
);
|
||||
}
|
||||
catch (Exception cause) {
|
||||
throw new GemfireIndexException(String.format("Failed to create Index [%s]",
|
||||
toDetailedIndexDefinition()), cause);
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@SuppressWarnings("all")
|
||||
private boolean isIndexDefinitionMatch(Index index) {
|
||||
|
||||
return Optional.ofNullable(index)
|
||||
.map(it -> {
|
||||
|
||||
IndexType thisIndexType = Optional.ofNullable(this.indexType).orElse(IndexType.FUNCTIONAL);
|
||||
|
||||
boolean result = ObjectUtils.nullSafeEquals(it.getIndexedExpression(), this.expression)
|
||||
&& ObjectUtils.nullSafeEquals(it.getFromClause(), this.from)
|
||||
&& ObjectUtils.nullSafeEquals(IndexType.valueOf(it.getType()), thisIndexType);
|
||||
|
||||
return result;
|
||||
})
|
||||
.orElse(false);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
private boolean isNotIndexDefinitionMatch(Index index) {
|
||||
return !isIndexDefinitionMatch(index);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
private Index warnOnIndexDefinitionMismatch(Index existingIndex, String indexName, String action) {
|
||||
|
||||
if (isNotIndexDefinitionMatch(existingIndex)) {
|
||||
|
||||
String existingIndexDefinition = String.format(BASIC_INDEX_DEFINITION, existingIndex.getIndexedExpression(),
|
||||
existingIndex.getFromClause(), IndexType.valueOf(existingIndex.getType()));
|
||||
|
||||
logWarning("WARNING! %1$s existing Index [%2$s] having a definition [%3$s]"
|
||||
+ " that does not match the Index defined [%4$s] by this IndexFactoryBean [%5$s]",
|
||||
action, existingIndex.getName(), existingIndexDefinition, toBasicIndexDefinition(), indexName);
|
||||
}
|
||||
|
||||
return existingIndex;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
private Index handleIgnore(Index existingIndex) {
|
||||
|
||||
registerAlias(getBeanName(), existingIndex.getName());
|
||||
|
||||
return existingIndex;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
private Index handleOverride(Index existingIndex, QueryService queryService, String indexName) {
|
||||
try {
|
||||
// No way to tell whether the QueryService.remove(:Index) was successful or not! o.O
|
||||
// Should return a boolean! Does it throw an RuntimeException? Javadoc is useless; #sigh
|
||||
queryService.removeIndex(existingIndex);
|
||||
|
||||
return createIndex(queryService, indexName, true);
|
||||
}
|
||||
catch (Exception cause) {
|
||||
throw new GemfireIndexException(String.format(
|
||||
"Attempt to 'override' existing Index [%1$s] with the Index that would be created"
|
||||
+ " by this IndexFactoryBean [%2$s] failed; you should verify the state of"
|
||||
+ " your system and make sure the previously existing Index [%1$s] still exits",
|
||||
existingIndex.getName(), indexName), cause);
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
private Index handleSmartOverride(Index existingIndex, QueryService queryService, String indexName) {
|
||||
|
||||
return Optional.of(existingIndex)
|
||||
.filter(it -> it.getName().equalsIgnoreCase(indexName))
|
||||
.filter(it -> isIndexDefinitionMatch(existingIndex))
|
||||
.orElseGet(() -> handleOverride(existingIndex, queryService, indexName));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
String toBasicIndexDefinition() {
|
||||
return String.format(BASIC_INDEX_DEFINITION, this.expression, this.from, this.indexType);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
String toDetailedIndexDefinition() {
|
||||
return String.format(DETAILED_INDEX_DEFINITION,
|
||||
this.name, this.expression, this.from, this.imports, this.indexType);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
Index createKeyIndex(QueryService queryService, String indexName, String expression, String from) throws Exception {
|
||||
|
||||
if (isDefine()) {
|
||||
queryService.defineKeyIndex(indexName, expression, from);
|
||||
return new IndexWrapper(queryService, indexName);
|
||||
}
|
||||
else {
|
||||
return queryService.createKeyIndex(indexName, expression, from);
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
Index createHashIndex(QueryService queryService, String indexName, String expression, String from, String imports)
|
||||
throws Exception {
|
||||
|
||||
boolean hasImports = StringUtils.hasText(imports);
|
||||
|
||||
if (isDefine()) {
|
||||
if (hasImports) {
|
||||
queryService.defineHashIndex(indexName, expression, from, imports);
|
||||
}
|
||||
else {
|
||||
queryService.defineHashIndex(indexName, expression, from);
|
||||
}
|
||||
|
||||
return new IndexWrapper(queryService, indexName);
|
||||
}
|
||||
else {
|
||||
if (hasImports) {
|
||||
return queryService.createHashIndex(indexName, expression, from, imports);
|
||||
}
|
||||
else {
|
||||
return queryService.createHashIndex(indexName, expression, from);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
Index createFunctionalIndex(QueryService queryService, String indexName, String expression, String from,
|
||||
String imports) throws Exception {
|
||||
|
||||
boolean hasImports = StringUtils.hasText(imports);
|
||||
|
||||
if (isDefine()) {
|
||||
if (hasImports) {
|
||||
queryService.defineIndex(indexName, expression, from , imports);
|
||||
}
|
||||
else {
|
||||
queryService.defineIndex(indexName, expression, from);
|
||||
}
|
||||
|
||||
return new IndexWrapper(queryService, indexName);
|
||||
}
|
||||
else {
|
||||
if (hasImports) {
|
||||
return queryService.createIndex(indexName, expression, from, imports);
|
||||
}
|
||||
else {
|
||||
return queryService.createIndex(indexName, expression, from);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
Optional<Index> tryToFindExistingIndexByDefinition(QueryService queryService,
|
||||
String expression, String fromClause, IndexType indexType) {
|
||||
|
||||
for (Index index : nullSafeCollection(queryService.getIndexes())) {
|
||||
if (index.getIndexedExpression().equalsIgnoreCase(expression)
|
||||
&& index.getFromClause().equalsIgnoreCase(fromClause)
|
||||
&& indexType.equals(IndexType.valueOf(index.getType()))) {
|
||||
|
||||
return Optional.of(index);
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
Optional<Index> tryToFindExistingIndexByName(QueryService queryService, String indexName) {
|
||||
|
||||
for (Index index : nullSafeCollection(queryService.getIndexes())) {
|
||||
if (index.getName().equalsIgnoreCase(indexName)) {
|
||||
return Optional.of(index);
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the Composite {@link IndexConfigurer} used to apply additional configuration
|
||||
* to this {@link IndexFactoryBean} on Spring container initialization.
|
||||
*
|
||||
* @return the Composite {@link IndexConfigurer}.
|
||||
* @see org.springframework.data.gemfire.config.annotation.IndexConfigurer
|
||||
*/
|
||||
protected IndexConfigurer getCompositeRegionConfigurer() {
|
||||
return this.compositeIndexConfigurer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the {@link Index} created by this {@link IndexFactoryBean}.
|
||||
*
|
||||
* @return a reference to the {@link Index} created by this {@link IndexFactoryBean}.
|
||||
* @see org.apache.geode.cache.query.Index
|
||||
*/
|
||||
public Index getIndex() {
|
||||
return this.index;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public Index getObject() {
|
||||
return Optional.ofNullable(getIndex()).orElseGet(() ->
|
||||
this.index = tryToFindExistingIndexByName(resolveQueryService(), resolveIndexName()).orElse(null));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Class<?> getObjectType() {
|
||||
return Optional.ofNullable(getIndex()).map(Index::getClass).orElse((Class) Index.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a reference to the {@link RegionService}.
|
||||
*
|
||||
* @param cache reference to the {@link RegionService}.
|
||||
* @see org.apache.geode.cache.RegionService
|
||||
*/
|
||||
public void setCache(RegionService cache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the {@link Index}.
|
||||
*
|
||||
* @param name {@link String} containing the name given to the {@link Index}.
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link QueryService} used to create the {@link Index}.
|
||||
*
|
||||
* @param service {@link QueryService} used to create the {@link Index}.
|
||||
* @see org.apache.geode.cache.query.QueryService
|
||||
*/
|
||||
public void setQueryService(QueryService service) {
|
||||
this.queryService = service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a boolean condition to indicate whether the {@link Index} declared and defined by this
|
||||
* {@link IndexFactoryBean} will only be defined initially, or defined and created. If defined-only,
|
||||
* the {@link IndexFactoryBean} will receive a callback at the end of the Spring container lifecycle
|
||||
* to subsequently "create" all "defined-only" {@link Index Indexes} once, in a single operation.
|
||||
*
|
||||
* @param define a boolean value indicating the define or define/create status. If {@literal true},
|
||||
* the {@link Index} declared by this {@link IndexFactoryBean} will only be defined initially
|
||||
* and subsequently created when this bean receives an appropriate callback from the Spring container;
|
||||
* if {@literal false}, the {@link Index} will be created immediately.
|
||||
*/
|
||||
public void setDefine(boolean define) {
|
||||
this.define = define;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a boolean indicating whether the {@link Index} declared and defined by this {@link IndexFactoryBean}
|
||||
* will only be defined initially, or defined and created. If defined-only, the {@link IndexFactoryBean}
|
||||
* will receive a callback at the end of the Spring container lifecycle to subsequently "create" all "defined-only"
|
||||
* {@link Index Indexes} once, in a single operation.
|
||||
*
|
||||
* @return a boolean value indicating the define or define/create status. If {@literal true}, the {@link Index}
|
||||
* declared by this {@link IndexFactoryBean} will only be defined initially and subsequently created when this bean
|
||||
* receives an appropriate callback from the Spring container; if {@literal false}, the {@link Index}
|
||||
* will be created immediately.
|
||||
*/
|
||||
protected boolean isDefine() {
|
||||
return define;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param expression Index expression to set
|
||||
*/
|
||||
public void setExpression(String expression) {
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param from Index from clause to set
|
||||
*/
|
||||
public void setFrom(String from) {
|
||||
this.from = from;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param imports Index imports to set
|
||||
*/
|
||||
public void setImports(String imports) {
|
||||
this.imports = imports;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures whether to ignore the {@link Index} defined by this {@link IndexFactoryBean}
|
||||
* when an {@link IndexExistsException} or {@link IndexNameConflictException} is thrown.
|
||||
*
|
||||
* An {@link IndexExistsException} is thrown when there exists another {@link Index} with the same definition
|
||||
* but with another name.
|
||||
*
|
||||
* An {@link IndexNameConflictException} is thrown when there exists another {@link Index} with the same name
|
||||
* but possibly a different definition.
|
||||
*
|
||||
* When {@literal ignoreIfExists} is set to {@literal true} and an {@link IndexExistsException} is thrown,
|
||||
* then the existing {@link Index} will be returned as the object of this {@link IndexFactoryBean} creation
|
||||
* and the name of the existing {@link Index} is added as an alias for this bean.
|
||||
*
|
||||
* When {@literal ignoreIfExists} is set to {@literal true} and {@link IndexNameConflictException} is thrown,
|
||||
* then the existing {@link Index} will be returned as the object of this {@link IndexFactoryBean} creation.
|
||||
* A warning is logged if the definition of this {@link IndexFactoryBean} and the existing {@link Index}
|
||||
* are different.
|
||||
*
|
||||
* {@literal ignoreIfExists} takes precedence over {@link #isOverride() override}.
|
||||
*
|
||||
* Defaults to {@literal false}.
|
||||
*
|
||||
* @param ignore boolean value indicating whether to ignore the {@link Index} defined by
|
||||
* this {@link IndexFactoryBean}. Default is {@literal false}.
|
||||
* @see #setOverride(boolean)
|
||||
*/
|
||||
public void setIgnoreIfExists(boolean ignore) {
|
||||
this.ignoreIfExists = ignore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether to ignore the {@link Index} defined by this {@link IndexFactoryBean}
|
||||
* when an {@link IndexExistsException} or {@link IndexNameConflictException} is thrown.
|
||||
*
|
||||
* An {@link IndexExistsException} is thrown when there exists another {@link Index} with the same definition
|
||||
* but with another name.
|
||||
*
|
||||
* An {@link IndexNameConflictException} is thrown when there exists another {@link Index} with the same name
|
||||
* but possibly a different definition.
|
||||
*
|
||||
* When {@literal ignoreIfExists} is set to {@literal true} and an {@link IndexExistsException} is thrown,
|
||||
* then the existing {@link Index} will be returned as the object of this {@link IndexFactoryBean} creation
|
||||
* and the name of the existing {@link Index} is added as an alias for this bean.
|
||||
*
|
||||
* When {@literal ignoreIfExists} is set to {@literal true} and {@link IndexNameConflictException} is thrown,
|
||||
* then the existing {@link Index} will be returned as the object of this {@link IndexFactoryBean} creation.
|
||||
* A warning is logged if the definition of this {@link IndexFactoryBean} and the existing {@link Index}
|
||||
* are different.
|
||||
*
|
||||
* {@literal ignoreIfExists} takes precedence over {@link #isOverride() override}.
|
||||
*
|
||||
* Defaults to {@literal false}.
|
||||
*
|
||||
* @return a boolean value indicating whether to ignore the {@link Index} defined by this {@link IndexFactoryBean}.
|
||||
* Default is {@literal false}.
|
||||
* @see #setIgnoreIfExists(boolean)
|
||||
*/
|
||||
public boolean isIgnoreIfExists() {
|
||||
return this.ignoreIfExists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe operation to set an array of {@link IndexConfigurer IndexConfigurers} used to apply
|
||||
* additional configuration to this {@link IndexFactoryBean} when using Annotation-based configuration.
|
||||
*
|
||||
* @param indexConfigurers array of {@link IndexConfigurer IndexConfigurers} used to apply
|
||||
* additional configuration to this {@link IndexFactoryBean}.
|
||||
* @see org.springframework.data.gemfire.config.annotation.IndexConfigurer
|
||||
* @see #setIndexConfigurers(List)
|
||||
*/
|
||||
public void setIndexConfigurers(IndexConfigurer... indexConfigurers) {
|
||||
setIndexConfigurers(Arrays.asList(nullSafeArray(indexConfigurers, IndexConfigurer.class)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe operation to set an {@link Iterable} of {@link IndexConfigurer IndexConfigurers} used to apply
|
||||
* additional configuration to this {@link IndexFactoryBean} when using Annotation-based configuration.
|
||||
*
|
||||
* @param indexConfigurers {@link Iterable } of {@link IndexConfigurer IndexConfigurers} used to apply
|
||||
* additional configuration to this {@link IndexFactoryBean}.
|
||||
* @see org.springframework.data.gemfire.config.annotation.IndexConfigurer
|
||||
*/
|
||||
public void setIndexConfigurers(List<IndexConfigurer> indexConfigurers) {
|
||||
this.indexConfigurers = Optional.ofNullable(indexConfigurers).orElseGet(Collections::emptyList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures whether to override an existing {@link Index} having the same definition but different name
|
||||
* as the {@link Index} that would be created by this {@link IndexFactoryBean}.
|
||||
*
|
||||
* An {@link IndexExistsException} is thrown when there exists another {@link Index} with the same definition
|
||||
* but with another name.
|
||||
*
|
||||
* An {@link IndexNameConflictException} is thrown when there exists another {@link Index} with the same name
|
||||
* but possibly a different definition.
|
||||
*
|
||||
* With {@literal override} set to {@literal true} when an {@link IndexExistsException} is thrown, then override
|
||||
* is effectively the same as "renaming" the existing {@link Index}. In other words, the existing {@link Index}
|
||||
* will be {@link QueryService#removeIndex(Index) removed} and recreated by this {@link IndexFactoryBean}
|
||||
* under the new {@link #resolveIndexName() name} having the same definition.
|
||||
*
|
||||
* With {@literal override} set to {@literal true} when an {@link IndexNameConflictException} is thrown,
|
||||
* then overriding the existing {@link Index} is equivalent to changing the existing {@link Index} definition.
|
||||
* When this happens, a warning is logged. If the existing {@link Index} definition is the same then overriding
|
||||
* effectively just rebuilds the {@link Index}.
|
||||
*
|
||||
* {@literal ignoreIfExists} takes precedence over {@literal override}.
|
||||
*
|
||||
* Defaults to {@literal false}.
|
||||
*
|
||||
* @param override boolean value indicating whether an existing {@link Index} will be removed and recreated
|
||||
* by this {@link IndexFactoryBean}. Default is {@literal false}.
|
||||
* @see #setIgnoreIfExists(boolean)
|
||||
*/
|
||||
public void setOverride(boolean override) {
|
||||
this.override = override;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether to override an existing {@link Index} having the same definition but different name
|
||||
* as the {@link Index} that would be created by this {@link IndexFactoryBean}.
|
||||
*
|
||||
* An {@link IndexExistsException} is thrown when there exists another {@link Index} with the same definition
|
||||
* but with another name.
|
||||
*
|
||||
* An {@link IndexNameConflictException} is thrown when there exists another {@link Index} with the same name
|
||||
* but possibly a different definition.
|
||||
*
|
||||
* With {@literal override} set to {@literal true} when an {@link IndexExistsException} is thrown, then override
|
||||
* is effectively the same as "renaming" the existing {@link Index}. In other words, the existing {@link Index}
|
||||
* will be {@link QueryService#removeIndex(Index) removed} and recreated by this {@link IndexFactoryBean}
|
||||
* under the new {@link #resolveIndexName() name} having the same definition.
|
||||
*
|
||||
* With {@literal override} set to {@literal true} when an {@link IndexNameConflictException} is thrown,
|
||||
* then overriding the existing {@link Index} is equivalent to changing the existing {@link Index} definition.
|
||||
* When this happens, a warning is logged. If the existing {@link Index} definition is the same then overriding
|
||||
* effectively just rebuilds the {@link Index}.
|
||||
*
|
||||
* {@literal ignoreIfExists} takes precedence over {@literal override}.
|
||||
*
|
||||
* Defaults to {@literal false}.
|
||||
*
|
||||
* @return a boolean value indicating whether an existing {@link Index} will be removed and recreated
|
||||
* by this {@link IndexFactoryBean}. Default is {@literal false}.
|
||||
* @see #setOverride(boolean)
|
||||
*/
|
||||
public boolean isOverride() {
|
||||
return this.override;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link IndexType type} of the {@link Index} as a {@link String}.
|
||||
*
|
||||
* @param type {@link String} specifying the {@link IndexType type} of the {@link Index}.
|
||||
* @see org.springframework.data.gemfire.IndexType#valueOf(String)
|
||||
* @see #setType(IndexType)
|
||||
*/
|
||||
public void setType(String type) {
|
||||
setType(IndexType.valueOfIgnoreCase(type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link IndexType type} of the {@link Index}.
|
||||
*
|
||||
* @param type {@link IndexType} indicating the type of the {@link Index}.
|
||||
* @see org.springframework.data.gemfire.IndexType
|
||||
*/
|
||||
public void setType(IndexType type) {
|
||||
this.indexType = type;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static final class IndexWrapper implements Index {
|
||||
|
||||
private Index index;
|
||||
|
||||
private final QueryService queryService;
|
||||
|
||||
private final String indexName;
|
||||
|
||||
protected IndexWrapper(QueryService queryService, String indexName) {
|
||||
|
||||
Assert.notNull(queryService, "QueryService is required");
|
||||
Assert.hasText(indexName, "Name of Index is required");
|
||||
|
||||
this.queryService = queryService;
|
||||
this.indexName = indexName;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected synchronized Index resolveIndex() {
|
||||
|
||||
String indexName = getIndexName();
|
||||
|
||||
return Optional.ofNullable(this.index)
|
||||
.orElseGet(() -> {
|
||||
|
||||
AtomicReference<Index> searchResult = new AtomicReference<>();
|
||||
|
||||
nullSafeCollection(getQueryService().getIndexes()).forEach(index -> {
|
||||
if (index.getName().equalsIgnoreCase(indexName)) {
|
||||
searchResult.set(index);
|
||||
}
|
||||
});
|
||||
|
||||
return Optional.of(searchResult).map(it -> {
|
||||
this.index = it.get();
|
||||
return this.index;
|
||||
}).orElseThrow(() -> new GemfireIndexException(
|
||||
String.format("Index with name [%s] was not found", indexName), (Exception) null));
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected Index getIndex() {
|
||||
return this.index;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected String getIndexName() {
|
||||
return Optional.ofNullable(this.indexName).filter(StringUtils::hasText).orElseThrow(() ->
|
||||
newIllegalStateException("Index name is required"));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected QueryService getQueryService() {
|
||||
return this.queryService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return resolveIndex().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCanonicalizedFromClause() {
|
||||
return resolveIndex().getCanonicalizedFromClause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCanonicalizedIndexedExpression() {
|
||||
return resolveIndex().getCanonicalizedIndexedExpression();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCanonicalizedProjectionAttributes() {
|
||||
return resolveIndex().getCanonicalizedProjectionAttributes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFromClause() {
|
||||
return resolveIndex().getFromClause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIndexedExpression() {
|
||||
return resolveIndex().getIndexedExpression();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProjectionAttributes() {
|
||||
return resolveIndex().getProjectionAttributes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Region<?, ?> getRegion() {
|
||||
return resolveIndex().getRegion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IndexStatistics getStatistics() {
|
||||
return resolveIndex().getStatistics();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public org.apache.geode.cache.query.IndexType getType() {
|
||||
return resolveIndex().getType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(obj instanceof IndexWrapper || obj instanceof Index)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (obj instanceof IndexWrapper) {
|
||||
return (getIndexName().equals(((IndexWrapper) obj).getIndexName()));
|
||||
}
|
||||
|
||||
return resolveIndex().equals(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
int hashValue = 37;
|
||||
|
||||
hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(getIndexName());
|
||||
hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(index);
|
||||
|
||||
return hashValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return Optional.ofNullable(getIndex()).map(String::valueOf)
|
||||
.orElseGet(this::getIndexName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import org.springframework.data.gemfire.support.AbstractPropertyEditorConverterSupport;
|
||||
|
||||
/**
|
||||
* The IndexMaintenanceTypeConverter class is a Spring Converter and JavaBeans PropertyEditor capable of converting
|
||||
* a String into a specific SDG IndexMaintenancePolicyType.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.data.gemfire.IndexMaintenancePolicyType
|
||||
* @see org.springframework.data.gemfire.support.AbstractPropertyEditorConverterSupport
|
||||
* @since 1.6.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class IndexMaintenancePolicyConverter extends AbstractPropertyEditorConverterSupport<IndexMaintenancePolicyType> {
|
||||
|
||||
/**
|
||||
* Converts the given String value into an appropriate IndexMaintenancePolicyType.
|
||||
*
|
||||
* @param source the String value to convert into a IndexMaintenancePolicyType.
|
||||
* @return an IndexMaintenancePolicyType converted from the given String value.
|
||||
* @throws java.lang.IllegalArgumentException if the String is not a valid IndexMaintenancePolicyType.
|
||||
* @see org.springframework.data.gemfire.IndexMaintenancePolicyType#valueOfIgnoreCase(String)
|
||||
* @see #assertConverted(String, Object, Class)
|
||||
*/
|
||||
@Override
|
||||
public IndexMaintenancePolicyType convert(final String source) {
|
||||
return assertConverted(source, IndexMaintenancePolicyType.valueOfIgnoreCase(source),
|
||||
IndexMaintenancePolicyType.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import org.apache.geode.cache.AttributesFactory;
|
||||
import org.apache.geode.cache.RegionFactory;
|
||||
|
||||
/**
|
||||
* The IndexMaintenanceType enum is a enumerated type of GemFire Index maintenance update options.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.apache.geode.cache.AttributesFactory#setIndexMaintenanceSynchronous(boolean)
|
||||
* @see org.apache.geode.cache.RegionAttributes#getIndexMaintenanceSynchronous()
|
||||
* @see org.apache.geode.cache.RegionFactory#setIndexMaintenanceSynchronous(boolean)
|
||||
* @since 1.6.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public enum IndexMaintenancePolicyType {
|
||||
SYNCHRONOUS,
|
||||
ASYNCHRONOUS;
|
||||
|
||||
public static final IndexMaintenancePolicyType DEFAULT = IndexMaintenancePolicyType.SYNCHRONOUS;
|
||||
|
||||
/**
|
||||
* Return an IndexMaintenanceType enumerated value given a case-insensitive, named String value
|
||||
* describing the type of Index maintenance.
|
||||
*
|
||||
* @param name the String value indicating the type of Index maintenance (update).
|
||||
* @return an IndexMaintenanceType enumerated value given a case-insensitive, named String value describing
|
||||
* the type of Index maintenance, or null if no match was found.
|
||||
* @see java.lang.String#equalsIgnoreCase(String)
|
||||
* @see #name()
|
||||
*/
|
||||
public static IndexMaintenancePolicyType valueOfIgnoreCase(final String name) {
|
||||
for (IndexMaintenancePolicyType indexMaintenancePolicyType : values()) {
|
||||
if (indexMaintenancePolicyType.name().equalsIgnoreCase(name)) {
|
||||
return indexMaintenancePolicyType;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the GemFire AttributesFactory's 'indexMaintenanceSynchronous' property appropriately based on
|
||||
* this IndexMaintenancePolicyType.
|
||||
*
|
||||
* @param attributesFactory the AttributesFactory instance on which to set the indexMaintenanceProperty.
|
||||
* @throws java.lang.NullPointerException if the AttributesFactory reference is null.
|
||||
* @see #setIndexMaintenance(org.apache.geode.cache.RegionFactory)
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public void setIndexMaintenance(final AttributesFactory attributesFactory) {
|
||||
attributesFactory.setIndexMaintenanceSynchronous(equals(SYNCHRONOUS));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the GemFire RegionFactory's 'indexMaintenanceSynchronous' property appropriately based on
|
||||
* this IndexMaintenancePolicyType.
|
||||
*
|
||||
* @param regionFactory the RegionFactory instance on which to set the indexMaintenanceProperty.
|
||||
* @throws java.lang.NullPointerException if the RegionFactory reference is null.
|
||||
* @see #setIndexMaintenance(org.apache.geode.cache.AttributesFactory)
|
||||
*/
|
||||
public void setIndexMaintenance(final RegionFactory regionFactory) {
|
||||
regionFactory.setIndexMaintenanceSynchronous(equals(SYNCHRONOUS));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
/**
|
||||
* The IndexType class is an enumerated type of GemFire Index Types.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.apache.geode.cache.query.IndexType
|
||||
* @since 1.5.2
|
||||
*/
|
||||
@SuppressWarnings({ "deprecation", "unused" })
|
||||
public enum IndexType {
|
||||
|
||||
FUNCTIONAL(org.apache.geode.cache.query.IndexType.FUNCTIONAL),
|
||||
HASH(org.apache.geode.cache.query.IndexType.HASH),
|
||||
PRIMARY_KEY(org.apache.geode.cache.query.IndexType.PRIMARY_KEY),
|
||||
KEY(org.apache.geode.cache.query.IndexType.PRIMARY_KEY);
|
||||
|
||||
private final org.apache.geode.cache.query.IndexType gemfireIndexType;
|
||||
|
||||
/**
|
||||
* Constructs an instance of the IndexType enum initialized with the given GemFire IndexType.
|
||||
*
|
||||
* @param gemfireIndexType the corresponding GemFire IndexType
|
||||
* @see org.apache.geode.cache.query.IndexType
|
||||
*/
|
||||
IndexType(final org.apache.geode.cache.query.IndexType gemfireIndexType) {
|
||||
this.gemfireIndexType = gemfireIndexType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe operation to determine if the IndexType is a "FUNCTIONAL" Index.
|
||||
*
|
||||
* @param indexType the IndexType to evaluate.
|
||||
* @return a boolean value indicating whether the IndexType is a "FUNCTIONAL" Index.
|
||||
* @see #isFunctional()
|
||||
*/
|
||||
public static boolean isFunctional(IndexType indexType) {
|
||||
return (indexType != null && indexType.isFunctional());
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe operation to determine if the IndexType is a "HASH" Index.
|
||||
*
|
||||
* @param indexType the IndexType to evaluate.
|
||||
* @return a boolean value indicating whether the IndexType is a "HASH" Index.
|
||||
* @see #isHash()
|
||||
*/
|
||||
public static boolean isHash(IndexType indexType) {
|
||||
return (indexType != null && indexType.isHash());
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe operation to determine if the IndexType is a "KEY" Index.
|
||||
*
|
||||
* @param indexType the IndexType to evaluate.
|
||||
* @return a boolean value indicating whether the IndexType is a "KEY" Index.
|
||||
* @see #isFunctional()
|
||||
*/
|
||||
public static boolean isKey(IndexType indexType) {
|
||||
return (indexType != null && indexType.isKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an IndexType given the corresponding GemFire IndexType or null if no SDG IndexType
|
||||
* corresponds to the GemFire IndexType.
|
||||
*
|
||||
* @param gemfireIndexType the GemFire IndexType.
|
||||
* @return a IndexType matching the GemFire IndexType or null if the GemFire IndexType does not match
|
||||
* any IndexType in this enumeration.
|
||||
* @see org.apache.geode.cache.query.IndexType
|
||||
*/
|
||||
public static IndexType valueOf(org.apache.geode.cache.query.IndexType gemfireIndexType) {
|
||||
|
||||
for (IndexType indexType : values()) {
|
||||
if (indexType.getGemfireIndexType().equals(gemfireIndexType)) {
|
||||
return indexType;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an IndexType matching the given String.
|
||||
*
|
||||
* @param value the String value describing the matching IndexType.
|
||||
* @return an IndexType matching the given String.
|
||||
* @see java.lang.String#equalsIgnoreCase(String)
|
||||
*/
|
||||
public static IndexType valueOfIgnoreCase(String value) {
|
||||
|
||||
for (IndexType indexType : values()) {
|
||||
if (indexType.name().equalsIgnoreCase(value)) {
|
||||
return indexType;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the matching GemFire IndexType for this IndexType enumerated value.
|
||||
*
|
||||
* @return the matching GemFire IndexType.
|
||||
* @see org.apache.geode.cache.query.IndexType
|
||||
*/
|
||||
public org.apache.geode.cache.query.IndexType getGemfireIndexType() {
|
||||
return gemfireIndexType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether this IndexType is "FUNCTIONAL".
|
||||
*
|
||||
* @return a boolean value indicating whether this IndexType is "FUNCTIONAL".
|
||||
*/
|
||||
public boolean isFunctional() {
|
||||
return this.equals(FUNCTIONAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether this IndexType is a "HASH" Index.
|
||||
*
|
||||
* @return a boolean value indicating whether this IndexType is a "HASH" Index.
|
||||
*/
|
||||
public boolean isHash() {
|
||||
return this.equals(HASH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether this IndexType is a "KEY" Index.
|
||||
*
|
||||
* @return a boolean value indicating whether this IndexType is a "KEY" Index.
|
||||
*/
|
||||
public boolean isKey() {
|
||||
return (this.equals(KEY) || this.equals(PRIMARY_KEY));
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import org.springframework.data.gemfire.support.AbstractPropertyEditorConverterSupport;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* The IndexTypeConverter class is a Spring Converter implementation as well as a JavaBeans PropertyEditor
|
||||
* that converts a given String value into a proper IndexType.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.data.gemfire.IndexType
|
||||
* @see org.springframework.data.gemfire.support.AbstractPropertyEditorConverterSupport
|
||||
* @since 1.5.2
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class IndexTypeConverter extends AbstractPropertyEditorConverterSupport<IndexType> {
|
||||
|
||||
/**
|
||||
* Converts the given String value into an appropriate IndexType.
|
||||
*
|
||||
* @param value the String to convert into an appropriate IndexType enumerated value.
|
||||
* @return an IndexType converted from the given String.
|
||||
* @throws java.lang.IllegalArgumentException if the given String could not be converted into
|
||||
* an appropriate IndexType enumerated value.
|
||||
* @see #assertConverted(String, Object, Class)
|
||||
* @see org.springframework.data.gemfire.IndexType#valueOfIgnoreCase(String)
|
||||
* @see org.springframework.util.StringUtils#trimWhitespace(String)
|
||||
*/
|
||||
@Override
|
||||
public IndexType convert(final String value) {
|
||||
return assertConverted(value, IndexType.valueOfIgnoreCase(StringUtils.trimWhitespace(value)), IndexType.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import org.apache.geode.cache.InterestPolicy;
|
||||
|
||||
import org.springframework.data.gemfire.support.AbstractPropertyEditorConverterSupport;
|
||||
|
||||
/**
|
||||
* The InterestPolicyConverter class is a Spring Converter implementation and Java PropertyEditor handling
|
||||
* the conversion between Strings and GemFire InterestPolicy values.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.data.gemfire.support.AbstractPropertyEditorConverterSupport
|
||||
* @see org.apache.geode.cache.InterestPolicy
|
||||
* @since 1.6.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class InterestPolicyConverter extends AbstractPropertyEditorConverterSupport<InterestPolicy> {
|
||||
|
||||
/**
|
||||
* Converts the given String into a GemFire InterestPolicy value.
|
||||
*
|
||||
* @param source the String value to convert into a GemFire InterestPolicy value.
|
||||
* @return a GemFire InterestPolicy value for the given String description of the GemFire InterestPolicy
|
||||
* @throws java.lang.IllegalArgumentException if the String is not a valid GemFire InterestPolicy.
|
||||
* @see org.springframework.data.gemfire.InterestPolicyType#getInterestPolicy(InterestPolicyType)
|
||||
* @see org.springframework.data.gemfire.InterestPolicyType#valueOfIgnoreCase(String)
|
||||
* @see #assertConverted(String, Object, Class)
|
||||
*/
|
||||
@Override
|
||||
public InterestPolicy convert(final String source) {
|
||||
return assertConverted(source, InterestPolicyType.getInterestPolicy(
|
||||
InterestPolicyType.valueOfIgnoreCase(source)), InterestPolicy.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import org.apache.geode.cache.InterestPolicy;
|
||||
|
||||
/**
|
||||
* The InterestPolicyType enum is an enumeration of all the GemFire Subscription, InterestPolicy values.
|
||||
*
|
||||
* @author Lyndon Adams
|
||||
* @author John Blum
|
||||
* @see org.apache.geode.cache.InterestPolicy
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public enum InterestPolicyType {
|
||||
ALL(InterestPolicy.ALL),
|
||||
CACHE_CONTENT(InterestPolicy.CACHE_CONTENT);
|
||||
|
||||
public static final InterestPolicyType DEFAULT = InterestPolicyType.valueOf(InterestPolicy.DEFAULT);
|
||||
|
||||
private final InterestPolicy interestPolicy;
|
||||
|
||||
/**
|
||||
* Constructs an instance of the SubscriptionType enum initialized with the matching GemFire InterestPolicy.
|
||||
*
|
||||
* @param interestPolicy a GemFire InterestPolicy corresponding to this SubscriptionType.
|
||||
* @see org.apache.geode.cache.InterestPolicy
|
||||
*/
|
||||
InterestPolicyType(final InterestPolicy interestPolicy) {
|
||||
this.interestPolicy = interestPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe operation to extract the GemFire InterestPolicy from the InterPolicyType enumerated value.
|
||||
*
|
||||
* @param interestPolicyType the InterestPolicyType enum from which to extract GemFire's InterestPolicy
|
||||
* @return a GemFire InterestPolicy for the given InterestPolicyType enumerated value
|
||||
* or null if InterestPolicyType is null.
|
||||
* @see org.apache.geode.cache.InterestPolicy
|
||||
*/
|
||||
public static InterestPolicy getInterestPolicy(final InterestPolicyType interestPolicyType) {
|
||||
return (interestPolicyType != null ? interestPolicyType.getInterestPolicy() : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a SubscriptionType enumerated value for the given GemFire InterestPolicy.
|
||||
*
|
||||
* @param interestPolicy the GemFire InterestPolicy used to lookup and match a SubscriptionType.
|
||||
* @return a SubscriptionType enumerated value matching the given GemFire InterestPolicy
|
||||
* or null if no matching value was found.
|
||||
* @see org.apache.geode.cache.InterestPolicy
|
||||
* @see #getInterestPolicy()
|
||||
*/
|
||||
public static InterestPolicyType valueOf(final InterestPolicy interestPolicy) {
|
||||
for (InterestPolicyType interestPolicyType : values()) {
|
||||
if (interestPolicyType.getInterestPolicy().equals(interestPolicy)) {
|
||||
return interestPolicyType;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a SubscriptionType enumerated value for the case-insensitive, named Subscription (InterestsPolicy).
|
||||
*
|
||||
* @param value a String name used to look and match the SubscriptionType.
|
||||
* @return a SubscriptionType enumerated value for the given case-insensitive named Subscription
|
||||
* or null if no match was found.
|
||||
* @see java.lang.String#equalsIgnoreCase(String)
|
||||
* @see #name()
|
||||
*/
|
||||
public static InterestPolicyType valueOfIgnoreCase(final String value) {
|
||||
for (InterestPolicyType interestPolicyType : values()) {
|
||||
if (interestPolicyType.name().equalsIgnoreCase(value)) {
|
||||
return interestPolicyType;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the GemFire InterestPolicy corresponding to this SubscriptionType enumerated value.
|
||||
*
|
||||
* @return the GemFire InterestPolicy corresponding to this SubscriptionType.
|
||||
* @see org.apache.geode.cache.InterestPolicy
|
||||
*/
|
||||
public InterestPolicy getInterestPolicy() {
|
||||
return interestPolicy;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* The JndiDataSourceType class is an enumeration of valid JNDI DataSource implementation types supported by GemFire.
|
||||
*
|
||||
* @author John Blum
|
||||
* @link https://gemfire.docs.pivotal.io/latest/userguide/index.html#reference/topics/cache_xml.html#jndi-binding
|
||||
* @since 1.7.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public enum JndiDataSourceType {
|
||||
MANAGED("ManagedDataSource"),
|
||||
POOLED("PooledDataSource"),
|
||||
SIMPLE("SimpleDataSource"),
|
||||
XA("XAPooledDataSource");
|
||||
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* Constructs an instance of the JndiDataSourceType enum initialized with the specified name used by GemFire to
|
||||
* specify supported JNDI DataSource implementations.
|
||||
*
|
||||
* @param name the GemFire named JNDI DataSource implementation.
|
||||
*/
|
||||
JndiDataSourceType(final String name) {
|
||||
Assert.hasText(name, "The JNDI DataSource Type 'name' must be specified!");
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a JndiDataSourceType enumerated value based on the GemFire preferred name for the supported JNDI,
|
||||
* DataSource implementation, ignoring case and all extra leading/trailing whitespace.
|
||||
*
|
||||
* @param name the GemFire named JNDI DataSource implementation.
|
||||
* @return the JndiDataSourceType enumerated value matching the given GemFire name used for the supported JNDI,
|
||||
* DataSource implementation, or null if not match was found.
|
||||
* @see #values()
|
||||
* @see #isMatch(JndiDataSourceType, String)
|
||||
*/
|
||||
public static JndiDataSourceType valueOfIgnoreCase(final String name) {
|
||||
for (JndiDataSourceType jndiDataSourceType : values()) {
|
||||
if (isMatch(jndiDataSourceType, name)) {
|
||||
return jndiDataSourceType;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the specified JndiDataSourceType enum and the given, supported GemFire 'named',
|
||||
* JNDI DataSource implementation are a match.
|
||||
*
|
||||
* @param jndiDataSourceType the given JndiDataSourceType enum used in the match.
|
||||
* @param name the specified GemFire "named" JNDI DataSource implementation.
|
||||
* @return a boolean value indicating whether the given JndiDataSourceType enumerated value matched the given name.
|
||||
* @see java.lang.String#equalsIgnoreCase(String)
|
||||
* @see org.springframework.util.StringUtils#trimWhitespace(String)
|
||||
*/
|
||||
private static boolean isMatch(final JndiDataSourceType jndiDataSourceType, String name) {
|
||||
name = StringUtils.trimWhitespace(name);
|
||||
return (jndiDataSourceType.getName().equalsIgnoreCase(name)
|
||||
|| jndiDataSourceType.name().equalsIgnoreCase(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the GemFire name of the support JNDI DataSource implementation type.
|
||||
*
|
||||
* @return the GemFire named JNDI DataSource implementation.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a String describing this JNDI DataSource implementation based on the GemFire supported names.
|
||||
*
|
||||
* @return a String description for this JNDI DataSource (implementation) type.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return getName();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalArgumentException;
|
||||
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newUnsupportedOperationException;
|
||||
|
||||
import org.apache.geode.cache.DataPolicy;
|
||||
import org.apache.geode.cache.RegionFactory;
|
||||
import org.apache.geode.cache.Scope;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author David Turanski
|
||||
* @author John Blum
|
||||
*/
|
||||
public class LocalRegionFactoryBean<K, V> extends PeerRegionFactoryBean<K, V> {
|
||||
|
||||
@Override
|
||||
public void setScope(Scope scope) {
|
||||
throw newUnsupportedOperationException("Setting the Scope on Local Regions is not allowed");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
|
||||
super.setScope(Scope.LOCAL);
|
||||
super.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resolveDataPolicy(RegionFactory<K, V> regionFactory, Boolean persistent, DataPolicy dataPolicy) {
|
||||
|
||||
if (dataPolicy == null || DataPolicy.NORMAL.equals(dataPolicy)) {
|
||||
|
||||
// NOTE this is safe since a LOCAL Scoped NORMAL Region requiring persistence can be satisfied with
|
||||
// PERSISTENT_REPLICATE, per the RegionShortcut.LOCAL_PERSISTENT
|
||||
DataPolicy resolvedDataPolicy = (isPersistent() ? DataPolicy.PERSISTENT_REPLICATE : DataPolicy.NORMAL);
|
||||
|
||||
regionFactory.setDataPolicy(resolvedDataPolicy);
|
||||
setDataPolicy(resolvedDataPolicy);
|
||||
}
|
||||
else if (DataPolicy.PRELOADED.equals(dataPolicy)) {
|
||||
|
||||
// NOTE this is safe since a LOCAL Scoped PRELOADED Region requiring persistence can be satisfied with
|
||||
// PERSISTENT_REPLICATE, per the RegionShortcut.LOCAL_PERSISTENT
|
||||
DataPolicy resolvedDataPolicy = (isPersistent() ? DataPolicy.PERSISTENT_REPLICATE : DataPolicy.PRELOADED);
|
||||
|
||||
regionFactory.setDataPolicy(resolvedDataPolicy);
|
||||
setDataPolicy(resolvedDataPolicy);
|
||||
}
|
||||
else if (DataPolicy.PERSISTENT_REPLICATE.equals(dataPolicy)
|
||||
&& RegionShortcutWrapper.valueOf(getShortcut()).isPersistent()) {
|
||||
|
||||
regionFactory.setDataPolicy(dataPolicy);
|
||||
setDataPolicy(dataPolicy);
|
||||
}
|
||||
else {
|
||||
throw newIllegalArgumentException("Data Policy [%s] is not supported for Local Regions", dataPolicy);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the Data Policy used by this "local" GemFire Region (i.e. locally Scoped; Scope.LOCAL) based on the
|
||||
* enumerated value from org.apache.geode.cache.RegionShortcuts (LOCAL, LOCAL_PERSISTENT, LOCAL_HEAP_LRU,
|
||||
* LOCAL_OVERFLOW, and LOCAL_PERSISTENT_OVERFLOW), but without consideration of the Eviction settings.
|
||||
*
|
||||
* @param regionFactory the GemFire RegionFactory used to created the Local Region.
|
||||
* @param persistent a boolean value indicating whether the Local Region should persist it's data.
|
||||
* @param dataPolicy requested Data Policy as set by the user in the Spring GemFire configuration meta-data.
|
||||
* @see org.apache.geode.cache.DataPolicy
|
||||
* @see org.apache.geode.cache.RegionFactory
|
||||
* @see org.apache.geode.cache.RegionShortcut
|
||||
*/
|
||||
@Override
|
||||
protected void resolveDataPolicy(RegionFactory<K, V> regionFactory, Boolean persistent, String dataPolicy) {
|
||||
|
||||
DataPolicy resolvedDataPolicy = null;
|
||||
|
||||
if (dataPolicy != null) {
|
||||
resolvedDataPolicy = new DataPolicyConverter().convert(dataPolicy);
|
||||
Assert.notNull(resolvedDataPolicy, String.format("Data Policy [%s] is invalid", dataPolicy));
|
||||
}
|
||||
|
||||
resolveDataPolicy(regionFactory, persistent, resolvedDataPolicy);
|
||||
}
|
||||
}
|
||||
@@ -1,243 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
|
||||
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeIterable;
|
||||
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import org.apache.geode.distributed.Locator;
|
||||
import org.apache.geode.distributed.LocatorLauncher;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.data.gemfire.config.annotation.LocatorConfigurer;
|
||||
import org.springframework.data.gemfire.support.AbstractFactoryBeanSupport;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Spring {@link FactoryBean} used to configure and initialize (bootstrap) an Apache Geode or Pivotal GemFire
|
||||
* {@link Locator} using the {@link LocatorLauncher} class.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see java.util.Properties
|
||||
* @see org.apache.geode.distributed.Locator
|
||||
* @see org.apache.geode.distributed.LocatorLauncher
|
||||
* @see org.springframework.beans.factory.FactoryBean
|
||||
* @see org.springframework.beans.factory.InitializingBean
|
||||
* @see org.springframework.data.gemfire.config.annotation.LocatorConfigurer
|
||||
* @see org.springframework.data.gemfire.support.AbstractFactoryBeanSupport
|
||||
* @since 2.2.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class LocatorFactoryBean extends AbstractFactoryBeanSupport<Locator> implements InitializingBean {
|
||||
|
||||
public static final int DEFAULT_PORT = 10334;
|
||||
|
||||
public static final String DEFAULT_LOG_LEVEL = "config";
|
||||
public static final String LOG_LEVEL_PROPERTY = "log-level";
|
||||
|
||||
private Integer port = DEFAULT_PORT;
|
||||
|
||||
private List<LocatorConfigurer> locatorConfigurers = new ArrayList<>();
|
||||
|
||||
private Locator locator;
|
||||
|
||||
private LocatorConfigurer compositeLocatorConfigurer = (beanName, bean) ->
|
||||
nullSafeList(this.locatorConfigurers).forEach(locatorConfigurer ->
|
||||
locatorConfigurer.configure(beanName, bean));
|
||||
|
||||
private LocatorLauncher locatorLauncher;
|
||||
|
||||
private Properties gemfireProperties;
|
||||
|
||||
private String bindAddress;
|
||||
private String hostnameForClients;
|
||||
private String logLevel;
|
||||
private String name;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
applyLocatorConfigurers(getCompositeLocatorConfigurer());
|
||||
init();
|
||||
}
|
||||
|
||||
protected void applyLocatorConfigurers(LocatorConfigurer... locatorConfigurers) {
|
||||
applyLocatorConfigurers(Arrays.asList(nullSafeArray(locatorConfigurers, LocatorConfigurer.class)));
|
||||
}
|
||||
|
||||
protected void applyLocatorConfigurers(Iterable<LocatorConfigurer> locatorConfigurers) {
|
||||
StreamSupport.stream(nullSafeIterable(locatorConfigurers).spliterator(), false)
|
||||
.forEach(locatorConfigurer -> locatorConfigurer.configure(getBeanName(), this));
|
||||
}
|
||||
|
||||
public void init() {
|
||||
|
||||
LocatorLauncher.Builder locatorBuilder = configureGemfireProperties(newLocatorLauncherBuilder());
|
||||
|
||||
getBindAddress().ifPresent(locatorBuilder::setBindAddress);
|
||||
getHostnameForClients().ifPresent(locatorBuilder::setHostnameForClients);
|
||||
getName().ifPresent(locatorBuilder::setMemberName);
|
||||
|
||||
locatorBuilder.set(LOG_LEVEL_PROPERTY, getLogLevel());
|
||||
locatorBuilder.setPort(getPort());
|
||||
|
||||
locatorBuilder = postProcess(locatorBuilder);
|
||||
|
||||
this.locatorLauncher = postProcess(locatorBuilder.build());
|
||||
|
||||
LocatorLauncher.LocatorState locatorState = this.locatorLauncher.start();
|
||||
|
||||
/*
|
||||
if (LocatorLauncher.Status.ONLINE.equals(locatorState.getStatus())) {
|
||||
// log warning
|
||||
}
|
||||
*/
|
||||
|
||||
this.locator = this.locatorLauncher.getLocator();
|
||||
}
|
||||
|
||||
protected LocatorLauncher.Builder configureGemfireProperties(LocatorLauncher.Builder locatorBuilder) {
|
||||
|
||||
Properties gemfireProperties = getGemFireProperties();
|
||||
|
||||
gemfireProperties.stringPropertyNames().stream()
|
||||
.forEach(propertyName -> locatorBuilder.set(propertyName, gemfireProperties.getProperty(propertyName)));
|
||||
|
||||
return locatorBuilder;
|
||||
}
|
||||
|
||||
protected LocatorLauncher.Builder newLocatorLauncherBuilder() {
|
||||
return new LocatorLauncher.Builder();
|
||||
}
|
||||
|
||||
protected LocatorLauncher.Builder postProcess(LocatorLauncher.Builder locatorBuilder) {
|
||||
return locatorBuilder;
|
||||
}
|
||||
|
||||
protected LocatorLauncher postProcess(LocatorLauncher locatorLauncher) {
|
||||
return locatorLauncher;
|
||||
}
|
||||
|
||||
public Locator getLocator() {
|
||||
return this.locator;
|
||||
}
|
||||
|
||||
public LocatorLauncher getLocatorLauncher() {
|
||||
return this.locatorLauncher;
|
||||
}
|
||||
|
||||
@Nullable @Override
|
||||
public Locator getObject() throws Exception {
|
||||
|
||||
Locator locator = getLocator();
|
||||
|
||||
Assert.state(locator != null, "Locator was not configured and initialized");
|
||||
|
||||
return locator;
|
||||
}
|
||||
|
||||
@Nullable @Override
|
||||
public Class<?> getObjectType() {
|
||||
|
||||
Locator locator = getLocator();
|
||||
|
||||
return locator != null ? locator.getClass() : Locator.class;
|
||||
}
|
||||
|
||||
public void setBindAddress(String bindAddress) {
|
||||
this.bindAddress = bindAddress;
|
||||
}
|
||||
|
||||
public Optional<String> getBindAddress() {
|
||||
|
||||
return Optional.ofNullable(this.bindAddress)
|
||||
.filter(StringUtils::hasText);
|
||||
}
|
||||
|
||||
public LocatorConfigurer getCompositeLocatorConfigurer() {
|
||||
return this.compositeLocatorConfigurer;
|
||||
}
|
||||
|
||||
public void setGemFireProperties(Properties gemfireProperties) {
|
||||
this.gemfireProperties = gemfireProperties;
|
||||
}
|
||||
|
||||
public Properties getGemFireProperties() {
|
||||
|
||||
if (this.gemfireProperties == null) {
|
||||
this.gemfireProperties = new Properties();
|
||||
}
|
||||
|
||||
return this.gemfireProperties;
|
||||
}
|
||||
|
||||
public void setHostnameForClients(String hostnameForClients) {
|
||||
this.hostnameForClients = hostnameForClients;
|
||||
}
|
||||
|
||||
public Optional<String> getHostnameForClients() {
|
||||
|
||||
return Optional.ofNullable(this.hostnameForClients)
|
||||
.filter(StringUtils::hasText);
|
||||
}
|
||||
|
||||
public void setLocatorConfigurers(LocatorConfigurer... locatorConfigurers) {
|
||||
setLocatorConfigurers(Arrays.asList(nullSafeArray(locatorConfigurers, LocatorConfigurer.class)));
|
||||
}
|
||||
|
||||
public void setLocatorConfigurers(List<LocatorConfigurer> locatorConfigurers) {
|
||||
Optional.ofNullable(locatorConfigurers).ifPresent(this.locatorConfigurers::addAll);
|
||||
}
|
||||
|
||||
public void setLogLevel(String logLevel) {
|
||||
this.logLevel = logLevel;
|
||||
}
|
||||
|
||||
public String getLogLevel() {
|
||||
return StringUtils.hasText(this.logLevel) ? this.logLevel : DEFAULT_LOG_LEVEL;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Optional<String> getName() {
|
||||
|
||||
return Optional.ofNullable(this.name)
|
||||
.filter(StringUtils::hasText);
|
||||
}
|
||||
|
||||
public void setPort(Integer port) {
|
||||
|
||||
Assert.isTrue(port >= 0 && port < 65536, String.format("Network port [%d] is not valid", port));
|
||||
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public Integer getPort() {
|
||||
return this.port;
|
||||
}
|
||||
}
|
||||
@@ -1,236 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.geode.cache.CacheListener;
|
||||
import org.apache.geode.cache.CacheLoader;
|
||||
import org.apache.geode.cache.CacheWriter;
|
||||
import org.apache.geode.cache.CustomExpiry;
|
||||
import org.apache.geode.cache.ExpirationAttributes;
|
||||
import org.apache.geode.cache.Region;
|
||||
import org.apache.geode.cache.asyncqueue.AsyncEventQueue;
|
||||
import org.apache.geode.cache.wan.GatewaySender;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* The LookupRegionFactoryBean class is a concrete implementation of ResolvableRegionFactoryBean for handling
|
||||
* >gfe:lookup-region/< SDG XML namespace (XSD) elements.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.apache.geode.cache.AttributesMutator
|
||||
* @see org.springframework.data.gemfire.ResolvableRegionFactoryBean
|
||||
* @since 1.6.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class LookupRegionFactoryBean<K, V> extends ResolvableRegionFactoryBean<K, V> {
|
||||
|
||||
private AsyncEventQueue[] asyncEventQueues;
|
||||
|
||||
private Boolean cloningEnabled;
|
||||
private Boolean enableStatistics;
|
||||
|
||||
private CacheListener<K, V>[] cacheListeners;
|
||||
|
||||
private CacheLoader<K, V> cacheLoader;
|
||||
|
||||
private CacheWriter<K, V> cacheWriter;
|
||||
|
||||
private CustomExpiry<K, V> customEntryIdleTimeout;
|
||||
private CustomExpiry<K, V> customEntryTimeToLive;
|
||||
|
||||
private ExpirationAttributes entryIdleTimeout;
|
||||
private ExpirationAttributes entryTimeToLive;
|
||||
private ExpirationAttributes regionIdleTimeout;
|
||||
private ExpirationAttributes regionTimeToLive;
|
||||
|
||||
private GatewaySender[] gatewaySenders;
|
||||
|
||||
private Integer evictionMaximum;
|
||||
|
||||
private String[] asyncEventQueueIds;
|
||||
private String[] gatewaySenderIds;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
|
||||
super.afterPropertiesSet();
|
||||
|
||||
Optional.ofNullable(getRegion().getAttributesMutator()).ifPresent(attributesMutator -> {
|
||||
|
||||
// AsyncEventQueues (AEQ)
|
||||
getConfiguredAsyncEventQueueIds().forEach(attributesMutator::addAsyncEventQueueId);
|
||||
|
||||
// CacheListeners
|
||||
Arrays.stream(nullSafeArray(this.cacheListeners, CacheListener.class))
|
||||
.forEach(attributesMutator::addCacheListener);
|
||||
|
||||
Optional.ofNullable(this.cacheLoader).ifPresent(attributesMutator::setCacheLoader);
|
||||
Optional.ofNullable(this.cacheWriter).ifPresent(attributesMutator::setCacheWriter);
|
||||
Optional.ofNullable(this.cloningEnabled).ifPresent(attributesMutator::setCloningEnabled);
|
||||
|
||||
// Eviction
|
||||
Optional.ofNullable(attributesMutator.getEvictionAttributesMutator())
|
||||
.ifPresent(evictionAttributesMutator -> Optional.ofNullable(this.evictionMaximum)
|
||||
.ifPresent(evictionAttributesMutator::setMaximum));
|
||||
|
||||
// Expiration
|
||||
if (isStatisticsEnabled()) {
|
||||
|
||||
assertStatisticsEnabled();
|
||||
|
||||
Optional.ofNullable(this.customEntryIdleTimeout).ifPresent(attributesMutator::setCustomEntryIdleTimeout);
|
||||
Optional.ofNullable(this.customEntryTimeToLive).ifPresent(attributesMutator::setCustomEntryTimeToLive);
|
||||
Optional.ofNullable(this.entryIdleTimeout).ifPresent(attributesMutator::setEntryIdleTimeout);
|
||||
Optional.ofNullable(this.entryTimeToLive).ifPresent(attributesMutator::setEntryTimeToLive);
|
||||
Optional.ofNullable(this.regionIdleTimeout).ifPresent(attributesMutator::setRegionIdleTimeout);
|
||||
Optional.ofNullable(this.regionTimeToLive).ifPresent(attributesMutator::setRegionTimeToLive);
|
||||
}
|
||||
|
||||
// GatewaySenders
|
||||
getConfiguredGatewaySenderIds().forEach(attributesMutator::addGatewaySenderId);
|
||||
});
|
||||
}
|
||||
|
||||
private Set<String> getConfiguredAsyncEventQueueIds() {
|
||||
|
||||
Set<String> asyncEventQueueIds = new HashSet<>();
|
||||
|
||||
Arrays.stream(nullSafeArray(this.asyncEventQueues, AsyncEventQueue.class))
|
||||
.map(AsyncEventQueue::getId)
|
||||
.collect(Collectors.toCollection(() -> asyncEventQueueIds));
|
||||
|
||||
Arrays.stream(nullSafeArray(this.asyncEventQueueIds, String.class))
|
||||
.filter(StringUtils::hasText)
|
||||
.map(String::trim)
|
||||
.collect(Collectors.toCollection(() -> asyncEventQueueIds));
|
||||
|
||||
return asyncEventQueueIds;
|
||||
}
|
||||
|
||||
private Set<String> getConfiguredGatewaySenderIds() {
|
||||
|
||||
Set<String> gatewaySenderIds = new HashSet<>();
|
||||
|
||||
Arrays.stream(nullSafeArray(this.gatewaySenders, GatewaySender.class))
|
||||
.map(GatewaySender::getId)
|
||||
.collect(Collectors.toCollection(() -> gatewaySenderIds));
|
||||
|
||||
Arrays.stream(nullSafeArray(this.gatewaySenderIds, String.class))
|
||||
.filter(StringUtils::hasText)
|
||||
.map(String::trim)
|
||||
.collect(Collectors.toCollection(() -> gatewaySenderIds));
|
||||
|
||||
return gatewaySenderIds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isLookupEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setAsyncEventQueues(AsyncEventQueue[] asyncEventQueues) {
|
||||
this.asyncEventQueues = asyncEventQueues;
|
||||
}
|
||||
|
||||
public void setAsyncEventQueueIds(String[] asyncEventQueueIds) {
|
||||
this.asyncEventQueueIds = asyncEventQueueIds;
|
||||
}
|
||||
|
||||
public void setCacheListeners(CacheListener<K, V>[] cacheListeners) {
|
||||
this.cacheListeners = cacheListeners;
|
||||
}
|
||||
|
||||
public void setCacheLoader(CacheLoader<K, V> cacheLoader) {
|
||||
this.cacheLoader = cacheLoader;
|
||||
}
|
||||
|
||||
public void setCacheWriter(CacheWriter<K, V> cacheWriter) {
|
||||
this.cacheWriter = cacheWriter;
|
||||
}
|
||||
|
||||
public void setCloningEnabled(Boolean cloningEnabled) {
|
||||
this.cloningEnabled = cloningEnabled;
|
||||
}
|
||||
|
||||
public void setCustomEntryIdleTimeout(CustomExpiry<K, V> customEntryIdleTimeout) {
|
||||
setStatisticsEnabled(customEntryIdleTimeout != null);
|
||||
this.customEntryIdleTimeout = customEntryIdleTimeout;
|
||||
}
|
||||
|
||||
public void setCustomEntryTimeToLive(CustomExpiry<K, V> customEntryTimeToLive) {
|
||||
setStatisticsEnabled(customEntryTimeToLive != null);
|
||||
this.customEntryTimeToLive = customEntryTimeToLive;
|
||||
}
|
||||
|
||||
public void setEntryIdleTimeout(ExpirationAttributes entryIdleTimeout) {
|
||||
setStatisticsEnabled(entryIdleTimeout != null);
|
||||
this.entryIdleTimeout = entryIdleTimeout;
|
||||
}
|
||||
|
||||
public void setEntryTimeToLive(ExpirationAttributes entryTimeToLive) {
|
||||
setStatisticsEnabled(entryTimeToLive != null);
|
||||
this.entryTimeToLive = entryTimeToLive;
|
||||
}
|
||||
|
||||
public void setEvictionMaximum(final Integer evictionMaximum) {
|
||||
this.evictionMaximum = evictionMaximum;
|
||||
}
|
||||
|
||||
public void setGatewaySenders(GatewaySender[] gatewaySenders) {
|
||||
this.gatewaySenders = gatewaySenders;
|
||||
}
|
||||
|
||||
public void setGatewaySenderIds(String[] gatewaySenderIds) {
|
||||
this.gatewaySenderIds = gatewaySenderIds;
|
||||
}
|
||||
|
||||
public void setRegionIdleTimeout(ExpirationAttributes regionIdleTimeout) {
|
||||
setStatisticsEnabled(regionIdleTimeout != null);
|
||||
this.regionIdleTimeout = regionIdleTimeout;
|
||||
}
|
||||
|
||||
public void setRegionTimeToLive(ExpirationAttributes regionTimeToLive) {
|
||||
setStatisticsEnabled(regionTimeToLive != null);
|
||||
this.regionTimeToLive = regionTimeToLive;
|
||||
}
|
||||
|
||||
public void setStatisticsEnabled(Boolean enableStatistics) {
|
||||
this.enableStatistics = enableStatistics;
|
||||
}
|
||||
|
||||
protected boolean isStatisticsEnabled() {
|
||||
return Boolean.TRUE.equals(this.enableStatistics);
|
||||
}
|
||||
|
||||
private void assertStatisticsEnabled() {
|
||||
|
||||
Region localRegion = getRegion();
|
||||
|
||||
Assert.state(localRegion.getAttributes().getStatisticsEnabled(),
|
||||
String.format("Statistics for Region [%s] must be enabled to change Entry & Region TTL/TTI Expiration settings",
|
||||
localRegion.getFullPath()));
|
||||
}
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeList;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.geode.cache.FixedPartitionAttributes;
|
||||
import org.apache.geode.cache.PartitionAttributes;
|
||||
import org.apache.geode.cache.PartitionAttributesFactory;
|
||||
import org.apache.geode.cache.PartitionResolver;
|
||||
import org.apache.geode.cache.partition.PartitionListener;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.data.gemfire.support.AbstractFactoryBeanSupport;
|
||||
|
||||
/**
|
||||
* Spring {@link FactoryBean} for creating {@link PartitionAttributes}.
|
||||
*
|
||||
* Eliminates the need to use a XML 'factory-method' tag and allows the attributes properties to be set directly.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author David Turanski
|
||||
* @author John Blum
|
||||
* @see org.apache.geode.cache.FixedPartitionAttributes
|
||||
* @see org.apache.geode.cache.PartitionAttributes
|
||||
* @see org.apache.geode.cache.PartitionAttributesFactory
|
||||
* @see org.apache.geode.cache.PartitionResolver
|
||||
* @see org.apache.geode.cache.partition.PartitionListener
|
||||
* @see org.springframework.beans.factory.FactoryBean
|
||||
* @see org.springframework.beans.factory.InitializingBean
|
||||
* @see org.springframework.data.gemfire.support.AbstractFactoryBeanSupport
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class PartitionAttributesFactoryBean<K, V> extends AbstractFactoryBeanSupport<PartitionAttributes<K, V>>
|
||||
implements InitializingBean {
|
||||
|
||||
private List<PartitionListener> partitionListeners;
|
||||
|
||||
private PartitionAttributes<K, V> partitionAttributes;
|
||||
|
||||
private final PartitionAttributesFactory<K, V> partitionAttributesFactory = new PartitionAttributesFactory<>();
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
nullSafeList(partitionListeners).forEach(partitionAttributesFactory::addPartitionListener);
|
||||
this.partitionAttributes = partitionAttributesFactory.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PartitionAttributes<K, V> getObject() throws Exception {
|
||||
return this.partitionAttributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return (this.partitionAttributes != null ? this.partitionAttributes.getClass() : PartitionAttributes.class);
|
||||
}
|
||||
|
||||
public void setColocatedWith(String collocatedWith) {
|
||||
this.partitionAttributesFactory.setColocatedWith(collocatedWith);
|
||||
}
|
||||
|
||||
public void setFixedPartitionAttributes(List<FixedPartitionAttributes> fixedPartitionAttributes) {
|
||||
nullSafeList(fixedPartitionAttributes).forEach(this.partitionAttributesFactory::addFixedPartitionAttributes);
|
||||
}
|
||||
|
||||
public void setLocalMaxMemory(int mb) {
|
||||
this.partitionAttributesFactory.setLocalMaxMemory(mb);
|
||||
}
|
||||
|
||||
public void setPartitionListeners(List<PartitionListener> partitionListeners) {
|
||||
this.partitionListeners = partitionListeners;
|
||||
}
|
||||
|
||||
public void setPartitionResolver(PartitionResolver<K, V> resolver) {
|
||||
this.partitionAttributesFactory.setPartitionResolver(resolver);
|
||||
}
|
||||
|
||||
public void setRecoveryDelay(long recoveryDelay) {
|
||||
this.partitionAttributesFactory.setRecoveryDelay(recoveryDelay);
|
||||
}
|
||||
|
||||
public void setRedundantCopies(int redundantCopies) {
|
||||
this.partitionAttributesFactory.setRedundantCopies(redundantCopies);
|
||||
}
|
||||
|
||||
public void setStartupRecoveryDelay(long startupRecoveryDelay) {
|
||||
this.partitionAttributesFactory.setStartupRecoveryDelay(startupRecoveryDelay);
|
||||
}
|
||||
|
||||
public void setTotalMaxMemory(long megabytes) {
|
||||
this.partitionAttributesFactory.setTotalMaxMemory(megabytes);
|
||||
}
|
||||
|
||||
public void setTotalNumBuckets(int numBuckets) {
|
||||
this.partitionAttributesFactory.setTotalNumBuckets(numBuckets);
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import org.apache.geode.cache.DataPolicy;
|
||||
import org.apache.geode.cache.RegionFactory;
|
||||
|
||||
import org.springframework.data.gemfire.util.RegionUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author David Turanski
|
||||
* @author John Blum
|
||||
*/
|
||||
public class PartitionedRegionFactoryBean<K, V> extends PeerRegionFactoryBean<K, V> {
|
||||
|
||||
@Override
|
||||
protected void resolveDataPolicy(RegionFactory<K, V> regionFactory, Boolean persistent, DataPolicy dataPolicy) {
|
||||
|
||||
if (dataPolicy == null) {
|
||||
dataPolicy = isPersistent() ? DataPolicy.PERSISTENT_PARTITION : DataPolicy.PARTITION;
|
||||
}
|
||||
else {
|
||||
// Validate that the user-defined Data Policy matches the appropriate Spring GemFire XML namespace
|
||||
// configuration meta-data element for Region (i.e. <gfe:partitioned-region .../>)!
|
||||
Assert.isTrue(dataPolicy.withPartitioning(), String.format(
|
||||
"Data Policy [%s] is not supported in Partitioned Regions.", dataPolicy));
|
||||
}
|
||||
|
||||
// Validate the data-policy and persistent attributes are compatible when specified!
|
||||
RegionUtils.assertDataPolicyAndPersistentAttributeAreCompatible(dataPolicy, persistent);
|
||||
|
||||
regionFactory.setDataPolicy(dataPolicy);
|
||||
setDataPolicy(dataPolicy);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resolveDataPolicy(RegionFactory<K, V> regionFactory, Boolean persistent, String dataPolicy) {
|
||||
|
||||
DataPolicy resolvedDataPolicy = null;
|
||||
|
||||
if (dataPolicy != null) {
|
||||
resolvedDataPolicy = new DataPolicyConverter().convert(dataPolicy);
|
||||
Assert.notNull(resolvedDataPolicy, String.format("Data Policy [%s] is invalid.", dataPolicy));
|
||||
}
|
||||
|
||||
resolveDataPolicy(regionFactory, persistent, resolvedDataPolicy);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.geode.cache.AttributesFactory;
|
||||
import org.apache.geode.cache.RegionAttributes;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.data.gemfire.util.ArrayUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Spring-friendly bean for creating {@link RegionAttributes}. Eliminates the need of using a XML 'factory-method' tag.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author John Blum
|
||||
* @see org.springframework.beans.factory.FactoryBean
|
||||
* @see org.springframework.beans.factory.InitializingBean
|
||||
* @see org.apache.geode.cache.AttributesFactory
|
||||
* @see org.apache.geode.cache.RegionAttributes
|
||||
*/
|
||||
@SuppressWarnings({ "unused" })
|
||||
public class RegionAttributesFactoryBean<K, V> extends AttributesFactory<K, V>
|
||||
implements FactoryBean<RegionAttributes>, InitializingBean {
|
||||
|
||||
private RegionAttributes<K, V> regionAttributes;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
this.regionAttributes = super.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegionAttributes<K, V> getObject() throws Exception {
|
||||
return this.regionAttributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
|
||||
return this.regionAttributes != null
|
||||
? this.regionAttributes.getClass()
|
||||
: RegionAttributes.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setAsyncEventQueueIds(String[] asyncEventQueueIds) {
|
||||
|
||||
Arrays.stream(ArrayUtils.nullSafeArray(asyncEventQueueIds, String.class))
|
||||
.filter(StringUtils::hasText)
|
||||
.map(String::trim)
|
||||
.forEach(this::addAsyncEventQueueId);
|
||||
}
|
||||
|
||||
public void setIndexUpdateType(IndexMaintenancePolicyType indexUpdateType) {
|
||||
indexUpdateType.setIndexMaintenance(this);
|
||||
}
|
||||
|
||||
public void setGatewaySenderIds(String[] gatewaySenderIds) {
|
||||
|
||||
Arrays.stream(ArrayUtils.nullSafeArray(gatewaySenderIds, String.class))
|
||||
.filter(StringUtils::hasText)
|
||||
.map(String::trim)
|
||||
.forEach(this::addGatewaySenderId);
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import org.apache.geode.cache.RegionShortcut;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
/**
|
||||
* The RegionShortcutConverter class is a Spring Converter implementation converting String value Region Shortcut
|
||||
* representations into actual GemFire RegionShortcut enumerated values.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.core.convert.converter.Converter
|
||||
* @see org.apache.geode.cache.RegionShortcut
|
||||
* @since 1.3.4
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class RegionShortcutConverter implements Converter<String, RegionShortcut> {
|
||||
|
||||
/**
|
||||
* Converts the String value to upper case, trimming all whitespace. This method guards against null values
|
||||
* and returns the "null" String if value is null.
|
||||
*
|
||||
* @param value the String to convert to a trimmed, upper case value.
|
||||
* @return a trimmed, upper case value of the specified String, or "null" if the String value reference is null.
|
||||
* @see java.lang.String#toUpperCase()
|
||||
* @see java.lang.String#trim()
|
||||
* @see java.lang.String#valueOf(Object)
|
||||
*/
|
||||
protected static String toUpperCase(final String value) {
|
||||
return (value != null ? value.toUpperCase().trim() : String.valueOf(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the source String representation of a Region Shortcut into a RegionShortcut enumerated value.
|
||||
*
|
||||
* @param source the String representation of the Region Shortcut to convert.
|
||||
* @return a RegionShortcut enumerated value for the String representation.
|
||||
* @throws IllegalArgumentException if the String source is not a valid RegionShortcut enumerated value.
|
||||
* @see org.apache.geode.cache.RegionShortcut#valueOf(String)
|
||||
*/
|
||||
@Override
|
||||
public RegionShortcut convert(final String source) {
|
||||
return RegionShortcut.valueOf(toUpperCase(source));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.geode.cache.DataPolicy;
|
||||
import org.apache.geode.cache.RegionShortcut;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Spring {@link Converter} to convert a {@link RegionShortcut} into a {@link DataPolicy}.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.apache.geode.cache.DataPolicy
|
||||
* @see org.apache.geode.cache.RegionShortcut
|
||||
* @see org.springframework.core.convert.converter.Converter
|
||||
* @see org.springframework.data.gemfire.RegionShortcutWrapper
|
||||
* @since 2.0.2
|
||||
*/
|
||||
public class RegionShortcutToDataPolicyConverter implements Converter<RegionShortcut, DataPolicy> {
|
||||
|
||||
public static final RegionShortcutToDataPolicyConverter INSTANCE = new RegionShortcutToDataPolicyConverter();
|
||||
|
||||
/**
|
||||
* Converts the given {@link RegionShortcut} into a corresponding {@link DataPolicy}.
|
||||
*
|
||||
* @param regionShortcut {@link RegionShortcut} to convert.
|
||||
* @return a corresponding {@link DataPolicy} for the given {@link RegionShortcut}.
|
||||
* @see org.apache.geode.cache.RegionShortcut
|
||||
* @see org.apache.geode.cache.DataPolicy
|
||||
*/
|
||||
@Nullable @Override
|
||||
public DataPolicy convert(RegionShortcut regionShortcut) {
|
||||
|
||||
return Optional.ofNullable(RegionShortcutWrapper.valueOf(regionShortcut))
|
||||
.map(RegionShortcutWrapper::getDataPolicy)
|
||||
.orElse(DataPolicy.DEFAULT);
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import org.apache.geode.cache.DataPolicy;
|
||||
import org.apache.geode.cache.RegionShortcut;
|
||||
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* The RegionShortcutWrapper enum is a Java enumerated type that wraps GemFire's RegionShortcuts
|
||||
* with Spring Data GemFire RegionShortcutWrapper enumerated values.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.apache.geode.cache.RegionShortcut
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public enum RegionShortcutWrapper {
|
||||
|
||||
LOCAL(RegionShortcut.LOCAL, DataPolicy.NORMAL),
|
||||
LOCAL_HEAP_LRU(RegionShortcut.LOCAL_HEAP_LRU, DataPolicy.NORMAL),
|
||||
LOCAL_OVERFLOW(RegionShortcut.LOCAL_OVERFLOW, DataPolicy.NORMAL),
|
||||
LOCAL_PERSISTENT(RegionShortcut.LOCAL_PERSISTENT, DataPolicy.PERSISTENT_REPLICATE),
|
||||
LOCAL_PERSISTENT_OVERFLOW(RegionShortcut.LOCAL_PERSISTENT_OVERFLOW, DataPolicy.PERSISTENT_REPLICATE),
|
||||
PARTITION(RegionShortcut.PARTITION, DataPolicy.PARTITION),
|
||||
PARTITION_HEAP_LRU(RegionShortcut.PARTITION_HEAP_LRU, DataPolicy.PARTITION),
|
||||
PARTITION_OVERFLOW(RegionShortcut.PARTITION_OVERFLOW, DataPolicy.PARTITION),
|
||||
PARTITION_PERSISTENT(RegionShortcut.PARTITION_PERSISTENT, DataPolicy.PERSISTENT_PARTITION),
|
||||
PARTITION_PERSISTENT_OVERFLOW(RegionShortcut.PARTITION_PERSISTENT_OVERFLOW, DataPolicy.PERSISTENT_PARTITION),
|
||||
PARTITION_PROXY(RegionShortcut.PARTITION_PROXY, DataPolicy.PARTITION),
|
||||
PARTITION_PROXY_REDUNDANT(RegionShortcut.PARTITION_PROXY_REDUNDANT, DataPolicy.PARTITION),
|
||||
PARTITION_REDUNDANT(RegionShortcut.PARTITION_REDUNDANT, DataPolicy.PARTITION),
|
||||
PARTITION_REDUNDANT_HEAP_LRU(RegionShortcut.PARTITION_REDUNDANT_HEAP_LRU, DataPolicy.PARTITION),
|
||||
PARTITION_REDUNDANT_OVERFLOW(RegionShortcut.PARTITION_REDUNDANT_OVERFLOW, DataPolicy.PARTITION),
|
||||
PARTITION_REDUNDANT_PERSISTENT(RegionShortcut.PARTITION_REDUNDANT_PERSISTENT, DataPolicy.PERSISTENT_PARTITION),
|
||||
PARTITION_REDUNDANT_PERSISTENT_OVERFLOW(RegionShortcut.PARTITION_REDUNDANT_PERSISTENT_OVERFLOW, DataPolicy.PERSISTENT_PARTITION),
|
||||
REPLICATE(RegionShortcut.REPLICATE, DataPolicy.REPLICATE),
|
||||
REPLICATE_HEAP_LRU(RegionShortcut.REPLICATE_HEAP_LRU, DataPolicy.REPLICATE),
|
||||
REPLICATE_OVERFLOW(RegionShortcut.REPLICATE_OVERFLOW, DataPolicy.REPLICATE),
|
||||
REPLICATE_PERSISTENT(RegionShortcut.REPLICATE_PERSISTENT, DataPolicy.PERSISTENT_REPLICATE),
|
||||
REPLICATE_PERSISTENT_OVERFLOW(RegionShortcut.REPLICATE_PERSISTENT_OVERFLOW, DataPolicy.PERSISTENT_REPLICATE),
|
||||
REPLICATE_PROXY(RegionShortcut.REPLICATE_PROXY, DataPolicy.EMPTY),
|
||||
UNSPECIFIED(null, null);
|
||||
|
||||
private final DataPolicy dataPolicy;
|
||||
|
||||
private final RegionShortcut regionShortcut;
|
||||
|
||||
RegionShortcutWrapper(RegionShortcut regionShortcut, DataPolicy dataPolicy) {
|
||||
this.regionShortcut = regionShortcut;
|
||||
this.dataPolicy = dataPolicy;
|
||||
}
|
||||
|
||||
public static RegionShortcutWrapper valueOf(RegionShortcut regionShortcut) {
|
||||
|
||||
for (RegionShortcutWrapper wrapper : values()) {
|
||||
if (ObjectUtils.nullSafeEquals(wrapper.getRegionShortcut(), regionShortcut)) {
|
||||
return wrapper;
|
||||
}
|
||||
}
|
||||
|
||||
return RegionShortcutWrapper.UNSPECIFIED;
|
||||
}
|
||||
|
||||
public DataPolicy getDataPolicy() {
|
||||
return this.dataPolicy;
|
||||
}
|
||||
|
||||
public RegionShortcut getRegionShortcut() {
|
||||
return this.regionShortcut;
|
||||
}
|
||||
|
||||
public boolean isHeapLru() {
|
||||
return name().contains("HEAP_LRU");
|
||||
}
|
||||
|
||||
public boolean isLocal() {
|
||||
return name().contains("LOCAL");
|
||||
}
|
||||
|
||||
public boolean isOverflow() {
|
||||
return name().contains("OVERFLOW");
|
||||
}
|
||||
|
||||
public boolean isPartition() {
|
||||
return name().contains("PARTITION");
|
||||
}
|
||||
|
||||
public boolean isPersistent() {
|
||||
return name().contains("PERSISTENT");
|
||||
}
|
||||
|
||||
public boolean isPersistentOverflow() {
|
||||
return (isOverflow() && isPersistent());
|
||||
}
|
||||
|
||||
public boolean isProxy() {
|
||||
return name().contains("PROXY");
|
||||
}
|
||||
|
||||
public boolean isRedundant() {
|
||||
return name().contains("REDUNDANT");
|
||||
}
|
||||
|
||||
public boolean isReplicate() {
|
||||
return name().contains("REPLICATE");
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import org.apache.geode.cache.DataPolicy;
|
||||
import org.apache.geode.cache.RegionFactory;
|
||||
|
||||
import org.springframework.data.gemfire.util.RegionUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author David Turanski
|
||||
* @author John Blum
|
||||
*/
|
||||
public class ReplicatedRegionFactoryBean<K, V> extends PeerRegionFactoryBean<K, V> {
|
||||
|
||||
@Override
|
||||
protected void resolveDataPolicy(RegionFactory<K, V> regionFactory, Boolean persistent, DataPolicy dataPolicy) {
|
||||
|
||||
if (dataPolicy == null) {
|
||||
dataPolicy = isPersistent() ? DataPolicy.PERSISTENT_REPLICATE : DataPolicy.REPLICATE;
|
||||
}
|
||||
else if (DataPolicy.EMPTY.equals(dataPolicy)) {
|
||||
dataPolicy = DataPolicy.EMPTY;
|
||||
}
|
||||
else {
|
||||
// Validate that the user-defined Data Policy matches the appropriate Spring GemFire XML namespace
|
||||
// configuration meta-data element for the Region (i.e. <gfe:replicated-region .../>)!
|
||||
Assert.isTrue(dataPolicy.withReplication(), String.format(
|
||||
"Data Policy [%s] is not supported in Replicated Regions.", dataPolicy));
|
||||
}
|
||||
|
||||
// Validate that the data-policy and persistent attributes are compatible when both are specified!
|
||||
RegionUtils.assertDataPolicyAndPersistentAttributeAreCompatible(dataPolicy, persistent);
|
||||
|
||||
regionFactory.setDataPolicy(dataPolicy);
|
||||
setDataPolicy(dataPolicy);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resolveDataPolicy(RegionFactory<K, V> regionFactory, Boolean persistent, String dataPolicy) {
|
||||
|
||||
DataPolicy resolvedDataPolicy = null;
|
||||
|
||||
if (dataPolicy != null) {
|
||||
resolvedDataPolicy = new DataPolicyConverter().convert(dataPolicy);
|
||||
Assert.notNull(resolvedDataPolicy, String.format("Data Policy [%s] is invalid.", dataPolicy));
|
||||
}
|
||||
|
||||
resolveDataPolicy(regionFactory, persistent, resolvedDataPolicy);
|
||||
}
|
||||
}
|
||||
@@ -1,318 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newRuntimeException;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.geode.cache.GemFireCache;
|
||||
import org.apache.geode.cache.Region;
|
||||
|
||||
import org.springframework.beans.factory.BeanInitializationException;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.data.gemfire.support.AbstractFactoryBeanSupport;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Spring {@link FactoryBean} for looking up {@link Region Regions}.
|
||||
*
|
||||
* If lookups are disabled or the {@link Region} does not exist, an exception is thrown.
|
||||
*
|
||||
* For declaring and configuring new Regions, see {@link PeerRegionFactoryBean}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author John Blum
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
* @see org.apache.geode.cache.Region
|
||||
* @see org.springframework.beans.factory.FactoryBean
|
||||
* @see org.springframework.beans.factory.InitializingBean
|
||||
* @see org.springframework.data.gemfire.support.AbstractFactoryBeanSupport
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class ResolvableRegionFactoryBean<K, V> extends AbstractFactoryBeanSupport<Region<K, V>>
|
||||
implements InitializingBean {
|
||||
|
||||
private Boolean lookupEnabled = false;
|
||||
|
||||
private GemFireCache cache;
|
||||
|
||||
private Region<?, ?> parent;
|
||||
|
||||
private Resource snapshot;
|
||||
|
||||
private volatile Region<K, V> region;
|
||||
|
||||
private String name;
|
||||
private String regionName;
|
||||
|
||||
/**
|
||||
* Initializes this {@link ResolvableRegionFactoryBean} after properties have been set by the Spring container.
|
||||
*
|
||||
* @throws Exception if initialization fails.
|
||||
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
||||
* @see #createRegion(GemFireCache, String)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("all")
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
|
||||
GemFireCache cache = requireCache();
|
||||
|
||||
String regionName = requireRegionName();
|
||||
|
||||
synchronized (cache) {
|
||||
|
||||
setRegion(isLookupEnabled()
|
||||
? Optional.ofNullable(getParent())
|
||||
.map(parentRegion -> parentRegion.<K, V>getSubregion(regionName))
|
||||
.orElseGet(() -> cache.<K, V>getRegion(regionName))
|
||||
: null);
|
||||
|
||||
if (getRegion() != null) {
|
||||
logInfo("Found Region [%1$s] in Cache [%2$s]", regionName, cache.getName());
|
||||
}
|
||||
else {
|
||||
logInfo("Falling back to creating Region [%1$s] in Cache [%2$s]",
|
||||
regionName, cache.getName());
|
||||
|
||||
setRegion(postProcess(loadSnapshot(createRegion(cache, regionName))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private GemFireCache requireCache() {
|
||||
|
||||
GemFireCache cache = getCache();
|
||||
|
||||
Assert.notNull(cache, "Cache is required");
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
private String requireRegionName() {
|
||||
|
||||
String regionName = resolveRegionName();
|
||||
|
||||
Assert.hasText(regionName, "regionName, name or the beanName property must be set");
|
||||
|
||||
return regionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the {@link String name} of the {@link Region}.
|
||||
*
|
||||
* @return a {@link String} containing the name of the {@link Region}.
|
||||
* @see org.apache.geode.cache.Region#getName()
|
||||
*/
|
||||
public String resolveRegionName() {
|
||||
return StringUtils.hasText(this.regionName) ? this.regionName
|
||||
: (StringUtils.hasText(this.name) ? this.name : getBeanName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Region} with the given {@link String name}.
|
||||
*
|
||||
* This method gets called when a {@link Region} with the specified {@link String name} does not already exist.
|
||||
* By default, this method implementation throws a {@link BeanInitializationException} and it is expected
|
||||
* that {@link Class subclasses} will override this method.
|
||||
*
|
||||
* @param cache reference to the {@link GemFireCache}.
|
||||
* @param regionName {@link String name} of the new {@link Region}.
|
||||
* @return a new {@link Region} with the given {@link String name}.
|
||||
* @throws BeanInitializationException by default unless a {@link Class subclass} overrides this method.
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
* @see org.apache.geode.cache.Region
|
||||
*/
|
||||
protected Region<K, V> createRegion(GemFireCache cache, String regionName) throws Exception {
|
||||
throw new BeanInitializationException(
|
||||
String.format("Region [%1$s] in Cache [%2$s] not found", regionName, cache));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the configured data {@link Resource snapshot} into the given {@link Region}.
|
||||
*
|
||||
* @param region {@link Region} to load.
|
||||
* @return the given {@link Region}.
|
||||
* @throws RuntimeException if the snapshot load fails.
|
||||
* @see org.apache.geode.cache.Region#loadSnapshot(InputStream)
|
||||
*/
|
||||
protected Region<K, V> loadSnapshot(Region<K, V> region) {
|
||||
|
||||
Optional.ofNullable(this.snapshot).ifPresent(snapshot -> {
|
||||
try {
|
||||
region.loadSnapshot(snapshot.getInputStream());
|
||||
}
|
||||
catch (Exception cause) {
|
||||
throw newRuntimeException(cause, "Failed to load snapshot [%s]", snapshot);
|
||||
}
|
||||
});
|
||||
|
||||
return region;
|
||||
}
|
||||
|
||||
/**
|
||||
* Post-process the {@link Region} created by this {@link PeerRegionFactoryBean}.
|
||||
*
|
||||
* @param region {@link Region} to process.
|
||||
* @see org.apache.geode.cache.Region
|
||||
*/
|
||||
protected Region<K, V> postProcess(Region<K, V> region) {
|
||||
return region;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object reference to the {@link Region} created by this {@link ResolvableRegionFactoryBean}.
|
||||
*
|
||||
* @return an object reference to the {@link Region} created by this {@link ResolvableRegionFactoryBean}.
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObject()
|
||||
* @see org.apache.geode.cache.Region
|
||||
* @see #getRegion()
|
||||
*/
|
||||
@Override
|
||||
public Region<K, V> getObject() throws Exception {
|
||||
return getRegion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Class} type of the {@link Region} produced by this {@link ResolvableRegionFactoryBean}.
|
||||
*
|
||||
* @return the {@link Class} type of the {@link Region} produced by this {@link ResolvableRegionFactoryBean}.
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Class<?> getObjectType() {
|
||||
return Optional.ofNullable(getRegion()).map(Region::getClass).orElse((Class) Region.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the {@link GemFireCache} used to create the {@link Region}.
|
||||
*
|
||||
* @return a reference to the {@link GemFireCache} used to create the {@link Region}..
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
*/
|
||||
public GemFireCache getCache() {
|
||||
return this.cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a reference to the {@link GemFireCache} used to create the {@link Region}.
|
||||
*
|
||||
* @param cache reference to the {@link GemFireCache}.
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
*/
|
||||
public void setCache(GemFireCache cache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
public boolean isLookupEnabled() {
|
||||
return Boolean.TRUE.equals(getLookupEnabled());
|
||||
}
|
||||
|
||||
public void setLookupEnabled(Boolean lookupEnabled) {
|
||||
this.lookupEnabled = lookupEnabled;
|
||||
}
|
||||
|
||||
public Boolean getLookupEnabled() {
|
||||
return this.lookupEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the cache {@link Region} based on the bean 'name' attribute. If no {@link Region} is found
|
||||
* with the given name, a new one will be created. If no name is given, the value of the 'beanName' property
|
||||
* will be used.
|
||||
*
|
||||
* @param name {@link Region} name.
|
||||
* @see #setBeanName(String)
|
||||
* @see org.apache.geode.cache.Region#getFullPath()
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a reference to the parent {@link Region} to indicated this {@link FactoryBean} represents a GemFire cache
|
||||
* {@link Region Sub-Region}.
|
||||
*
|
||||
* @param parent reference to the parent {@link Region}.
|
||||
* @see org.apache.geode.cache.Region
|
||||
*/
|
||||
public void setParent(Region<?, ?> parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the parent {@link Region} indicating this {@link FactoryBean} represents a GemFire cache
|
||||
* {@link Region Sub-Region}.
|
||||
*
|
||||
* @return a reference to the parent {@link Region} or {@literal null} if this {@link Region}
|
||||
* is not a {@link Region Sub-Region}.
|
||||
* @see org.apache.geode.cache.Region
|
||||
*/
|
||||
protected Region<?, ?> getParent() {
|
||||
return this.parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a reference to the {@link Region} to be resolved by this Spring {@link FactoryBean}.
|
||||
*
|
||||
* @param region reference to the resolvable {@link Region}.
|
||||
* @see org.apache.geode.cache.Region
|
||||
*/
|
||||
protected void setRegion(Region<K, V> region) {
|
||||
this.region = region;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the {@link Region} resolved by this Spring {@link FactoryBean}
|
||||
* during the lookup operation; maybe a new {@link Region}.
|
||||
*
|
||||
* @return a reference to the {@link Region} resolved during lookup.
|
||||
* @see org.apache.geode.cache.Region
|
||||
*/
|
||||
public Region<K, V> getRegion() {
|
||||
return this.region;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the cache {@link Region}. If no {@link Region} is found with the given name,
|
||||
* a new one will be created. If no name is given, the value of the 'name' property will be used.
|
||||
*
|
||||
* @param regionName name of the {@link Region}.
|
||||
* @see #setName(String)
|
||||
* @see org.apache.geode.cache.Region#getName()
|
||||
*/
|
||||
public void setRegionName(String regionName) {
|
||||
this.regionName = regionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the snapshots used for loading a newly <i>created</i> region. That
|
||||
* is, the snapshot will be used <i>only</i> when a new region is created -
|
||||
* if the region already exists, no loading will be performed.
|
||||
*
|
||||
* @see #setName(String)
|
||||
* @param snapshot the snapshot to set
|
||||
*/
|
||||
public void setSnapshot(Resource snapshot) {
|
||||
this.snapshot = snapshot;
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import org.apache.geode.cache.Scope;
|
||||
|
||||
import org.springframework.data.gemfire.support.AbstractPropertyEditorConverterSupport;
|
||||
|
||||
/**
|
||||
* The ScopeConverter class is a Spring Converter and JavaBeans PropertyEditor that converts Strings
|
||||
* into GemFire Scope constant values.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.data.gemfire.support.AbstractPropertyEditorConverterSupport
|
||||
* @see org.apache.geode.cache.Scope
|
||||
* @since 1.6.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class ScopeConverter extends AbstractPropertyEditorConverterSupport<Scope> {
|
||||
|
||||
/**
|
||||
* Converts the given String source into an instance of GemFire Scope.
|
||||
*
|
||||
* @param source the String to convert into a GemFire Scope.
|
||||
* @return a GemFire Scope for the given String.
|
||||
* @throws java.lang.IllegalArgumentException if the String is not a valid GemFire Scope.
|
||||
* @see org.springframework.data.gemfire.ScopeType#getScope(ScopeType)
|
||||
* @see org.springframework.data.gemfire.ScopeType#valueOfIgnoreCase(String)
|
||||
* @see org.apache.geode.cache.Scope#fromString(String)
|
||||
* @see #assertConverted(String, Object, Class)
|
||||
*/
|
||||
@Override
|
||||
public Scope convert(final String source) {
|
||||
try {
|
||||
return Scope.fromString(source);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
return assertConverted(source, ScopeType.getScope(ScopeType.valueOfIgnoreCase(source)), Scope.class);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import org.apache.geode.cache.Scope;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* The ScopeType enum is an enumeration of GemFire Scopes.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.apache.geode.cache.Scope
|
||||
* @since 1.6.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public enum ScopeType {
|
||||
DISTRIBUTED_ACK(Scope.DISTRIBUTED_ACK),
|
||||
DISTRIBUTED_NO_ACK(Scope.DISTRIBUTED_NO_ACK),
|
||||
GLOBAL(Scope.GLOBAL),
|
||||
LOCAL(Scope.LOCAL);
|
||||
|
||||
private final Scope gemfireScope;
|
||||
|
||||
/**
|
||||
* Constructs an instance of the ScopeType initialized with a matching GemFire Scope.
|
||||
*
|
||||
* @param gemfireScope the GemFire Scope paired with this enumerated value.
|
||||
* @see org.apache.geode.cache.Scope
|
||||
*/
|
||||
ScopeType(final Scope gemfireScope) {
|
||||
this.gemfireScope = gemfireScope;
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe operation to extract the GemFire Scope from the given ScopeType enum value, or null if the provided
|
||||
* scopeType is null.
|
||||
*
|
||||
* @param scopeType the ScopeType enumerated value from which to extract the GemFire Scope.
|
||||
* @return the paired GemFire Scope from the given ScopeType or null if scopeType is null.
|
||||
* @see org.apache.geode.cache.Scope
|
||||
* @see #getScope()
|
||||
*/
|
||||
public static Scope getScope(final ScopeType scopeType) {
|
||||
return (scopeType != null ? scopeType.getScope() : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a ScopeType enumerated value for the given a GemFire Scope.
|
||||
*
|
||||
* @param scope the GemFire Scope used to lookup and match the appropriate ScopeType.
|
||||
* @return a ScopeType for the given GemFire Scope or null if no match was found.
|
||||
* @see org.apache.geode.cache.Scope
|
||||
* @see #getScope()
|
||||
* @see #values()
|
||||
*/
|
||||
public static ScopeType valueOf(final Scope scope) {
|
||||
for (ScopeType scopeType : values()) {
|
||||
if (scopeType.getScope().equals(scope)) {
|
||||
return scopeType;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a ScopeType enumerated value given the case-insensitive name of the GemFire Scope.
|
||||
*
|
||||
* @param name a String name describing the ScopeType enum value.
|
||||
* @return a ScopeType for the given case-insensitive, named GemFire Scope.
|
||||
* @see java.lang.String#equalsIgnoreCase(String)
|
||||
* @see #values()
|
||||
* @see #name()
|
||||
* @see #transform(String)
|
||||
*/
|
||||
public static ScopeType valueOfIgnoreCase(String name) {
|
||||
name = transform(name);
|
||||
|
||||
for (ScopeType scopeType : values()) {
|
||||
if (scopeType.name().equalsIgnoreCase(name)) {
|
||||
return scopeType;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe operation that transforms a String name having hyphens and whitespace into a String with underscores
|
||||
* and no whitespace.
|
||||
*
|
||||
* @param name the String to transform.
|
||||
* @return a String value with underscores for hyphens and all leading/trailing whitespace trimmed, or null
|
||||
* if the given String name is null.
|
||||
*/
|
||||
private static String transform(final String name) {
|
||||
return (StringUtils.hasText(name) ? name.trim().replaceAll("-", "_") : name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the matching GemFire Scope for this enumerated value.
|
||||
*
|
||||
* @return a GemFire Scope for this enumerated value.
|
||||
* @see org.apache.geode.cache.Scope
|
||||
*/
|
||||
public Scope getScope() {
|
||||
return gemfireScope;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import org.apache.geode.cache.InterestPolicy;
|
||||
import org.apache.geode.cache.SubscriptionAttributes;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
/**
|
||||
* The SubscriptionAttributesFactoryBean class is a Spring FactoryBean used for defining and constructing
|
||||
* a GemFire SubscriptionAttributes object, which determines the Subscription policy used by Regions to
|
||||
* declared their data interests.
|
||||
*
|
||||
* @author Lyndon Adams
|
||||
* @author John Blum
|
||||
* @see org.springframework.beans.factory.FactoryBean
|
||||
* @see org.springframework.beans.factory.InitializingBean
|
||||
* @see org.apache.geode.cache.InterestPolicy
|
||||
* @see org.apache.geode.cache.SubscriptionAttributes
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public class SubscriptionAttributesFactoryBean implements FactoryBean<SubscriptionAttributes>, InitializingBean {
|
||||
|
||||
private InterestPolicy interestPolicy;
|
||||
|
||||
private SubscriptionAttributes subscriptionAttributes;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
this.subscriptionAttributes = new SubscriptionAttributes(getInterestPolicy());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObject()
|
||||
*/
|
||||
@Override
|
||||
public SubscriptionAttributes getObject() throws Exception {
|
||||
return this.subscriptionAttributes;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
|
||||
*/
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
|
||||
return this.subscriptionAttributes != null
|
||||
? this.subscriptionAttributes.getClass()
|
||||
: SubscriptionAttributes.class;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.FactoryBean#isSingleton()
|
||||
*/
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets GemFire's InterestPolicy specified on the SubscriptionAttributes in order to define/declare
|
||||
* the data interests and distribution of changes.
|
||||
*
|
||||
* @param interestPolicy the GemFire InterestsPolicy to set for Subscription.
|
||||
* @see org.apache.geode.cache.InterestPolicy
|
||||
* @see org.apache.geode.cache.SubscriptionAttributes#SubscriptionAttributes(org.apache.geode.cache.InterestPolicy)
|
||||
*/
|
||||
public void setInterestPolicy(InterestPolicy interestPolicy) {
|
||||
this.interestPolicy = interestPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets GemFire's InterestPolicy specified on the SubscriptionAttributes which defines data interests
|
||||
* and distribution of changes.
|
||||
*
|
||||
* @return the GemFire InterestsPolicy set for Subscription.
|
||||
* @see org.apache.geode.cache.InterestPolicy
|
||||
* @see org.apache.geode.cache.SubscriptionAttributes#getInterestPolicy()
|
||||
*/
|
||||
public InterestPolicy getInterestPolicy() {
|
||||
return this.interestPolicy != null ? this.interestPolicy : InterestPolicy.DEFAULT;
|
||||
}
|
||||
}
|
||||
@@ -1,200 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.cache;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.apache.geode.cache.CacheLoader;
|
||||
import org.apache.geode.cache.CacheLoaderException;
|
||||
import org.apache.geode.cache.LoaderHelper;
|
||||
import org.apache.geode.cache.Region;
|
||||
import org.apache.geode.cache.TimeoutException;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* The {@link CallableCacheLoaderAdapter} class is a {@link Callable} and GemFire {@link CacheLoader} implementation
|
||||
* that adapts the {@link Callable} interface into an instance of the {@link CacheLoader} interface. This class is
|
||||
* useful in situations where GemFire developers have several {@link CacheLoader} implementations that they wish to
|
||||
* use with Spring's Cache Abstraction.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see java.util.concurrent.Callable
|
||||
* @see org.apache.geode.cache.CacheLoader
|
||||
* @see org.apache.geode.cache.LoaderHelper
|
||||
* @see org.apache.geode.cache.Region
|
||||
* @since 1.9.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class CallableCacheLoaderAdapter<K, V> implements Callable<V>, CacheLoader<K, V> {
|
||||
|
||||
private final K key;
|
||||
|
||||
private final CacheLoader<K, V> cacheLoader;
|
||||
|
||||
private final Object argument;
|
||||
|
||||
private final Region<K, V> region;
|
||||
|
||||
/**
|
||||
* Constructs an instance of the CallableCacheLoaderAdapter that delegates to the given {@link CacheLoader}.
|
||||
*
|
||||
* @param delegate the {@link CacheLoader} delegated to by this adapter.
|
||||
* @see #CallableCacheLoaderAdapter(CacheLoader, Object, Region, Object)
|
||||
* @see org.apache.geode.cache.CacheLoader
|
||||
*/
|
||||
public CallableCacheLoaderAdapter(CacheLoader<K, V> delegate) {
|
||||
this(delegate, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance of the CallableCacheLoaderAdapter that delegates to the given {@link CacheLoader}
|
||||
* and is initialized with the given key for which the value will be loaded along with the {@link Region}
|
||||
* in which the entry (key/value) belongs.
|
||||
*
|
||||
* @param delegate the {@link CacheLoader} delegated to by this adapter.
|
||||
* @param key the key for which the value will be loaded.
|
||||
* @param region the {@link Region} in which the entry (key/value) belongs.
|
||||
* @see #CallableCacheLoaderAdapter(CacheLoader, Object, Region, Object)
|
||||
* @see org.apache.geode.cache.CacheLoader
|
||||
* @see org.apache.geode.cache.Region
|
||||
*/
|
||||
public CallableCacheLoaderAdapter(CacheLoader<K, V> delegate, K key, Region<K, V> region) {
|
||||
this(delegate, key, region, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance of the CallableCacheLoaderAdapter that delegates to the given {@link CacheLoader}
|
||||
* and is initialized with the given key for which the value will be loaded along with the {@link Region}
|
||||
* in which the entry (key/value) belongs. Additionally, an argument may be specified for use with the
|
||||
* {@link CacheLoader} delegate.
|
||||
*
|
||||
* @param delegate the {@link CacheLoader} delegated to by this adapter.
|
||||
* @param key the key for which the value will be loaded.
|
||||
* @param region the {@link Region} in which the entry (key/value) belongs.
|
||||
* @param argument the Object argument used with the {@link CacheLoader} delegate.
|
||||
* @see #CallableCacheLoaderAdapter(CacheLoader, Object, Region, Object)
|
||||
* @see org.apache.geode.cache.CacheLoader
|
||||
* @see org.apache.geode.cache.Region
|
||||
*/
|
||||
public CallableCacheLoaderAdapter(CacheLoader<K, V> delegate, K key, Region<K, V> region, Object argument) {
|
||||
Assert.notNull(delegate, "CacheLoader must not be null");
|
||||
this.cacheLoader = delegate;
|
||||
this.argument = argument;
|
||||
this.key = key;
|
||||
this.region = region;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument used by this {@link CacheLoader} to load the value for the specified key.
|
||||
*
|
||||
* @return an Object argument used by this {@link CacheLoader} when loading the value for the specified key.
|
||||
*/
|
||||
protected Object getArgument() {
|
||||
return argument;
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link CacheLoader} delegate used to actually load the cache value for the specified key.
|
||||
*
|
||||
* @return a reference to the actual {@link CacheLoader} used when loading the cache value for the specified key.
|
||||
* @see org.apache.geode.cache.CacheLoader
|
||||
*/
|
||||
protected CacheLoader<K, V> getCacheLoader() {
|
||||
return cacheLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* The specified key for which a value will be loaded by this {@link CacheLoader}.
|
||||
*
|
||||
* @return the specified key for which the value will be loaded.
|
||||
*/
|
||||
protected K getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Region to which the entry (key/value) belongs.
|
||||
*
|
||||
* @return the Region to which the entry belongs.
|
||||
* @see org.apache.geode.cache.Region
|
||||
*/
|
||||
protected Region<K, V> getRegion() {
|
||||
return region;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked to load a cache value for the specified key. Delegates to {@link #load(LoaderHelper)}.
|
||||
*
|
||||
* @return the loaded cache value for the specified key.
|
||||
* @throws java.lang.IllegalStateException if the {@link Region} or key references are null.
|
||||
* @throws java.lang.Exception if the load operation fails.
|
||||
* @see #load(LoaderHelper)
|
||||
*/
|
||||
public final V call() throws Exception {
|
||||
Assert.state(getKey() != null, "The key for which the value is loaded for cannot be null");
|
||||
Assert.state(getRegion() != null, "The Region to load cannot be null");
|
||||
|
||||
return load(new LoaderHelper<K, V>() {
|
||||
public V netSearch(final boolean doNetLoad) throws CacheLoaderException, TimeoutException {
|
||||
throw new UnsupportedOperationException("not implemented");
|
||||
}
|
||||
|
||||
public K getKey() {
|
||||
return CallableCacheLoaderAdapter.this.getKey();
|
||||
}
|
||||
|
||||
public Region<K, V> getRegion() {
|
||||
return CallableCacheLoaderAdapter.this.getRegion();
|
||||
}
|
||||
|
||||
public Object getArgument() {
|
||||
return CallableCacheLoaderAdapter.this.getArgument();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes any resources used by this {@link CacheLoader}. Delegates to the underlying {@link CacheLoader}.
|
||||
*
|
||||
* @see #getCacheLoader()
|
||||
*/
|
||||
public void close() {
|
||||
getCacheLoader().close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a value for the specified cache (i.e. {@link Region}) and key with the help of the {@link LoaderHelper}.
|
||||
* Delegates to the underlying {@link CacheLoader}.
|
||||
*
|
||||
* @param loaderHelper a {@link LoaderHelper} object passed in from cache service providing access to the key,
|
||||
* {@link Region}, argument, and <code>netSearch</code>.
|
||||
* @return the value supplied for the specified key, or null if no value can be supplied. A local loader will
|
||||
* always be invoked if one exists. Otherwise one remote loader is invoked. Returning <code>null</code> causes
|
||||
* {@link Region#get(Object, Object)} to return <code>null</code>.
|
||||
* @throws CacheLoaderException if an error occurs during the load operation. This exception, or any other
|
||||
* Exception thrown by this method will be propagated back to the application from the
|
||||
* {@link Region#get(Object)} method.
|
||||
* @see org.apache.geode.cache.CacheLoader#load(LoaderHelper)
|
||||
* @see org.apache.geode.cache.LoaderHelper
|
||||
* @see #getCacheLoader()
|
||||
*/
|
||||
public V load(LoaderHelper<K, V> loaderHelper) throws CacheLoaderException {
|
||||
return getCacheLoader().load(loaderHelper);
|
||||
}
|
||||
}
|
||||
@@ -1,214 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.cache;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.apache.geode.cache.GemFireCache;
|
||||
import org.apache.geode.cache.Region;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.support.SimpleValueWrapper;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Spring Framework {@link Cache} implementation backed by a GemFire {@link Region}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author John Blum
|
||||
* @author Oliver Gierke
|
||||
* @see org.springframework.cache.Cache
|
||||
* @see org.apache.geode.cache.Region
|
||||
*/
|
||||
public class GemfireCache implements Cache {
|
||||
|
||||
private final Region region;
|
||||
|
||||
/**
|
||||
* Wraps a GemFire {@link Region} in an instance of {@link GemfireCache} to adapt the GemFire {@link Region}
|
||||
* to function as a Spring {@link Cache} in Spring's caching infrastructure.
|
||||
*
|
||||
* @param region GemFire {@link Region} to wrap.
|
||||
* @return an instance of {@link GemfireCache} backed by the provided GemFire {@link Region}.
|
||||
* @see org.apache.geode.cache.Region
|
||||
* @see org.springframework.cache.Cache
|
||||
* @see #GemfireCache(Region)
|
||||
*/
|
||||
public static GemfireCache wrap(Region<?, ?> region) {
|
||||
return new GemfireCache(region);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance of {@link GemFireCache} initialized with the given GemFire {@link Region}.
|
||||
* The {@link Region} will function as the backing store and implementation for
|
||||
* the Spring {@link Cache} interface.
|
||||
*
|
||||
* @param region GemFire {@link Region} backing the Spring {@link Cache}.
|
||||
* @throws IllegalArgumentException if {@link Region} is null.
|
||||
*/
|
||||
public GemfireCache(Region<?, ?> region) {
|
||||
Assert.notNull(region, "GemFire Region must not be null");
|
||||
this.region = region;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the GemFire {@link Region} used as the implementation for this Spring {@link Cache}.
|
||||
*
|
||||
* @return the GemFire {@link Region} used as the implementation for this Spring {@link Cache}.
|
||||
* @see org.apache.geode.cache.Region
|
||||
*/
|
||||
public Region getNativeCache() {
|
||||
return this.region;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this Spring {@link Cache}.
|
||||
*
|
||||
* @return the name of this Spring {@link Cache}.
|
||||
* @see org.apache.geode.cache.Region#getName()
|
||||
*/
|
||||
public String getName() {
|
||||
return getNativeCache().getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the entire contents of this Spring {@link Cache}.
|
||||
*
|
||||
* @see org.apache.geode.cache.Region#clear()
|
||||
*/
|
||||
public void clear() {
|
||||
getNativeCache().clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Evicts (destroys) the entry (key/value) mapped to the given key from this Spring {@link Cache}.
|
||||
*
|
||||
* @param key key used to identify the cache entry to evict.
|
||||
* @see org.apache.geode.cache.Region#destroy(Object)
|
||||
*/
|
||||
public void evict(Object key) {
|
||||
getNativeCache().remove(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cache value for the given key wrapped in an instance of
|
||||
* {@link org.springframework.cache.Cache.ValueWrapper}.
|
||||
*
|
||||
* @param key key identifying the the value to retrieve from the cache.
|
||||
* @return the value cached with the given key.
|
||||
* @see org.springframework.cache.Cache.ValueWrapper
|
||||
* @see org.apache.geode.cache.Region#get(Object)
|
||||
*/
|
||||
public ValueWrapper get(Object key) {
|
||||
Object value = getNativeCache().get(key);
|
||||
|
||||
return (value != null ? new SimpleValueWrapper(value) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cache value for the given key cast to the specified {@link Class} type.
|
||||
*
|
||||
* @param <T> desired {@link Class} type of the cache value.
|
||||
* @param key key identifying the the value to retrieve from the cache.
|
||||
* @param type desired {@link Class} type of the value.
|
||||
* @return the cache value for the given key cast to the specified {@link Class} type.
|
||||
* @throws IllegalStateException if the value is not null and not an instance of the desired type.
|
||||
* @see org.apache.geode.cache.Region#get(Object)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T get(Object key, Class<T> type) {
|
||||
Object value = getNativeCache().get(key);
|
||||
|
||||
if (value != null && type != null && !type.isInstance(value)) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"Cached value [%1$s] is not an instance of type [%2$s]",
|
||||
value, type.getName()));
|
||||
}
|
||||
|
||||
return (T) value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cache value for given key. If the value is {@literal null}, then the provided
|
||||
* {@link Callable} {@code valueLoader} will be called to obtain a value and add the entry
|
||||
* to this cache.
|
||||
*
|
||||
* @param <T> {@link Class} type of the value.
|
||||
* @param key key identifying the the value to retrieve from the cache.
|
||||
* @param valueLoader {@link Callable} object used to load a value if the entry identified by the key
|
||||
* does not already have value.
|
||||
* @return the cache value of the given key or a value obtained by calling the {@link Callable} object
|
||||
* if the value for key is {@literal null}.
|
||||
* @throws org.springframework.cache.Cache.ValueRetrievalException if an error occurs while trying to
|
||||
* load a value for given key using the {@link Callable}.
|
||||
* @see #get(Object, Class)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T get(Object key, Callable<T> valueLoader) {
|
||||
T value = (T) get(key, Object.class);
|
||||
|
||||
if (value == null) {
|
||||
synchronized (getNativeCache()) {
|
||||
value = (T) get(key, Object.class);
|
||||
|
||||
if (value == null) {
|
||||
try {
|
||||
value = valueLoader.call();
|
||||
put(key, value);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new ValueRetrievalException(key, valueLoader, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the given value in the cache referenced by the given key. This operation will only store the value
|
||||
* if the value is not {@literal null}.
|
||||
*
|
||||
* @param key key used to reference the value in the cache.
|
||||
* @param value value to store in the cache referenced by the key.
|
||||
* @see org.apache.geode.cache.Region#put(Object, Object)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void put(Object key, Object value) {
|
||||
if (value != null) {
|
||||
getNativeCache().put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of {@link Cache#putIfAbsent(Object, Object)} satisfying the extension of
|
||||
* the {@link Cache} interface in Spring 4.1. Don't add the {@link Override} annotation
|
||||
* otherwise this will break the compilation on 4.0.
|
||||
*
|
||||
* @return the existing value if the given key is already mapped to a value.
|
||||
* @see org.springframework.cache.Cache#putIfAbsent(java.lang.Object, java.lang.Object)
|
||||
* @see org.apache.geode.cache.Region#putIfAbsent(Object, Object)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public ValueWrapper putIfAbsent(Object key, Object value) {
|
||||
Object existingValue = getNativeCache().putIfAbsent(key, value);
|
||||
|
||||
return (existingValue != null ? new SimpleValueWrapper(existingValue) : null);
|
||||
}
|
||||
}
|
||||
@@ -1,252 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.cache;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.apache.geode.cache.GemFireCache;
|
||||
import org.apache.geode.cache.Region;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.support.AbstractCacheManager;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Core Spring Framework {@link CacheManager} implementation backed by a GemFire cache instance
|
||||
* (either a client or peer cache).
|
||||
*
|
||||
* Automatically discovers available caches (or GemFire {@link Region Regions}) when a cache for a given name
|
||||
* is missing and dynamic cache lookup/creation is enabled.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author David Turanski
|
||||
* @author John Blum
|
||||
* @see org.springframework.cache.Cache
|
||||
* @see org.springframework.cache.CacheManager
|
||||
* @see org.springframework.cache.support.AbstractCacheManager
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
* @see org.apache.geode.cache.Region
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class GemfireCacheManager extends AbstractCacheManager {
|
||||
|
||||
private final AtomicBoolean dynamic = new AtomicBoolean(true);
|
||||
|
||||
private org.apache.geode.cache.GemFireCache gemfireCache;
|
||||
|
||||
private Set<Region<?, ?>> regions;
|
||||
|
||||
private Set<String> cacheNames;
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@SuppressWarnings("all")
|
||||
<T extends GemFireCache> T assertGemFireCacheAvailable(T gemfireCache) {
|
||||
Assert.state(gemfireCache != null, "A GemFire cache instance is required");
|
||||
Assert.state(!gemfireCache.isClosed(), String.format("GemFire cache [%s] has been closed",
|
||||
gemfireCache.getName()));
|
||||
|
||||
return gemfireCache;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@SuppressWarnings("all")
|
||||
Region<?, ?> assertGemFireRegionAvailable(Region<?, ?> region, String cacheName) {
|
||||
Assert.state(region != null, String.format("No Region for cache name [%s] was found", cacheName));
|
||||
Assert.state(!region.isDestroyed(), String.format("Region [%s] has been destroyed", cacheName));
|
||||
|
||||
return region;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all configured GemFire {@link Region Regions} that will be used by this {@link CacheManager}.
|
||||
*
|
||||
* Any GemFire {@link Region Regions} configured with the {@link #regions} property will take precedence over
|
||||
* any configured {@link #cacheNames}. If no GemFire {@link Region Regions} were configured, then any
|
||||
* {@link #cacheNames} that were specified will be used to lookup existing GemFire {@link Region Regions}
|
||||
* to function as Spring {@link Cache Caches}in Spring's caching infrastructure.
|
||||
*
|
||||
* However, if neither {@link #regions} nor {@link #cacheNames} were specified, then all defined GemFire
|
||||
* {@link Region Regions} declared in the Spring application context, as determined by
|
||||
* {@link GemFireCache#rootRegions()}, will be used as Spring {@link Cache Caches}, and this {@link CacheManager}
|
||||
* will allow any dynamically created GemFire {@link Region Regions} at runtime to be found and used as a
|
||||
* Spring {@link Cache} as well.
|
||||
*
|
||||
* @return a {@link Collection} of GemFire {@link Region Regions} used by this {@link CacheManager}
|
||||
* to function as {@link Cache Caches} in Spring's caching infrastructure.
|
||||
* @throws IllegalStateException if a GemFire cache instance was not provided, the provided GemFire cache instance
|
||||
* has been closed, no GemFire {@link Region} could be found for a given cache name, or the GemFire {@link Region}
|
||||
* for the given cache name has been destroyed.
|
||||
* @see org.springframework.cache.Cache
|
||||
*/
|
||||
@Override
|
||||
protected Collection<Cache> loadCaches() {
|
||||
Set<Region<?, ?>> regions = resolveRegions(this.gemfireCache, this.regions, this.cacheNames);
|
||||
|
||||
Collection<Cache> caches = new HashSet<Cache>(regions.size());
|
||||
|
||||
for (Region<?, ?> region : regions) {
|
||||
caches.add(newGemfireCache(region));
|
||||
}
|
||||
|
||||
return caches;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
Set<Region<?, ?>> resolveRegions(GemFireCache gemfireCache, Set<Region<?, ?>> regions, Set<String> cacheNames) {
|
||||
if (isSet(regions)) {
|
||||
dynamic.set(false);
|
||||
return regions;
|
||||
}
|
||||
else if (isSet(cacheNames)) {
|
||||
dynamic.set(false);
|
||||
|
||||
regions = new HashSet<Region<?, ?>>(cacheNames.size());
|
||||
|
||||
for (String cacheName : cacheNames) {
|
||||
regions.add(regionFor(gemfireCache, cacheName));
|
||||
}
|
||||
|
||||
return regions;
|
||||
}
|
||||
else {
|
||||
return assertGemFireCacheAvailable(gemfireCache).rootRegions();
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
boolean isSet(Iterable<?> collection) {
|
||||
return (collection != null && collection.iterator().hasNext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance of {@link GemfireCache} initialized with the given GemFire {@link Region}.
|
||||
*
|
||||
* @param region GemFire {@link Region} to wrap (adapt).
|
||||
* @return an instance of {@link GemfireCache} initialized with the given GemFire {@link Region}.
|
||||
* @see GemfireCache
|
||||
* @see org.apache.geode.cache.Region
|
||||
*/
|
||||
protected GemfireCache newGemfireCache(Region<?, ?> region) {
|
||||
return GemfireCache.wrap(region);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
Region<?, ?> regionFor(GemFireCache gemfireCache, String cacheName) {
|
||||
return assertGemFireRegionAvailable(assertGemFireCacheAvailable(gemfireCache).getRegion(cacheName), cacheName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a missing Spring {@link Cache} for the given {@code name}.
|
||||
*
|
||||
* To return a missing Spring {@link Cache} for the given {@code name}, dynamic cache lookup/creation must be
|
||||
* enabled, which means that either the {@link #cacheNames} or {@link #regions} properties must not be set.
|
||||
* If either property was specified then dynamic Spring {@link Cache} lookup/creation will be disabled and this
|
||||
* overridden {@link AbstractCacheManager#getMissingCache(String)} method will return {@literal null}.
|
||||
*
|
||||
* @param name name of the missing Spring {@link Cache} to lookup (and potentially create).
|
||||
* @return a Spring {@link Cache} instance for the given {@code name} or {@literal null} if the {@link Cache}
|
||||
* cannot be found (or possibly created).
|
||||
* @see org.springframework.cache.support.AbstractCacheManager#getMissingCache(String)
|
||||
* @see org.springframework.cache.Cache
|
||||
*/
|
||||
@Override
|
||||
protected Cache getMissingCache(String name) {
|
||||
Cache cache = super.getMissingCache(name);
|
||||
|
||||
return (cache != null ? cache : (isDynamic() ? newGemfireCache(regionFor(this.gemfireCache, name)) : null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether this {@link CacheManager} allows the dynamic creation of a {@link Cache} at runtime.
|
||||
*
|
||||
* @return a boolean value indicating whether dynamic {@link Cache} creation is enabled.
|
||||
*/
|
||||
protected boolean isDynamic() {
|
||||
return dynamic.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the GemFire cache instance backing this {@link CacheManager}.
|
||||
*
|
||||
* When set, if neither {@link Region Regions} nor {@code cacheNames} were specified, then this {@link CacheManager}
|
||||
* is capable of creating Spring {@link Cache Caches} backed by existing GemFire {@link Region Regions} used by
|
||||
* the application at runtime. However, in order to dynamically create Spring {@link Cache Caches} a reference to
|
||||
* an open GemFire cache instance must be set.
|
||||
*
|
||||
* @param gemfireCache the GemFire cache instance used by this {@link CacheManager}
|
||||
* to manage Spring {@link Cache Caches}.
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
*/
|
||||
public void setCache(org.apache.geode.cache.GemFireCache gemfireCache) {
|
||||
this.gemfireCache = gemfireCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link GemFireCache} instance backing this {@link CacheManager}.
|
||||
*
|
||||
* @return the {@link GemFireCache} instance backing this {@link CacheManager}.
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
*/
|
||||
protected org.apache.geode.cache.GemFireCache getCache() {
|
||||
return this.gemfireCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the names of all Spring {@link Cache Caches} that will be used in the application.
|
||||
*
|
||||
* When set, this disables the dynamic capability of this {@link CacheManager} to create Spring {@link Cache Caches}
|
||||
* at runtime by dynamically looking up existing {@link Region Regions} from the GemFire cache instance.
|
||||
*
|
||||
* @param cacheNames {@link Set} of cache names that will be used in the application.
|
||||
* @see java.util.Set
|
||||
*/
|
||||
public void setCacheNames(Set<String> cacheNames) {
|
||||
this.cacheNames = cacheNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicitly sets the GemFire {@link Region Regions} to be used as Spring {@link Cache Caches}
|
||||
* in the application.
|
||||
*
|
||||
* When set, this disables the dynamic capability of this {@link CacheManager} to create Spring {@link Cache Caches}
|
||||
* at runtime by dynamically looking up existing {@link Region Regions} from the GemFire cache instance.
|
||||
*
|
||||
* @param regions {@link Set} of GemFire {@link Region Regions} used by this {@link CacheManager}
|
||||
* as Spring {@link Cache Caches}.
|
||||
* @see org.apache.geode.cache.Region
|
||||
*/
|
||||
public void setRegions(Set<Region<?, ?>> regions) {
|
||||
this.regions = regions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of GemFire {@link Region Regions} used explicitly as Spring {@link Cache Caches}
|
||||
* in Spring's caching infrastructure.
|
||||
*
|
||||
* @return the set of GemFire {@link Region Regions} functioning as Spring {@link Cache Caches}
|
||||
* in Spring's caching infrastructure
|
||||
* @see org.apache.geode.cache.Region
|
||||
*/
|
||||
protected Set<Region<?, ?>> getRegions() {
|
||||
return this.regions;
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.cache.config;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* The {@link EnableGemfireCaching} annotation enables Pivotal GemFire or Apache Geode as a caching provider
|
||||
* in Spring's Cache Abstraction.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see java.lang.annotation.Documented
|
||||
* @see java.lang.annotation.Inherited
|
||||
* @see java.lang.annotation.Retention
|
||||
* @see java.lang.annotation.Target
|
||||
* @see org.springframework.context.annotation.Import
|
||||
* @see <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#cache">Cache Abstraction</a>
|
||||
* @see <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#cache-store-configuration-gemfire">GemFire-based Cache</a>
|
||||
* @see <a href="https://docs.spring.io/spring-data-gemfire/docs/current/reference/html/#apis:spring-cache-abstraction">Support for Spring Cache Abstraction</a>
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
@Import(GemfireCachingConfiguration.class)
|
||||
@SuppressWarnings("unused")
|
||||
public @interface EnableGemfireCaching {
|
||||
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.cache.config;
|
||||
|
||||
import org.apache.geode.cache.GemFireCache;
|
||||
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.gemfire.cache.GemfireCacheManager;
|
||||
|
||||
/**
|
||||
* The {@link GemfireCachingConfiguration} class is a Spring {@link Configuration @Configuration} class
|
||||
* used to configure Pivotal GemFire or Apache Geode as the caching provider in Spring's Cache Abstraction.
|
||||
*
|
||||
* This {@link Configuration @Configuration} class is specifically responsible for declaring and registering
|
||||
* Spring Data GemFire/Geode's {@link GemfireCacheManager} implementation to properly enable either Pivotal GemFire
|
||||
* or Apache Geode as the caching provider used with Springs Cache Abstraction.
|
||||
*
|
||||
* Additionally, this Spring {@link Configuration @Configuration} class also enables the Spring Cache Abstraction
|
||||
* by declaring Spring's {@link EnableCaching} annotation for the user extending or importing this class using
|
||||
* the SDG provided {@link EnableGemfireCaching} annotation.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
* @see org.springframework.cache.annotation.EnableCaching
|
||||
* @see org.springframework.context.annotation.Bean
|
||||
* @see org.springframework.context.annotation.Configuration
|
||||
* @see org.springframework.data.gemfire.cache.GemfireCacheManager
|
||||
* @see org.springframework.data.gemfire.cache.config.EnableGemfireCaching
|
||||
* @see <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#cache">Cache Abstraction</a>
|
||||
* @see <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#cache-store-configuration-gemfire">GemFire-based Cache</a>
|
||||
* @see <a href="https://docs.spring.io/spring-data-gemfire/docs/current/reference/html/#apis:spring-cache-abstraction">Support for Spring Cache Abstraction</a>
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
@SuppressWarnings("unused")
|
||||
public class GemfireCachingConfiguration {
|
||||
|
||||
/**
|
||||
* SDG's {@link GemfireCacheManager} used to position Pivotal GemFire or Apache Geode as the caching provider
|
||||
* in Spring's Cache Abstraction.
|
||||
*
|
||||
* @return an instance of {@link GemfireCacheManager}.
|
||||
* @see org.springframework.data.gemfire.cache.GemfireCacheManager
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
*/
|
||||
@Bean
|
||||
public GemfireCacheManager cacheManager(GemFireCache gemfireCache) {
|
||||
|
||||
GemfireCacheManager gemfireCacheManager = new GemfireCacheManager();
|
||||
|
||||
gemfireCacheManager.setCache(gemfireCache);
|
||||
|
||||
return gemfireCacheManager;
|
||||
}
|
||||
}
|
||||
@@ -1,847 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.gemfire.client;
|
||||
|
||||
import static java.util.stream.StreamSupport.stream;
|
||||
import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
|
||||
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeCollection;
|
||||
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeIterable;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.apache.geode.cache.CacheClosedException;
|
||||
import org.apache.geode.cache.GemFireCache;
|
||||
import org.apache.geode.cache.client.ClientCache;
|
||||
import org.apache.geode.cache.client.ClientCacheFactory;
|
||||
import org.apache.geode.cache.client.Pool;
|
||||
import org.apache.geode.cache.client.PoolManager;
|
||||
import org.apache.geode.distributed.DistributedSystem;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.event.ApplicationContextEvent;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.data.gemfire.CacheFactoryBean;
|
||||
import org.springframework.data.gemfire.GemfireUtils;
|
||||
import org.springframework.data.gemfire.client.support.DefaultableDelegatingPoolAdapter;
|
||||
import org.springframework.data.gemfire.client.support.DelegatingPoolAdapter;
|
||||
import org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer;
|
||||
import org.springframework.data.gemfire.config.xml.GemfireConstants;
|
||||
import org.springframework.data.gemfire.support.ConnectionEndpoint;
|
||||
import org.springframework.data.gemfire.support.ConnectionEndpointList;
|
||||
import org.springframework.data.gemfire.util.SpringUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Spring {@link org.springframework.beans.factory.FactoryBean} used to create a Pivotal GemFire/Apache Geode
|
||||
* {@link ClientCache}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Lyndon Adams
|
||||
* @author John Blum
|
||||
* @see java.net.InetSocketAddress
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
* @see org.apache.geode.cache.client.ClientCache
|
||||
* @see org.apache.geode.cache.client.ClientCacheFactory
|
||||
* @see org.apache.geode.cache.client.Pool
|
||||
* @see org.apache.geode.cache.client.PoolManager
|
||||
* @see org.apache.geode.distributed.DistributedSystem
|
||||
* @see org.apache.geode.pdx.PdxSerializer
|
||||
* @see org.springframework.beans.factory.BeanFactory
|
||||
* @see org.springframework.context.ApplicationContext
|
||||
* @see org.springframework.context.ApplicationListener
|
||||
* @see org.springframework.context.event.ContextRefreshedEvent
|
||||
* @see org.springframework.data.gemfire.CacheFactoryBean
|
||||
* @see org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer
|
||||
* @see org.springframework.data.gemfire.support.ConnectionEndpoint
|
||||
* @see org.springframework.data.gemfire.support.ConnectionEndpointList
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class ClientCacheFactoryBean extends CacheFactoryBean implements ApplicationListener<ContextRefreshedEvent> {
|
||||
|
||||
private Boolean keepAlive = false;
|
||||
private Boolean multiUserAuthentication;
|
||||
private Boolean prSingleHopEnabled;
|
||||
private Boolean readyForEvents;
|
||||
private Boolean subscriptionEnabled;
|
||||
private Boolean threadLocalConnections;
|
||||
|
||||
private ConnectionEndpointList locators = new ConnectionEndpointList();
|
||||
private ConnectionEndpointList servers = new ConnectionEndpointList();
|
||||
|
||||
private Integer durableClientTimeout;
|
||||
private Integer freeConnectionTimeout;
|
||||
private Integer loadConditioningInterval;
|
||||
private Integer maxConnections;
|
||||
private Integer minConnections;
|
||||
private Integer readTimeout;
|
||||
private Integer retryAttempts;
|
||||
private Integer socketBufferSize;
|
||||
private Integer socketConnectTimeout;
|
||||
private Integer statisticsInterval;
|
||||
private Integer subscriptionAckInterval;
|
||||
private Integer subscriptionMessageTrackingTimeout;
|
||||
private Integer subscriptionRedundancy;
|
||||
|
||||
private List<ClientCacheConfigurer> clientCacheConfigurers = Collections.emptyList();
|
||||
|
||||
private Long idleTimeout;
|
||||
private Long pingInterval;
|
||||
|
||||
private Pool pool;
|
||||
|
||||
private String durableClientId;
|
||||
private String poolName;
|
||||
private String serverGroup;
|
||||
|
||||
private final ClientCacheConfigurer compositeClientCacheConfigurer = (beanName, bean) ->
|
||||
nullSafeCollection(clientCacheConfigurers).forEach(clientCacheConfigurer ->
|
||||
clientCacheConfigurer.configure(beanName, bean));
|
||||
|
||||
/**
|
||||
* Applies the composite {@link ClientCacheConfigurer ClientCacheConfigurers}
|
||||
* to this {@link ClientCacheFactoryBean}.
|
||||
*
|
||||
* @see #getCompositeClientCacheConfigurer()
|
||||
* @see #applyClientCacheConfigurers(ClientCacheConfigurer...)
|
||||
*/
|
||||
@Override
|
||||
protected void applyCacheConfigurers() {
|
||||
applyClientCacheConfigurers(getCompositeClientCacheConfigurer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe operation to apply the given array of {@link ClientCacheConfigurer ClientCacheConfigurers}
|
||||
* to this {@link ClientCacheFactoryBean}.
|
||||
*
|
||||
* @param clientCacheConfigurers array of {@link ClientCacheConfigurer ClientCacheConfigurers} applied to
|
||||
* this {@link ClientCacheFactoryBean}.
|
||||
* @see org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer
|
||||
* @see #applyClientCacheConfigurers(Iterable)
|
||||
*/
|
||||
protected void applyClientCacheConfigurers(ClientCacheConfigurer... clientCacheConfigurers) {
|
||||
applyClientCacheConfigurers(Arrays.asList(nullSafeArray(clientCacheConfigurers, ClientCacheConfigurer.class)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe operation to apply the given {@link Iterable} of {@link ClientCacheConfigurer ClientCacheConfigurers}
|
||||
* to this {@link ClientCacheFactoryBean}.
|
||||
*
|
||||
* @param clientCacheConfigurers {@link Iterable} of {@link ClientCacheConfigurer ClientCacheConfigurers}
|
||||
* applied to this {@link ClientCacheFactoryBean}.
|
||||
* @see org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer
|
||||
* @see java.lang.Iterable
|
||||
*/
|
||||
protected void applyClientCacheConfigurers(Iterable<ClientCacheConfigurer> clientCacheConfigurers) {
|
||||
stream(nullSafeIterable(clientCacheConfigurers).spliterator(), false)
|
||||
.forEach(clientCacheConfigurer -> clientCacheConfigurer.configure(getBeanName(), this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches an existing {@link ClientCache} instance from the {@link ClientCacheFactory}.
|
||||
*
|
||||
* @param <T> parameterized {@link Class} type extension of {@link GemFireCache}.
|
||||
* @return an existing {@link ClientCache} instance if available.
|
||||
* @throws org.apache.geode.cache.CacheClosedException if an existing {@link ClientCache} instance does not exist.
|
||||
* @see org.apache.geode.cache.client.ClientCacheFactory#getAnyInstance()
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
* @see #getCache()
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T extends GemFireCache> T fetchCache() {
|
||||
return (T) Optional.ofNullable(getCache()).orElseGet(ClientCacheFactory::getAnyInstance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the Pivotal GemFire/Apache Geode {@link Properties} used to configure the {@link ClientCache}.
|
||||
*
|
||||
* @return the resolved Pivotal GemFire/Apache Geode {@link Properties} used to configure the {@link ClientCache}.
|
||||
* @see org.apache.geode.distributed.DistributedSystem#getProperties()
|
||||
* @see #getDistributedSystem()
|
||||
*/
|
||||
@Override
|
||||
protected Properties resolveProperties() {
|
||||
|
||||
Properties gemfireProperties = super.resolveProperties();
|
||||
|
||||
DistributedSystem distributedSystem = getDistributedSystem();
|
||||
|
||||
if (GemfireUtils.isConnected(distributedSystem)) {
|
||||
Properties distributedSystemProperties = (Properties) distributedSystem.getProperties().clone();
|
||||
distributedSystemProperties.putAll(gemfireProperties);
|
||||
gemfireProperties = distributedSystemProperties;
|
||||
}
|
||||
|
||||
GemfireUtils.configureDurableClient(gemfireProperties, getDurableClientId(), getDurableClientTimeout());
|
||||
|
||||
return gemfireProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link DistributedSystem} formed from cache initialization.
|
||||
*
|
||||
* @param <T> {@link Class} type of the {@link DistributedSystem}.
|
||||
* @return an instance of the {@link DistributedSystem}.
|
||||
* @see org.apache.geode.distributed.DistributedSystem
|
||||
*/
|
||||
<T extends DistributedSystem> T getDistributedSystem() {
|
||||
return GemfireUtils.getDistributedSystem();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance of {@link ClientCacheFactory} initialized with the given Pivotal GemFire/Apache Geode
|
||||
* {@link Properties} used to construct, configure and initialize an instance of a {@link ClientCache}.
|
||||
*
|
||||
* @param gemfireProperties {@link Properties} used by the {@link ClientCacheFactory}
|
||||
* to configure the {@link ClientCache}.
|
||||
* @return a new instance of {@link ClientCacheFactory} initialized with
|
||||
* the given Pivotal GemFire/Apache Geode {@link Properties}.
|
||||
* @see org.apache.geode.cache.client.ClientCacheFactory
|
||||
* @see java.util.Properties
|
||||
*/
|
||||
@Override
|
||||
protected Object createFactory(Properties gemfireProperties) {
|
||||
return new ClientCacheFactory(gemfireProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the {@link ClientCacheFactory} used to create the {@link ClientCache}.
|
||||
*
|
||||
* Sets PDX options specified by the user.
|
||||
*
|
||||
* Sets Pool options specified by the user.
|
||||
*
|
||||
* @param factory {@link ClientCacheFactory} used to create the {@link ClientCache}.
|
||||
* @return the configured {@link ClientCacheFactory}.
|
||||
* @see #configurePdx(ClientCacheFactory)
|
||||
*/
|
||||
@Override
|
||||
protected Object configureFactory(Object factory) {
|
||||
return configurePool(configurePdx((ClientCacheFactory) factory));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure PDX for the {@link ClientCacheFactory}.
|
||||
*
|
||||
* @param clientCacheFactory {@link ClientCacheFactory} used to configure PDX.
|
||||
* @return the given {@link ClientCacheFactory}
|
||||
* @see org.apache.geode.cache.client.ClientCacheFactory
|
||||
*/
|
||||
ClientCacheFactory configurePdx(ClientCacheFactory clientCacheFactory) {
|
||||
|
||||
Optional.ofNullable(getPdxSerializer()).ifPresent(clientCacheFactory::setPdxSerializer);
|
||||
|
||||
Optional.ofNullable(getPdxDiskStoreName()).filter(StringUtils::hasText)
|
||||
.ifPresent(clientCacheFactory::setPdxDiskStore);
|
||||
|
||||
Optional.ofNullable(getPdxIgnoreUnreadFields()).ifPresent(clientCacheFactory::setPdxIgnoreUnreadFields);
|
||||
|
||||
Optional.ofNullable(getPdxPersistent()).ifPresent(clientCacheFactory::setPdxPersistent);
|
||||
|
||||
Optional.ofNullable(getPdxReadSerialized()).ifPresent(clientCacheFactory::setPdxReadSerialized);
|
||||
|
||||
return clientCacheFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the {@literal DEFAULT} {@link Pool} configuration settings with the {@link ClientCacheFactory}
|
||||
* using a given {@link Pool} instance or a named {@link Pool}.
|
||||
*
|
||||
* @param clientCacheFactory {@link ClientCacheFactory} use to configure the {@literal DEFAULT} {@link Pool}.
|
||||
* @see org.apache.geode.cache.client.ClientCacheFactory
|
||||
* @see org.apache.geode.cache.client.Pool
|
||||
*/
|
||||
ClientCacheFactory configurePool(ClientCacheFactory clientCacheFactory) {
|
||||
|
||||
DefaultableDelegatingPoolAdapter pool =
|
||||
DefaultableDelegatingPoolAdapter.from(DelegatingPoolAdapter.from(resolvePool())).preferDefault();
|
||||
|
||||
clientCacheFactory.setPoolFreeConnectionTimeout(pool.getFreeConnectionTimeout(getFreeConnectionTimeout()));
|
||||
clientCacheFactory.setPoolIdleTimeout(pool.getIdleTimeout(getIdleTimeout()));
|
||||
clientCacheFactory.setPoolLoadConditioningInterval(pool.getLoadConditioningInterval(getLoadConditioningInterval()));
|
||||
clientCacheFactory.setPoolMaxConnections(pool.getMaxConnections(getMaxConnections()));
|
||||
clientCacheFactory.setPoolMinConnections(pool.getMinConnections(getMinConnections()));
|
||||
clientCacheFactory.setPoolMultiuserAuthentication(pool.getMultiuserAuthentication(getMultiUserAuthentication()));
|
||||
clientCacheFactory.setPoolPingInterval(pool.getPingInterval(getPingInterval()));
|
||||
clientCacheFactory.setPoolPRSingleHopEnabled(pool.getPRSingleHopEnabled(getPrSingleHopEnabled()));
|
||||
clientCacheFactory.setPoolReadTimeout(pool.getReadTimeout(getReadTimeout()));
|
||||
clientCacheFactory.setPoolRetryAttempts(pool.getRetryAttempts(getRetryAttempts()));
|
||||
clientCacheFactory.setPoolServerGroup(pool.getServerGroup(getServerGroup()));
|
||||
clientCacheFactory.setPoolSocketBufferSize(pool.getSocketBufferSize(getSocketBufferSize()));
|
||||
clientCacheFactory.setPoolSocketConnectTimeout(pool.getSocketConnectTimeout(getSocketConnectTimeout()));
|
||||
clientCacheFactory.setPoolStatisticInterval(pool.getStatisticInterval(getStatisticsInterval()));
|
||||
clientCacheFactory.setPoolSubscriptionAckInterval(pool.getSubscriptionAckInterval(getSubscriptionAckInterval()));
|
||||
clientCacheFactory.setPoolSubscriptionEnabled(pool.getSubscriptionEnabled(getSubscriptionEnabled()));
|
||||
clientCacheFactory.setPoolSubscriptionMessageTrackingTimeout(pool.getSubscriptionMessageTrackingTimeout(getSubscriptionMessageTrackingTimeout()));
|
||||
clientCacheFactory.setPoolSubscriptionRedundancy(pool.getSubscriptionRedundancy(getSubscriptionRedundancy()));
|
||||
clientCacheFactory.setPoolThreadLocalConnections(pool.getThreadLocalConnections(getThreadLocalConnections()));
|
||||
|
||||
AtomicBoolean noServers = new AtomicBoolean(getServers().isEmpty());
|
||||
|
||||
boolean noLocators = getLocators().isEmpty();
|
||||
boolean hasLocators = !noLocators;
|
||||
boolean hasServers = !noServers.get();
|
||||
|
||||
if (hasServers || noLocators) {
|
||||
|
||||
Iterable<InetSocketAddress> servers = pool.getServers(getServers().toInetSocketAddresses());
|
||||
|
||||
stream(servers.spliterator(), false).forEach(server -> {
|
||||
clientCacheFactory.addPoolServer(server.getHostName(), server.getPort());
|
||||
noServers.set(false);
|
||||
});
|
||||
}
|
||||
|
||||
if (hasLocators || noServers.get()) {
|
||||
|
||||
Iterable<InetSocketAddress> locators = pool.getLocators(getLocators().toInetSocketAddresses());
|
||||
|
||||
stream(locators.spliterator(), false).forEach(locator ->
|
||||
clientCacheFactory.addPoolLocator(locator.getHostName(), locator.getPort()));
|
||||
}
|
||||
|
||||
return clientCacheFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the {@link Pool} used to configure the {@link ClientCache}, {@literal DEFAULT} {@link Pool}.
|
||||
*
|
||||
* @return the resolved {@link Pool} used to configure the {@link ClientCache}, {@literal DEFAULT} {@link Pool}.
|
||||
* @see org.apache.geode.cache.client.PoolManager#find(String)
|
||||
* @see org.apache.geode.cache.client.Pool
|
||||
* @see #getPoolName()
|
||||
* @see #getPool()
|
||||
* @see #findPool(String)
|
||||
* @see #isPoolNameResolvable(String)
|
||||
*/
|
||||
Pool resolvePool() {
|
||||
|
||||
Pool pool = getPool();
|
||||
|
||||
if (pool == null) {
|
||||
|
||||
String poolName = resolvePoolName();
|
||||
|
||||
pool = findPool(poolName);
|
||||
|
||||
if (pool == null && isPoolNameResolvable(poolName)) {
|
||||
|
||||
String dereferencedPoolName = SpringUtils.dereferenceBean(poolName);
|
||||
|
||||
PoolFactoryBean poolFactoryBean =
|
||||
getBeanFactory().getBean(dereferencedPoolName, PoolFactoryBean.class);
|
||||
|
||||
return poolFactoryBean.getPool();
|
||||
}
|
||||
}
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
String resolvePoolName() {
|
||||
|
||||
return Optional.ofNullable(getPoolName())
|
||||
.filter(StringUtils::hasText)
|
||||
.orElse(GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME);
|
||||
}
|
||||
|
||||
Pool findPool(String name) {
|
||||
return PoolManager.find(name);
|
||||
}
|
||||
|
||||
private boolean isPoolNameResolvable(String poolName) {
|
||||
|
||||
return Optional.ofNullable(poolName)
|
||||
.filter(getBeanFactory()::containsBean)
|
||||
.isPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ClientCache} instance using the provided factory.
|
||||
*
|
||||
* @param <T> parameterized {@link Class} type extension of {@link GemFireCache}.
|
||||
* @param factory instance of {@link ClientCacheFactory}.
|
||||
* @return a new instance of {@link ClientCache} created by the provided factory.
|
||||
* @see org.apache.geode.cache.client.ClientCacheFactory#create()
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T extends GemFireCache> T createCache(Object factory) {
|
||||
return (T) ((ClientCacheFactory) factory).create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Inform the Pivotal GemFire/Apache Geode cluster that this cache client is ready to receive events
|
||||
* iff the client is non-durable.
|
||||
*
|
||||
* @param event {@link ApplicationContextEvent} fired when the {@link ApplicationContext} is refreshed.
|
||||
* @see org.apache.geode.cache.client.ClientCache#readyForEvents()
|
||||
* @see #isReadyForEvents()
|
||||
* @see #fetchCache()
|
||||
*/
|
||||
@Override
|
||||
public void onApplicationEvent(ContextRefreshedEvent event) {
|
||||
|
||||
if (isReadyForEvents()) {
|
||||
try {
|
||||
this.<ClientCache>fetchCache().readyForEvents();
|
||||
}
|
||||
catch (IllegalStateException | CacheClosedException ignore) {
|
||||
// Thrown when ClientCache.readyForEvents() is called on a non-durable client
|
||||
// or the ClientCache is closing.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe internal method used to close the {@link ClientCache} and preserve durability.
|
||||
*
|
||||
* @param cache {@link GemFireCache} to close.
|
||||
* @see org.apache.geode.cache.client.ClientCache#close(boolean)
|
||||
* @see #isKeepAlive()
|
||||
*/
|
||||
@Override
|
||||
protected void close(GemFireCache cache) {
|
||||
((ClientCache) cache).close(isKeepAlive());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Class} type of the {@link GemFireCache} produced by this {@link ClientCacheFactoryBean}.
|
||||
*
|
||||
* @return the {@link Class} type of the {@link GemFireCache} produced by this {@link ClientCacheFactoryBean}.
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Class<? extends GemFireCache> getObjectType() {
|
||||
return Optional.ofNullable(getCache()).map(Object::getClass).orElse((Class) ClientCache.class);
|
||||
}
|
||||
|
||||
public void addLocators(ConnectionEndpoint... locators) {
|
||||
this.locators.add(locators);
|
||||
}
|
||||
|
||||
public void addLocators(Iterable<ConnectionEndpoint> locators) {
|
||||
this.locators.add(locators);
|
||||
}
|
||||
|
||||
public void addServers(ConnectionEndpoint... servers) {
|
||||
this.servers.add(servers);
|
||||
}
|
||||
|
||||
public void addServers(Iterable<ConnectionEndpoint> servers) {
|
||||
this.servers.add(servers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe operation to set an array of {@link ClientCacheConfigurer ClientCacheConfigurers} used to apply
|
||||
* additional configuration to this {@link ClientCacheFactoryBean} when using Annotation-based configuration.
|
||||
*
|
||||
* @param clientCacheConfigurers array of {@link ClientCacheConfigurer ClientCacheConfigurers} used to apply
|
||||
* additional configuration to this {@link ClientCacheFactoryBean}.
|
||||
* @see org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer
|
||||
* @see #setClientCacheConfigurers(List)
|
||||
*/
|
||||
public void setClientCacheConfigurers(ClientCacheConfigurer... clientCacheConfigurers) {
|
||||
setClientCacheConfigurers(Arrays.asList(nullSafeArray(clientCacheConfigurers, ClientCacheConfigurer.class)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe operation to set an {@link Iterable} of {@link ClientCacheConfigurer ClientCacheConfigurers} to apply
|
||||
* additional configuration to this {@link ClientCacheFactoryBean} when using Annotation-based configuration.
|
||||
*
|
||||
* @param peerCacheConfigurers {@link Iterable} of {@link ClientCacheConfigurer ClientCacheConfigurers} used to apply
|
||||
* additional configuration to this {@link ClientCacheFactoryBean}.
|
||||
* @see org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer
|
||||
*/
|
||||
public void setClientCacheConfigurers(List<ClientCacheConfigurer> peerCacheConfigurers) {
|
||||
this.clientCacheConfigurers = Optional.ofNullable(peerCacheConfigurers).orElseGet(Collections::emptyList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the Composite {@link ClientCacheConfigurer} used to apply additional configuration
|
||||
* to this {@link ClientCacheFactoryBean} on Spring container initialization.
|
||||
*
|
||||
* @return the Composite {@link ClientCacheConfigurer}.
|
||||
* @see org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer
|
||||
*/
|
||||
public ClientCacheConfigurer getCompositeClientCacheConfigurer() {
|
||||
return this.compositeClientCacheConfigurer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the GemFire System property 'durable-client-id' to indicate to the server that this client is durable.
|
||||
*
|
||||
* @param durableClientId a String value indicating the durable client id.
|
||||
*/
|
||||
public void setDurableClientId(String durableClientId) {
|
||||
this.durableClientId = durableClientId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the GemFire System property 'durable-client-id' indicating to the server whether
|
||||
* this client is durable.
|
||||
*
|
||||
* @return a String value indicating the durable client id.
|
||||
*/
|
||||
public String getDurableClientId() {
|
||||
return this.durableClientId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the GemFire System property 'durable-client-timeout' indicating to the server how long to track events
|
||||
* for the durable client when disconnected.
|
||||
*
|
||||
* @param durableClientTimeout an Integer value indicating the timeout in seconds for the server to keep
|
||||
* the durable client's queue around.
|
||||
*/
|
||||
public void setDurableClientTimeout(Integer durableClientTimeout) {
|
||||
this.durableClientTimeout = durableClientTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the GemFire System property 'durable-client-timeout' indicating to the server how long
|
||||
* to track events for the durable client when disconnected.
|
||||
*
|
||||
* @return an Integer value indicating the timeout in seconds for the server to keep
|
||||
* the durable client's queue around.
|
||||
*/
|
||||
public Integer getDurableClientTimeout() {
|
||||
return this.durableClientTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setEnableAutoReconnect(Boolean enableAutoReconnect) {
|
||||
throw new UnsupportedOperationException("Auto-reconnect does not apply to clients");
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Boolean getEnableAutoReconnect() {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
|
||||
public void setFreeConnectionTimeout(Integer freeConnectionTimeout) {
|
||||
this.freeConnectionTimeout = freeConnectionTimeout;
|
||||
}
|
||||
|
||||
public Integer getFreeConnectionTimeout() {
|
||||
return this.freeConnectionTimeout;
|
||||
}
|
||||
|
||||
public void setIdleTimeout(Long idleTimeout) {
|
||||
this.idleTimeout = idleTimeout;
|
||||
}
|
||||
|
||||
public Long getIdleTimeout() {
|
||||
return this.idleTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the server(s) should keep the durable client's queue alive for the duration of the timeout
|
||||
* when the client voluntarily disconnects.
|
||||
*
|
||||
* @param keepAlive a boolean value indicating to the server to keep the durable client's queues alive.
|
||||
*/
|
||||
public void setKeepAlive(Boolean keepAlive) {
|
||||
this.keepAlive = keepAlive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user specified value for whether the server(s) should keep the durable client's queue alive
|
||||
* for the duration of the timeout when the client voluntarily disconnects.
|
||||
*
|
||||
* @return a boolean value indicating whether the server should keep the durable client's queues alive.
|
||||
*/
|
||||
public Boolean getKeepAlive() {
|
||||
return this.keepAlive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the server(s) should keep the durable client's queue alive for the duration of the timeout
|
||||
* when the client voluntarily disconnects.
|
||||
*
|
||||
* @return a boolean value indicating whether the server should keep the durable client's queues alive.
|
||||
*/
|
||||
public boolean isKeepAlive() {
|
||||
return Boolean.TRUE.equals(getKeepAlive());
|
||||
}
|
||||
|
||||
public void setLoadConditioningInterval(Integer loadConditioningInterval) {
|
||||
this.loadConditioningInterval = loadConditioningInterval;
|
||||
}
|
||||
|
||||
public Integer getLoadConditioningInterval() {
|
||||
return this.loadConditioningInterval;
|
||||
}
|
||||
|
||||
public void setLocators(ConnectionEndpoint[] locators) {
|
||||
setLocators(ConnectionEndpointList.from(locators));
|
||||
}
|
||||
|
||||
public void setLocators(Iterable<ConnectionEndpoint> locators) {
|
||||
getLocators().clear();
|
||||
addLocators(locators);
|
||||
}
|
||||
|
||||
protected ConnectionEndpointList getLocators() {
|
||||
return this.locators;
|
||||
}
|
||||
|
||||
public void setMaxConnections(Integer maxConnections) {
|
||||
this.maxConnections = maxConnections;
|
||||
}
|
||||
|
||||
public Integer getMaxConnections() {
|
||||
return this.maxConnections;
|
||||
}
|
||||
|
||||
public void setMinConnections(Integer minConnections) {
|
||||
this.minConnections = minConnections;
|
||||
}
|
||||
|
||||
public Integer getMinConnections() {
|
||||
return this.minConnections;
|
||||
}
|
||||
|
||||
public void setMultiUserAuthentication(Boolean multiUserAuthentication) {
|
||||
this.multiUserAuthentication = multiUserAuthentication;
|
||||
}
|
||||
|
||||
public Boolean getMultiUserAuthentication() {
|
||||
return this.multiUserAuthentication;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link Pool} used by this cache client to obtain connections to the GemFire cluster.
|
||||
*
|
||||
* @param pool the GemFire {@link Pool} used by this {@link ClientCache} to obtain connections
|
||||
* to the GemFire cluster.
|
||||
* @throws IllegalArgumentException if the {@link Pool} is null.
|
||||
*/
|
||||
public void setPool(Pool pool) {
|
||||
this.pool = pool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link Pool} used by this cache client to obtain connections to the GemFire cluster.
|
||||
*
|
||||
* @return the GemFire {@link Pool} used by this {@link ClientCache} to obtain connections
|
||||
* to the GemFire cluster.
|
||||
*/
|
||||
public Pool getPool() {
|
||||
return this.pool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the {@link Pool} used by this cache client to obtain connections to the GemFire cluster.
|
||||
*
|
||||
* @param poolName set the name of the GemFire {@link Pool} used by this GemFire {@link ClientCache}.
|
||||
* @throws IllegalArgumentException if the {@link Pool} name is unspecified.
|
||||
*/
|
||||
public void setPoolName(String poolName) {
|
||||
this.poolName = poolName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the GemFire {@link Pool} used by this GemFire cache client.
|
||||
*
|
||||
* @return the name of the GemFire {@link Pool} used by this GemFire cache client.
|
||||
*/
|
||||
public String getPoolName() {
|
||||
return this.poolName;
|
||||
}
|
||||
|
||||
public void setPingInterval(Long pingInterval) {
|
||||
this.pingInterval = pingInterval;
|
||||
}
|
||||
|
||||
public Long getPingInterval() {
|
||||
return this.pingInterval;
|
||||
}
|
||||
|
||||
public void setPrSingleHopEnabled(Boolean prSingleHopEnabled) {
|
||||
this.prSingleHopEnabled = prSingleHopEnabled;
|
||||
}
|
||||
|
||||
public Boolean getPrSingleHopEnabled() {
|
||||
return this.prSingleHopEnabled;
|
||||
}
|
||||
|
||||
public void setReadTimeout(Integer readTimeout) {
|
||||
this.readTimeout = readTimeout;
|
||||
}
|
||||
|
||||
public Integer getReadTimeout() {
|
||||
return this.readTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the readyForEvents property to indicate whether the cache client should notify the server
|
||||
* that it is ready to receive updates.
|
||||
*
|
||||
* @param readyForEvents sets a boolean flag to notify the server that this durable client
|
||||
* is ready to receive updates.
|
||||
* @see #getReadyForEvents()
|
||||
*/
|
||||
public void setReadyForEvents(Boolean readyForEvents){
|
||||
this.readyForEvents = readyForEvents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user-specified value for the readyForEvents property.
|
||||
*
|
||||
* @return a boolean value indicating the state of the 'readyForEvents' property.
|
||||
*/
|
||||
public Boolean getReadyForEvents(){
|
||||
return this.readyForEvents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether this GemFire cache client is ready for events. If 'readyForEvents' was explicitly set,
|
||||
* then it takes precedence over all other considerations (e.g. durability).
|
||||
*
|
||||
* @return a boolean value indicating whether this GemFire cache client is ready for events.
|
||||
* @see org.springframework.data.gemfire.GemfireUtils#isDurable(ClientCache)
|
||||
* @see #getReadyForEvents()
|
||||
*/
|
||||
public boolean isReadyForEvents() {
|
||||
|
||||
Boolean readyForEvents = getReadyForEvents();
|
||||
|
||||
if (readyForEvents != null) {
|
||||
return Boolean.TRUE.equals(readyForEvents);
|
||||
}
|
||||
else {
|
||||
try {
|
||||
return GemfireUtils.isDurable(fetchCache());
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setRetryAttempts(Integer retryAttempts) {
|
||||
this.retryAttempts = retryAttempts;
|
||||
}
|
||||
|
||||
public Integer getRetryAttempts() {
|
||||
return this.retryAttempts;
|
||||
}
|
||||
|
||||
public void setServerGroup(String serverGroup) {
|
||||
this.serverGroup = serverGroup;
|
||||
}
|
||||
|
||||
public String getServerGroup() {
|
||||
return this.serverGroup;
|
||||
}
|
||||
|
||||
public void setServers(ConnectionEndpoint[] servers) {
|
||||
setServers(ConnectionEndpointList.from(servers));
|
||||
}
|
||||
|
||||
public void setServers(Iterable<ConnectionEndpoint> servers) {
|
||||
getServers().clear();
|
||||
addServers(servers);
|
||||
}
|
||||
|
||||
protected ConnectionEndpointList getServers() {
|
||||
return this.servers;
|
||||
}
|
||||
|
||||
public void setSocketBufferSize(Integer socketBufferSize) {
|
||||
this.socketBufferSize = socketBufferSize;
|
||||
}
|
||||
|
||||
public Integer getSocketBufferSize() {
|
||||
return this.socketBufferSize;
|
||||
}
|
||||
|
||||
public void setSocketConnectTimeout(Integer socketConnectTimeout) {
|
||||
this.socketConnectTimeout = socketConnectTimeout;
|
||||
}
|
||||
|
||||
public Integer getSocketConnectTimeout() {
|
||||
return this.socketConnectTimeout;
|
||||
}
|
||||
|
||||
public void setStatisticsInterval(Integer statisticsInterval) {
|
||||
this.statisticsInterval = statisticsInterval;
|
||||
}
|
||||
|
||||
public Integer getStatisticsInterval() {
|
||||
return this.statisticsInterval;
|
||||
}
|
||||
|
||||
public void setSubscriptionAckInterval(Integer subscriptionAckInterval) {
|
||||
this.subscriptionAckInterval = subscriptionAckInterval;
|
||||
}
|
||||
|
||||
public Integer getSubscriptionAckInterval() {
|
||||
return this.subscriptionAckInterval;
|
||||
}
|
||||
|
||||
public void setSubscriptionEnabled(Boolean subscriptionEnabled) {
|
||||
this.subscriptionEnabled = subscriptionEnabled;
|
||||
}
|
||||
|
||||
public Boolean getSubscriptionEnabled() {
|
||||
return this.subscriptionEnabled;
|
||||
}
|
||||
|
||||
public void setSubscriptionMessageTrackingTimeout(Integer subscriptionMessageTrackingTimeout) {
|
||||
this.subscriptionMessageTrackingTimeout = subscriptionMessageTrackingTimeout;
|
||||
}
|
||||
|
||||
public Integer getSubscriptionMessageTrackingTimeout() {
|
||||
return this.subscriptionMessageTrackingTimeout;
|
||||
}
|
||||
|
||||
public void setSubscriptionRedundancy(Integer subscriptionRedundancy) {
|
||||
this.subscriptionRedundancy = subscriptionRedundancy;
|
||||
}
|
||||
|
||||
public Integer getSubscriptionRedundancy() {
|
||||
return this.subscriptionRedundancy;
|
||||
}
|
||||
|
||||
public void setThreadLocalConnections(Boolean threadLocalConnections) {
|
||||
this.threadLocalConnections = threadLocalConnections;
|
||||
}
|
||||
|
||||
public Boolean getThreadLocalConnections() {
|
||||
return this.threadLocalConnections;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setUseClusterConfiguration(Boolean useClusterConfiguration) {
|
||||
throw new UnsupportedOperationException("Cluster-based Configuration is not applicable for clients");
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Boolean getUseClusterConfiguration() {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
}
|
||||
@@ -1,736 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.gemfire.client;
|
||||
|
||||
import static java.util.Arrays.stream;
|
||||
import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
|
||||
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalArgumentException;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.apache.geode.cache.CacheListener;
|
||||
import org.apache.geode.cache.CacheLoader;
|
||||
import org.apache.geode.cache.CacheWriter;
|
||||
import org.apache.geode.cache.CustomExpiry;
|
||||
import org.apache.geode.cache.DataPolicy;
|
||||
import org.apache.geode.cache.EvictionAttributes;
|
||||
import org.apache.geode.cache.ExpirationAttributes;
|
||||
import org.apache.geode.cache.GemFireCache;
|
||||
import org.apache.geode.cache.Region;
|
||||
import org.apache.geode.cache.RegionAttributes;
|
||||
import org.apache.geode.cache.client.ClientCache;
|
||||
import org.apache.geode.cache.client.ClientRegionFactory;
|
||||
import org.apache.geode.cache.client.ClientRegionShortcut;
|
||||
import org.apache.geode.cache.client.Pool;
|
||||
import org.apache.geode.cache.client.PoolManager;
|
||||
import org.apache.geode.compression.Compressor;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.data.gemfire.ConfigurableRegionFactoryBean;
|
||||
import org.springframework.data.gemfire.GemfireUtils;
|
||||
import org.springframework.data.gemfire.config.xml.GemfireConstants;
|
||||
import org.springframework.data.gemfire.eviction.EvictingRegionFactoryBean;
|
||||
import org.springframework.data.gemfire.expiration.ExpiringRegionFactoryBean;
|
||||
import org.springframework.data.gemfire.support.SmartLifecycleSupport;
|
||||
import org.springframework.data.gemfire.util.RegionUtils;
|
||||
import org.springframework.data.gemfire.util.SpringUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Spring {@link FactoryBean} used to construct, configure and initialize a client {@link Region}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author David Turanski
|
||||
* @author John Blum
|
||||
* @see org.apache.geode.cache.DataPolicy
|
||||
* @see org.apache.geode.cache.EvictionAttributes
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
* @see org.apache.geode.cache.Region
|
||||
* @see org.apache.geode.cache.RegionAttributes
|
||||
* @see org.apache.geode.cache.client.ClientCache
|
||||
* @see org.apache.geode.cache.client.ClientRegionFactory
|
||||
* @see org.apache.geode.cache.client.ClientRegionShortcut
|
||||
* @see org.apache.geode.cache.client.Pool
|
||||
* @see org.springframework.beans.factory.DisposableBean
|
||||
* @see org.springframework.beans.factory.FactoryBean
|
||||
* @see org.springframework.data.gemfire.DataPolicyConverter
|
||||
* @see org.springframework.data.gemfire.ResolvableRegionFactoryBean
|
||||
* @see org.springframework.data.gemfire.config.annotation.RegionConfigurer
|
||||
* @see org.springframework.data.gemfire.support.SmartLifecycleSupport
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class ClientRegionFactoryBean<K, V> extends ConfigurableRegionFactoryBean<K, V>
|
||||
implements SmartLifecycleSupport, EvictingRegionFactoryBean, ExpiringRegionFactoryBean<K, V>, DisposableBean {
|
||||
|
||||
public static final String DEFAULT_POOL_NAME = "DEFAULT";
|
||||
public static final String GEMFIRE_POOL_NAME = GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME;
|
||||
|
||||
private boolean close = false;
|
||||
private boolean destroy = false;
|
||||
|
||||
private Boolean cloningEnabled;
|
||||
private Boolean concurrencyChecksEnabled;
|
||||
private Boolean diskSynchronous;
|
||||
private Boolean persistent;
|
||||
private Boolean statisticsEnabled;
|
||||
|
||||
private CacheListener<K, V>[] cacheListeners;
|
||||
|
||||
private CacheLoader<K, V> cacheLoader;
|
||||
|
||||
private CacheWriter<K, V> cacheWriter;
|
||||
|
||||
private Class<K> keyConstraint;
|
||||
private Class<V> valueConstraint;
|
||||
|
||||
private ClientRegionShortcut shortcut;
|
||||
|
||||
private Compressor compressor;
|
||||
|
||||
private CustomExpiry<K, V> customEntryIdleTimeout;
|
||||
private CustomExpiry<K, V> customEntryTimeToLive;
|
||||
|
||||
private DataPolicy dataPolicy;
|
||||
|
||||
private EvictionAttributes evictionAttributes;
|
||||
|
||||
private ExpirationAttributes entryIdleTimeout;
|
||||
private ExpirationAttributes entryTimeToLive;
|
||||
private ExpirationAttributes regionIdleTimeout;
|
||||
private ExpirationAttributes regionTimeToLive;
|
||||
|
||||
private Integer concurrencyLevel;
|
||||
private Integer initialCapacity;
|
||||
|
||||
private Interest<K>[] interests;
|
||||
|
||||
private Float loadFactor;
|
||||
|
||||
private RegionAttributes<K, V> attributes;
|
||||
|
||||
private String diskStoreName;
|
||||
private String poolName;
|
||||
|
||||
/**
|
||||
* Creates a new {@link Region} with the given {@link String name}.
|
||||
*
|
||||
* @param gemfireCache reference to the {@link GemFireCache}.
|
||||
* @param regionName {@link String name} of the new {@link Region}.
|
||||
* @return a new {@link Region} with the given {@link String name}.
|
||||
* @see #createClientRegionFactory(ClientCache, ClientRegionShortcut)
|
||||
* @see #newRegion(ClientRegionFactory, Region, String)
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
* @see org.apache.geode.cache.Region
|
||||
*/
|
||||
@Override
|
||||
protected Region<K, V> createRegion(GemFireCache gemfireCache, String regionName) {
|
||||
|
||||
applyRegionConfigurers(regionName);
|
||||
|
||||
ClientCache clientCache = resolveCache(gemfireCache);
|
||||
|
||||
ClientRegionFactory<K, V> clientRegionFactory =
|
||||
postProcess(configure(createClientRegionFactory(clientCache, resolveClientRegionShortcut())));
|
||||
|
||||
return newRegion(clientRegionFactory, getParent(), regionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@link Region} using the provided {@link ClientRegionFactory} as either
|
||||
* a {@link Region root Region} or a {@link Region sub-Region} if {@link Region parent}
|
||||
* is not {@literal null}.
|
||||
*
|
||||
* @param clientRegionFactory {@link ClientRegionFactory} containing the configuration
|
||||
* for the new {@link Region}.
|
||||
* @param parent {@link Region} designated as the parent of the new {@link Region}
|
||||
* if the new {@link Region} is a {@link Region sub-Region}.
|
||||
* @param regionName {@link String name} of the new {@link Region}.
|
||||
* @return the new {@link Region} initialized with the given {@link String name}.
|
||||
*/
|
||||
private Region<K, V> newRegion(ClientRegionFactory<K, V> clientRegionFactory,
|
||||
Region<?, ?> parent, String regionName) {
|
||||
|
||||
if (parent != null) {
|
||||
|
||||
logInfo("Creating client sub-Region [%1$s] with parent Region [%2$s]",
|
||||
regionName, parent.getName());
|
||||
|
||||
return clientRegionFactory.createSubregion(parent, regionName);
|
||||
}
|
||||
else {
|
||||
|
||||
logInfo("Creating client Region [%s]", regionName);
|
||||
|
||||
return clientRegionFactory.create(regionName);
|
||||
}
|
||||
}
|
||||
|
||||
private ClientCache resolveCache(GemFireCache gemfireCache) {
|
||||
|
||||
return Optional.ofNullable(gemfireCache)
|
||||
.filter(GemfireUtils::isClient)
|
||||
.map(cache -> (ClientCache) cache)
|
||||
.orElseThrow(() -> newIllegalArgumentException("ClientCache is required"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the {@link ClientRegionShortcut} used to configure the {@link DataPolicy}
|
||||
* for the {@link Region client Region}.
|
||||
*
|
||||
* @return a {@link ClientRegionShortcut} used to configure the {@link DataPolicy}
|
||||
* for the {@link Region client Region}.
|
||||
* @see org.apache.geode.cache.client.ClientRegionShortcut
|
||||
* @see org.apache.geode.cache.DataPolicy
|
||||
*/
|
||||
ClientRegionShortcut resolveClientRegionShortcut() {
|
||||
|
||||
ClientRegionShortcut resolvedShortcut = this.shortcut;
|
||||
|
||||
if (resolvedShortcut == null) {
|
||||
|
||||
DataPolicy dataPolicy = this.dataPolicy;
|
||||
|
||||
if (dataPolicy != null) {
|
||||
|
||||
RegionUtils.assertDataPolicyAndPersistentAttributeAreCompatible(dataPolicy, this.persistent);
|
||||
|
||||
if (DataPolicy.EMPTY.equals(dataPolicy)) {
|
||||
resolvedShortcut = ClientRegionShortcut.PROXY;
|
||||
}
|
||||
else if (DataPolicy.NORMAL.equals(dataPolicy)) {
|
||||
resolvedShortcut = ClientRegionShortcut.CACHING_PROXY;
|
||||
}
|
||||
else if (DataPolicy.PERSISTENT_REPLICATE.equals(dataPolicy)) {
|
||||
resolvedShortcut = ClientRegionShortcut.LOCAL_PERSISTENT;
|
||||
}
|
||||
else {
|
||||
// NOTE: DataPolicy validation is based on the ClientRegionShortcut initialization logic
|
||||
// in org.apache.geode.internal.cache.GemFireCacheImpl.initializeClientRegionShortcuts.
|
||||
throw newIllegalArgumentException("Data Policy [%s] is not valid for a client Region", dataPolicy);
|
||||
}
|
||||
}
|
||||
else {
|
||||
resolvedShortcut = isPersistent() ? ClientRegionShortcut.LOCAL_PERSISTENT : ClientRegionShortcut.LOCAL;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: The ClientRegionShortcut and Persistent attribute will be compatible
|
||||
// if the shortcut was derived from the DataPolicy.
|
||||
RegionUtils.assertClientRegionShortcutAndPersistentAttributeAreCompatible(resolvedShortcut, this.persistent);
|
||||
|
||||
return resolvedShortcut;
|
||||
}
|
||||
|
||||
private String resolvePoolName(String factoryPoolName, String attributesPoolName) {
|
||||
|
||||
String resolvedPoolName = StringUtils.hasText(factoryPoolName) ? factoryPoolName : attributesPoolName;
|
||||
|
||||
return Optional.ofNullable(resolvedPoolName)
|
||||
.filter(StringUtils::hasText)
|
||||
.filter(GemfireUtils::isNotDefaultPool)
|
||||
.map(it -> {
|
||||
|
||||
Assert.isTrue(eagerlyInitializePool(it),
|
||||
String.format("[%s] is not resolvable as a Pool in the application context", it));
|
||||
|
||||
return it;
|
||||
})
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private boolean eagerlyInitializePool(String poolName) {
|
||||
|
||||
return Optional.ofNullable(PoolManager.find(poolName))
|
||||
.map(it -> true)
|
||||
.orElseGet(() ->
|
||||
SpringUtils.safeGetValue(() ->
|
||||
getBeanFactory().getBean(poolName, Pool.class) != null, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance of {@link ClientRegionFactory} using the given {@link ClientCache}
|
||||
* and {@link ClientRegionShortcut}.
|
||||
*
|
||||
* @param clientCache reference to the {@link ClientCache}.
|
||||
* @param clientRegionShortcut {@link ClientRegionShortcut} used to configure
|
||||
* the {@link Region client Region} {@link DataPolicy}.
|
||||
* @return a new instance of {@link ClientRegionFactory}.
|
||||
* @see org.apache.geode.cache.client.ClientCache#createClientRegionFactory(ClientRegionShortcut)
|
||||
* @see org.apache.geode.cache.client.ClientRegionShortcut
|
||||
* @see org.apache.geode.cache.client.ClientRegionFactory
|
||||
*/
|
||||
protected ClientRegionFactory<K, V> createClientRegionFactory(ClientCache clientCache,
|
||||
ClientRegionShortcut clientRegionShortcut) {
|
||||
|
||||
return clientCache.createClientRegionFactory(clientRegionShortcut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the given {@link ClientRegionFactoryBean} from the configuration settings
|
||||
* of this {@link ClientRegionFactoryBean} and any {@link RegionAttributes}.
|
||||
*
|
||||
* @param clientRegionFactory {@link ClientRegionFactory} to configure.
|
||||
* @return the configured {@link ClientRegionFactory}.
|
||||
* @see org.apache.geode.cache.client.ClientRegionFactory
|
||||
*/
|
||||
protected ClientRegionFactory<K, V> configure(ClientRegionFactory<K, V> clientRegionFactory) {
|
||||
|
||||
Optional<String> regionAttributesPoolName = configureWithRegionAttributes(clientRegionFactory);
|
||||
|
||||
stream(nullSafeArray(this.cacheListeners, CacheListener.class)).forEach(clientRegionFactory::addCacheListener);
|
||||
|
||||
clientRegionFactory.setStatisticsEnabled(resolveStatisticsEnabled());
|
||||
|
||||
Optional.ofNullable(this.cloningEnabled).ifPresent(clientRegionFactory::setCloningEnabled);
|
||||
Optional.ofNullable(this.compressor).ifPresent(clientRegionFactory::setCompressor);
|
||||
Optional.ofNullable(this.concurrencyChecksEnabled).ifPresent(clientRegionFactory::setConcurrencyChecksEnabled);
|
||||
Optional.ofNullable(this.concurrencyLevel).ifPresent(clientRegionFactory::setConcurrencyLevel);
|
||||
Optional.ofNullable(this.customEntryIdleTimeout).ifPresent(clientRegionFactory::setCustomEntryIdleTimeout);
|
||||
Optional.ofNullable(this.customEntryTimeToLive).ifPresent(clientRegionFactory::setCustomEntryTimeToLive);
|
||||
Optional.ofNullable(this.diskStoreName).filter(StringUtils::hasText).ifPresent(clientRegionFactory::setDiskStoreName);
|
||||
Optional.ofNullable(this.diskSynchronous).ifPresent(clientRegionFactory::setDiskSynchronous);
|
||||
Optional.ofNullable(this.entryIdleTimeout).ifPresent(clientRegionFactory::setEntryIdleTimeout);
|
||||
Optional.ofNullable(this.entryTimeToLive).ifPresent(clientRegionFactory::setEntryTimeToLive);
|
||||
Optional.ofNullable(this.evictionAttributes).ifPresent(clientRegionFactory::setEvictionAttributes);
|
||||
Optional.ofNullable(this.initialCapacity).ifPresent(clientRegionFactory::setInitialCapacity);
|
||||
Optional.ofNullable(this.keyConstraint).ifPresent(clientRegionFactory::setKeyConstraint);
|
||||
Optional.ofNullable(this.loadFactor).ifPresent(clientRegionFactory::setLoadFactor);
|
||||
|
||||
Optional.ofNullable(resolvePoolName(getPoolName().orElse(null), regionAttributesPoolName.orElse(null)))
|
||||
.ifPresent(clientRegionFactory::setPoolName);
|
||||
|
||||
Optional.ofNullable(this.regionIdleTimeout).ifPresent(clientRegionFactory::setRegionIdleTimeout);
|
||||
Optional.ofNullable(this.regionTimeToLive).ifPresent(clientRegionFactory::setRegionTimeToLive);
|
||||
Optional.ofNullable(this.valueConstraint).ifPresent(clientRegionFactory::setValueConstraint);
|
||||
|
||||
return clientRegionFactory;
|
||||
}
|
||||
|
||||
private Optional<String> configureWithRegionAttributes(ClientRegionFactory<K, V> clientRegionFactory) {
|
||||
|
||||
AtomicReference<String> regionAttributesPoolName = new AtomicReference<>(null);
|
||||
|
||||
Optional.ofNullable(getAttributes()).ifPresent(regionAttributes -> {
|
||||
|
||||
regionAttributesPoolName.set(regionAttributes.getPoolName());
|
||||
|
||||
stream(nullSafeArray(regionAttributes.getCacheListeners(), CacheListener.class))
|
||||
.forEach(clientRegionFactory::addCacheListener);
|
||||
|
||||
clientRegionFactory.setCloningEnabled(regionAttributes.getCloningEnabled());
|
||||
clientRegionFactory.setCompressor(regionAttributes.getCompressor());
|
||||
clientRegionFactory.setConcurrencyChecksEnabled(regionAttributes.getConcurrencyChecksEnabled());
|
||||
clientRegionFactory.setConcurrencyLevel(regionAttributes.getConcurrencyLevel());
|
||||
clientRegionFactory.setCustomEntryIdleTimeout(regionAttributes.getCustomEntryIdleTimeout());
|
||||
clientRegionFactory.setCustomEntryTimeToLive(regionAttributes.getCustomEntryTimeToLive());
|
||||
clientRegionFactory.setDiskStoreName(regionAttributes.getDiskStoreName());
|
||||
clientRegionFactory.setDiskSynchronous(regionAttributes.isDiskSynchronous());
|
||||
clientRegionFactory.setEntryIdleTimeout(regionAttributes.getEntryIdleTimeout());
|
||||
clientRegionFactory.setEntryTimeToLive(regionAttributes.getEntryTimeToLive());
|
||||
clientRegionFactory.setEvictionAttributes(regionAttributes.getEvictionAttributes());
|
||||
clientRegionFactory.setInitialCapacity(regionAttributes.getInitialCapacity());
|
||||
clientRegionFactory.setKeyConstraint(regionAttributes.getKeyConstraint());
|
||||
clientRegionFactory.setLoadFactor(regionAttributes.getLoadFactor());
|
||||
clientRegionFactory.setRegionIdleTimeout(regionAttributes.getRegionIdleTimeout());
|
||||
clientRegionFactory.setRegionTimeToLive(regionAttributes.getRegionTimeToLive());
|
||||
clientRegionFactory.setStatisticsEnabled(regionAttributes.getStatisticsEnabled());
|
||||
clientRegionFactory.setValueConstraint(regionAttributes.getValueConstraint());
|
||||
});
|
||||
|
||||
return Optional.ofNullable(regionAttributesPoolName.get()).filter(StringUtils::hasText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Post-process the given {@link ClientRegionFactory} setup by this {@link ClientRegionFactoryBean}.
|
||||
*
|
||||
* @param clientRegionFactory {@link ClientRegionFactory} to process.
|
||||
* @return the given {@link ClientRegionFactory}.
|
||||
* @see org.apache.geode.cache.client.ClientRegionFactory
|
||||
*/
|
||||
protected ClientRegionFactory<K, V> postProcess(ClientRegionFactory<K, V> clientRegionFactory) {
|
||||
return clientRegionFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Post-process the {@link Region} created by this {@link ClientRegionFactoryBean}.
|
||||
*
|
||||
* @param region {@link Region} to process.
|
||||
* @see org.apache.geode.cache.Region
|
||||
*/
|
||||
@Override
|
||||
protected Region<K, V> postProcess(Region<K, V> region) {
|
||||
|
||||
super.postProcess(region);
|
||||
|
||||
Optional.ofNullable(this.cacheLoader)
|
||||
.ifPresent(cacheLoader -> region.getAttributesMutator().setCacheLoader(cacheLoader));
|
||||
|
||||
Optional.ofNullable(this.cacheWriter)
|
||||
.ifPresent(cacheWriter -> region.getAttributesMutator().setCacheWriter(cacheWriter));
|
||||
|
||||
return region;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers interests in the startup lifecycle phase of the Spring container.
|
||||
*
|
||||
* @see #getRegion()
|
||||
* @see #registerInterests(Region)
|
||||
*/
|
||||
@Override
|
||||
public void start() {
|
||||
registerInterests(getRegion());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Region<K, V> registerInterests(Region<K, V> region) {
|
||||
|
||||
stream(nullSafeArray(getInterests(), Interest.class)).forEach(interest -> {
|
||||
|
||||
if (interest.isRegexType()) {
|
||||
region.registerInterestRegex((String) interest.getKey(), interest.getPolicy(),
|
||||
interest.isDurable(), interest.isReceiveValues());
|
||||
}
|
||||
else {
|
||||
region.registerInterest(((Interest<K>) interest).getKey(), interest.getPolicy(),
|
||||
interest.isDurable(), interest.isReceiveValues());
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return region;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes and destroys the {@link Region}.
|
||||
*
|
||||
* @throws Exception if destroy fails.
|
||||
* @see org.springframework.beans.factory.DisposableBean
|
||||
*/
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
|
||||
Optional.ofNullable(getObject()).ifPresent(region -> {
|
||||
|
||||
if (isClose() && RegionUtils.isCloseable(region)) {
|
||||
RegionUtils.close(region);
|
||||
}
|
||||
|
||||
if (isDestroy()) {
|
||||
region.destroyRegion();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the region attributes used for the region used by this factory.
|
||||
* Allows maximum control in specifying the region settings. Used only when
|
||||
* a new region is created. Note that using this method allows for advanced
|
||||
* customization of the region - while it provides a lot of flexibility,
|
||||
* note that it's quite easy to create misconfigured regions (especially in
|
||||
* a client/server scenario).
|
||||
*
|
||||
* @param attributes the attributes to set on a newly created region
|
||||
*/
|
||||
public void setAttributes(RegionAttributes<K, V> attributes) {
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link RegionAttributes} used to configure the {@link Region client Region}
|
||||
* created by this {@link ClientRegionFactoryBean}.
|
||||
*
|
||||
* @return the {@link RegionAttributes} used to configure the {@link Region client Region}.
|
||||
* @see org.apache.geode.cache.RegionAttributes
|
||||
*/
|
||||
protected RegionAttributes<K, V> getAttributes() {
|
||||
return this.attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cache listeners used for the region used by this factory. Used
|
||||
* only when a new region is created.Overrides the settings specified
|
||||
* through {@link #setAttributes(RegionAttributes)}.
|
||||
*
|
||||
* @param cacheListeners the cacheListeners to set on a newly created region
|
||||
*/
|
||||
public void setCacheListeners(CacheListener<K, V>[] cacheListeners) {
|
||||
this.cacheListeners = cacheListeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the CacheLoader used to load data local to the client's Region on cache misses.
|
||||
*
|
||||
* @param cacheLoader a GemFire CacheLoader used to load data into the client Region.
|
||||
* @see org.apache.geode.cache.CacheLoader
|
||||
*/
|
||||
public void setCacheLoader(CacheLoader<K, V> cacheLoader) {
|
||||
this.cacheLoader = cacheLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the CacheWriter used to perform a synchronous write-behind when data is put into the client's Region.
|
||||
*
|
||||
* @param cacheWriter the GemFire CacheWriter used to perform synchronous write-behinds on put ops.
|
||||
* @see org.apache.geode.cache.CacheWriter
|
||||
*/
|
||||
public void setCacheWriter(CacheWriter<K, V> cacheWriter) {
|
||||
this.cacheWriter = cacheWriter;
|
||||
}
|
||||
|
||||
public void setCloningEnabled(Boolean cloningEnabled) {
|
||||
this.cloningEnabled = cloningEnabled;
|
||||
}
|
||||
|
||||
final boolean isClose() {
|
||||
return this.close;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the region referred by this factory bean will be closed on shutdown (default true).
|
||||
*
|
||||
* Note: destroy and close are mutually exclusive. Enabling one will automatically disable the other.
|
||||
*
|
||||
* @param close whether to close or not the region
|
||||
* @see #setDestroy(boolean)
|
||||
*/
|
||||
public void setClose(boolean close) {
|
||||
this.close = close;
|
||||
this.destroy = this.destroy && !close; // retain previous value iff close is false.
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the {@link Compressor} used to compress the this {@link Region Region's} data.
|
||||
*
|
||||
* @param compressor {@link Compressor} used to compress the this {@link Region Region's} data.
|
||||
* @see org.apache.geode.compression.Compressor
|
||||
*/
|
||||
public void setCompressor(Compressor compressor) {
|
||||
this.compressor = compressor;
|
||||
}
|
||||
|
||||
public void setConcurrencyChecksEnabled(Boolean concurrencyChecksEnabled) {
|
||||
this.concurrencyChecksEnabled = concurrencyChecksEnabled;
|
||||
}
|
||||
|
||||
public void setConcurrencyLevel(Integer concurrencyLevel) {
|
||||
this.concurrencyLevel = concurrencyLevel;
|
||||
}
|
||||
|
||||
public void setCustomEntryIdleTimeout(CustomExpiry<K, V> customEntryIdleTimeout) {
|
||||
this.customEntryIdleTimeout = customEntryIdleTimeout;
|
||||
}
|
||||
|
||||
public void setCustomEntryTimeToLive(CustomExpiry<K, V> customEntryTimeToLive) {
|
||||
this.customEntryTimeToLive = customEntryTimeToLive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Data Policy. Used only when a new Region is created.
|
||||
*
|
||||
* @param dataPolicy the client Region's Data Policy.
|
||||
* @see org.apache.geode.cache.DataPolicy
|
||||
*/
|
||||
public void setDataPolicy(DataPolicy dataPolicy) {
|
||||
this.dataPolicy = dataPolicy;
|
||||
}
|
||||
|
||||
final boolean isDestroy() {
|
||||
return this.destroy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the region referred by this factory bean will be
|
||||
* destroyed on shutdown (default false). Note: destroy and close are
|
||||
* mutually exclusive. Enabling one will automatically disable the other.
|
||||
*
|
||||
* @param destroy whether or not to destroy the region
|
||||
* @see #setClose(boolean)
|
||||
*/
|
||||
public void setDestroy(boolean destroy) {
|
||||
this.destroy = destroy;
|
||||
this.close = this.close && !destroy; // retain previous value iff destroy is false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of disk store to use for overflow and persistence
|
||||
*
|
||||
* @param diskStoreName a String specifying the 'name' of the client Region Disk Store.
|
||||
*/
|
||||
public void setDiskStoreName(String diskStoreName) {
|
||||
this.diskStoreName = diskStoreName;
|
||||
}
|
||||
|
||||
public void setDiskSynchronous(Boolean diskSynchronous) {
|
||||
this.diskSynchronous = diskSynchronous;
|
||||
}
|
||||
|
||||
public void setEntryIdleTimeout(ExpirationAttributes entryIdleTimeout) {
|
||||
this.entryIdleTimeout = entryIdleTimeout;
|
||||
}
|
||||
|
||||
public void setEntryTimeToLive(ExpirationAttributes entryTimeToLive) {
|
||||
this.entryTimeToLive = entryTimeToLive;
|
||||
}
|
||||
|
||||
public void setEvictionAttributes(EvictionAttributes evictionAttributes) {
|
||||
this.evictionAttributes = evictionAttributes;
|
||||
}
|
||||
|
||||
public void setInitialCapacity(Integer initialCapacity) {
|
||||
this.initialCapacity = initialCapacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the interests for this client region. Both key and regex interest are
|
||||
* supported.
|
||||
*
|
||||
* @param interests the interests to set
|
||||
*/
|
||||
public void setInterests(Interest<K>[] interests) {
|
||||
this.interests = interests;
|
||||
}
|
||||
|
||||
Interest<K>[] getInterests() {
|
||||
return this.interests;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a {@link Class type} constraint on this {@link Region client Region's} keys.
|
||||
*
|
||||
* @param keyConstraint {@link Class type} of this {@link Region client Region's} keys.
|
||||
* @see java.lang.Class
|
||||
*/
|
||||
public void setKeyConstraint(Class<K> keyConstraint) {
|
||||
this.keyConstraint = keyConstraint;
|
||||
}
|
||||
|
||||
public void setLoadFactor(Float loadFactor) {
|
||||
this.loadFactor = loadFactor;
|
||||
}
|
||||
|
||||
protected boolean isPersistent() {
|
||||
return Boolean.TRUE.equals(persistent);
|
||||
}
|
||||
|
||||
protected boolean isNotPersistent() {
|
||||
return Boolean.FALSE.equals(persistent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures whether this {@link Region client Region} is persistent, i.e. stores data to disk.
|
||||
*
|
||||
* @param persistent boolean value used to enable disk persistence.
|
||||
*/
|
||||
public void setPersistent(boolean persistent) {
|
||||
this.persistent = persistent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the {@link Pool} used by this {@link Region client Region}.
|
||||
*
|
||||
* @param pool {@link Pool} used by this {@link Region client Region}
|
||||
* to send/receive data to/from the server.
|
||||
* @see org.apache.geode.cache.client.Pool
|
||||
* @see #setPoolName(String)
|
||||
*/
|
||||
public void setPool(Pool pool) {
|
||||
setPoolName(Optional.ofNullable(pool).map(Pool::getName).orElse(null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the {@link String name} of the {@link Pool} used by this {@link Region client Region}.
|
||||
*
|
||||
* @param poolName {@link String} containing the name of the client {@link Pool}
|
||||
* used by this {@link Region client Region}.
|
||||
* @see #getPoolName()
|
||||
* @see #setPool(Pool)
|
||||
*/
|
||||
public void setPoolName(String poolName) {
|
||||
this.poolName = poolName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link String name} of the configured {@link Pool} to use with this {@link Region client Region}.
|
||||
*
|
||||
* @return the {@link Optional} {@link String name} of the configured {@link Pool} to use
|
||||
* with this {@link Region client Region}.
|
||||
* @see #setPoolName(String)
|
||||
*/
|
||||
public Optional<String> getPoolName() {
|
||||
return Optional.ofNullable(this.poolName);
|
||||
}
|
||||
|
||||
public void setRegionIdleTimeout(ExpirationAttributes regionIdleTimeout) {
|
||||
this.regionIdleTimeout = regionIdleTimeout;
|
||||
}
|
||||
|
||||
public void setRegionTimeToLive(ExpirationAttributes regionTimeToLive) {
|
||||
this.regionTimeToLive = regionTimeToLive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the {@link DataPolicy} of the {@link Region client Region}
|
||||
* using the given {@link ClientRegionShortcut}.
|
||||
*
|
||||
* @param shortcut {@link ClientRegionShortcut} used to initialize the {@link DataPolicy}
|
||||
* of this {@link Region client Region}.
|
||||
* @see org.apache.geode.cache.client.ClientRegionShortcut
|
||||
*/
|
||||
public void setShortcut(ClientRegionShortcut shortcut) {
|
||||
this.shortcut = shortcut;
|
||||
}
|
||||
|
||||
public void setStatisticsEnabled(Boolean statisticsEnabled) {
|
||||
this.statisticsEnabled = statisticsEnabled;
|
||||
}
|
||||
|
||||
public Boolean getStatisticsEnabled() {
|
||||
return this.statisticsEnabled;
|
||||
}
|
||||
|
||||
public boolean isStatisticsEnabled() {
|
||||
return Boolean.TRUE.equals(getStatisticsEnabled());
|
||||
}
|
||||
|
||||
protected boolean resolveStatisticsEnabled() {
|
||||
|
||||
return isStatisticsEnabled()
|
||||
|| this.customEntryIdleTimeout != null
|
||||
|| this.customEntryTimeToLive != null
|
||||
|| this.entryIdleTimeout != null
|
||||
|| this.entryTimeToLive != null
|
||||
|| this.regionIdleTimeout != null
|
||||
|| this.regionTimeToLive != null
|
||||
|| Optional.ofNullable(getAttributes())
|
||||
.map(RegionAttributes::getStatisticsEnabled)
|
||||
.orElse(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a {@link Class type} constraint on this {@link Region client Region's} values.
|
||||
*
|
||||
* @param valueConstraint {@link Class type} of this {@link Region client Region's} values.
|
||||
* @see java.lang.Class
|
||||
*/
|
||||
public void setValueConstraint(Class<V> valueConstraint) {
|
||||
this.valueConstraint = valueConstraint;
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.client;
|
||||
|
||||
import org.apache.geode.cache.client.ClientRegionShortcut;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
/**
|
||||
* The ClientRegionShortcutConverter class is a Spring Converter implementation converting String value Client Region
|
||||
* Shortcut representations into actual GemFire ClientRegionShortcut enumerated values.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.core.convert.converter.Converter
|
||||
* @see org.apache.geode.cache.client.ClientRegionShortcut
|
||||
* @since 1.3.4
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class ClientRegionShortcutConverter implements Converter<String, ClientRegionShortcut> {
|
||||
|
||||
/**
|
||||
* Converts the String value to upper case, trimming all whitespace. This method guards against null values
|
||||
* and returns the "null" String if value is null.
|
||||
*
|
||||
* @param value the String to convert to a trimmed, upper case value.
|
||||
* @return a trimmed, upper case value of the specified String, or "null" if the String value reference is null.
|
||||
* @see java.lang.String#toUpperCase()
|
||||
* @see java.lang.String#trim()
|
||||
* @see java.lang.String#valueOf(Object)
|
||||
*/
|
||||
protected static String toUpperCase(final String value) {
|
||||
return (value != null ? value.toUpperCase().trim() : String.valueOf(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the source String representation of a Client Region Shortcut into a ClientRegionShortcut enumerated
|
||||
* value.
|
||||
*
|
||||
* @param source the String representation of the Client Region Shortcut to convert.
|
||||
* @return a ClientRegionShortcut enumerated value for the String representation.
|
||||
* @throws IllegalArgumentException if the String source is not a valid ClientRegionShortcut enumerated value.
|
||||
* @see org.apache.geode.cache.client.ClientRegionShortcut#valueOf(String)
|
||||
*/
|
||||
@Override
|
||||
public ClientRegionShortcut convert(final String source) {
|
||||
return ClientRegionShortcut.valueOf(toUpperCase(source));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.client;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.geode.cache.DataPolicy;
|
||||
import org.apache.geode.cache.client.ClientRegionShortcut;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Spring {@link Converter} to convert a {@link ClientRegionShortcut} into a {@link DataPolicy}.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.apache.geode.cache.DataPolicy
|
||||
* @see org.apache.geode.cache.client.ClientRegionShortcut
|
||||
* @see org.springframework.core.convert.converter.Converter
|
||||
* @see org.springframework.data.gemfire.client.ClientRegionShortcutWrapper
|
||||
* @since 2.0.2
|
||||
*/
|
||||
public class ClientRegionShortcutToDataPolicyConverter implements Converter<ClientRegionShortcut, DataPolicy> {
|
||||
|
||||
public static final ClientRegionShortcutToDataPolicyConverter INSTANCE =
|
||||
new ClientRegionShortcutToDataPolicyConverter();
|
||||
|
||||
/**
|
||||
* Converts the given {@link ClientRegionShortcut} into a corresponding {@link DataPolicy}.
|
||||
*
|
||||
* @param regionShortcut {@link ClientRegionShortcut} to convert.
|
||||
* @return a corresponding {@link DataPolicy} for the given {@link ClientRegionShortcut}.
|
||||
* @see org.apache.geode.cache.client.ClientRegionShortcut
|
||||
* @see org.apache.geode.cache.DataPolicy
|
||||
*/
|
||||
@Nullable @Override
|
||||
public DataPolicy convert(ClientRegionShortcut clientRegionShortcut) {
|
||||
|
||||
return Optional.ofNullable(ClientRegionShortcutWrapper.valueOf(clientRegionShortcut))
|
||||
.map(ClientRegionShortcutWrapper::getDataPolicy)
|
||||
.orElse(DataPolicy.DEFAULT);
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.client;
|
||||
|
||||
import org.apache.geode.cache.DataPolicy;
|
||||
import org.apache.geode.cache.client.ClientRegionShortcut;
|
||||
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* The ClientRegionShortcutWrapper enum is a Java enumerated type that wraps GemFire's ClientRegionShortcuts
|
||||
* with Spring Data GemFire ClientRegionShortcutWrapper enumerated values.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.apache.geode.cache.client.ClientRegionShortcut
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public enum ClientRegionShortcutWrapper {
|
||||
|
||||
CACHING_PROXY(ClientRegionShortcut.CACHING_PROXY, DataPolicy.NORMAL),
|
||||
CACHING_PROXY_HEAP_LRU(ClientRegionShortcut.CACHING_PROXY_HEAP_LRU, DataPolicy.NORMAL),
|
||||
CACHING_PROXY_OVERFLOW(ClientRegionShortcut.CACHING_PROXY_OVERFLOW, DataPolicy.NORMAL),
|
||||
LOCAL(ClientRegionShortcut.LOCAL, DataPolicy.NORMAL),
|
||||
LOCAL_HEAP_LRU(ClientRegionShortcut.LOCAL_HEAP_LRU, DataPolicy.NORMAL),
|
||||
LOCAL_OVERFLOW(ClientRegionShortcut.LOCAL_OVERFLOW, DataPolicy.NORMAL),
|
||||
LOCAL_PERSISTENT(ClientRegionShortcut.LOCAL_PERSISTENT, DataPolicy.PERSISTENT_REPLICATE),
|
||||
LOCAL_PERSISTENT_OVERFLOW(ClientRegionShortcut.LOCAL_PERSISTENT_OVERFLOW, DataPolicy.PERSISTENT_REPLICATE),
|
||||
PROXY(ClientRegionShortcut.PROXY, DataPolicy.EMPTY),
|
||||
UNSPECIFIED(null, null);
|
||||
|
||||
private final ClientRegionShortcut clientRegionShortcut;
|
||||
|
||||
private final DataPolicy dataPolicy;
|
||||
|
||||
public static ClientRegionShortcutWrapper valueOf(ClientRegionShortcut clientRegionShortcut) {
|
||||
|
||||
for (ClientRegionShortcutWrapper wrapper : values()) {
|
||||
if (ObjectUtils.nullSafeEquals(wrapper.getClientRegionShortcut(), clientRegionShortcut)) {
|
||||
return wrapper;
|
||||
}
|
||||
}
|
||||
|
||||
return ClientRegionShortcutWrapper.UNSPECIFIED;
|
||||
}
|
||||
|
||||
ClientRegionShortcutWrapper(ClientRegionShortcut clientRegionShortcut, DataPolicy dataPolicy) {
|
||||
this.clientRegionShortcut = clientRegionShortcut;
|
||||
this.dataPolicy = dataPolicy;
|
||||
}
|
||||
|
||||
public ClientRegionShortcut getClientRegionShortcut() {
|
||||
return this.clientRegionShortcut;
|
||||
}
|
||||
|
||||
public DataPolicy getDataPolicy() {
|
||||
return this.dataPolicy;
|
||||
}
|
||||
|
||||
public boolean isCaching() {
|
||||
return name().contains("CACHING");
|
||||
}
|
||||
|
||||
public boolean isHeapLru() {
|
||||
return name().contains("HEAP_LRU");
|
||||
}
|
||||
|
||||
public boolean isLocal() {
|
||||
return name().contains("LOCAL");
|
||||
}
|
||||
|
||||
public boolean isOverflow() {
|
||||
return name().contains("OVERFLOW");
|
||||
}
|
||||
|
||||
public boolean isPersistent() {
|
||||
return name().contains("PERSISTENT");
|
||||
}
|
||||
|
||||
public boolean isPersistentOverflow() {
|
||||
return name().contains("PERSISTENT_OVERFLOW");
|
||||
}
|
||||
|
||||
public boolean isProxy() {
|
||||
return name().contains("PROXY");
|
||||
}
|
||||
}
|
||||
@@ -1,271 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.gemfire.client;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.geode.cache.Region;
|
||||
import org.apache.geode.cache.client.ClientCache;
|
||||
import org.apache.geode.cache.client.ClientRegionFactory;
|
||||
import org.apache.geode.cache.client.ClientRegionShortcut;
|
||||
import org.apache.geode.cache.execute.Function;
|
||||
import org.apache.geode.management.internal.cli.domain.RegionInformation;
|
||||
import org.apache.geode.management.internal.cli.functions.GetRegionsFunction;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.TypeMismatchException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.data.gemfire.client.function.ListRegionsOnServerFunction;
|
||||
import org.springframework.data.gemfire.function.execution.GemfireOnServersFunctionTemplate;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* A Spring {@link BeanFactoryPostProcessor} used to register a Client Region beans for each Region accessible to
|
||||
* an Apache Geode or Pivotal GemFire DataSource. If the Region is already defined, the bean definition
|
||||
* will not be overridden.
|
||||
*
|
||||
* @author David Turanski
|
||||
* @author John Blum
|
||||
* @see org.apache.geode.cache.Region
|
||||
* @see org.apache.geode.cache.client.ClientCache
|
||||
* @see org.apache.geode.cache.client.ClientRegionFactory
|
||||
* @see org.apache.geode.cache.client.ClientRegionShortcut
|
||||
* @see org.apache.geode.cache.execute.Function
|
||||
* @see org.apache.geode.management.internal.cli.functions.GetRegionsFunction
|
||||
* @see org.springframework.beans.factory.config.BeanFactoryPostProcessor
|
||||
* @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory
|
||||
* @see org.springframework.data.gemfire.client.function.ListRegionsOnServerFunction
|
||||
* @see org.springframework.data.gemfire.function.execution.GemfireOnServersFunctionTemplate
|
||||
* @see ListRegionsOnServerFunction
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public class GemfireDataSourcePostProcessor implements BeanFactoryAware, BeanPostProcessor {
|
||||
|
||||
private static final ClientRegionShortcut DEFAULT_CLIENT_REGION_SHORTCUT = ClientRegionShortcut.PROXY;
|
||||
|
||||
private ClientRegionShortcut clientRegionShortcut;
|
||||
|
||||
private ConfigurableBeanFactory beanFactory;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
/**
|
||||
* Set a reference to the {@link BeanFactory}.
|
||||
*
|
||||
* @param beanFactory reference to the {@link BeanFactory}.
|
||||
* @throws BeansException if the {@link BeanFactory} is not a {@link ConfigurableBeanFactory}.
|
||||
* @see org.springframework.beans.factory.config.ConfigurableBeanFactory
|
||||
* @see org.springframework.beans.factory.BeanFactory
|
||||
*/
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
|
||||
if (beanFactory instanceof ConfigurableBeanFactory) {
|
||||
this.beanFactory = (ConfigurableBeanFactory) beanFactory;
|
||||
}
|
||||
else {
|
||||
throw new TypeMismatchException(beanFactory, ConfigurableBeanFactory.class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the configured {@link ConfigurableBeanFactory}.
|
||||
*
|
||||
* @return a reference to the configured {@link ConfigurableBeanFactory}.
|
||||
* @see org.springframework.beans.factory.config.ConfigurableBeanFactory
|
||||
*/
|
||||
public Optional<ConfigurableBeanFactory> getBeanFactory() {
|
||||
return Optional.ofNullable(this.beanFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the data policy used to configure the client {@link Region}.
|
||||
*
|
||||
* @param clientRegionShortcut {@link ClientRegionShortcut} used to define the data policy
|
||||
* used by the client {@link Region}.
|
||||
* @see org.apache.geode.cache.client.ClientRegionShortcut
|
||||
*/
|
||||
public void setClientRegionShortcut(ClientRegionShortcut clientRegionShortcut) {
|
||||
this.clientRegionShortcut = clientRegionShortcut;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data policy used to configure the client {@link Region}.
|
||||
*
|
||||
* @return the configured {@link ClientRegionShortcut} used to define the data policy
|
||||
* used by the client {@link Region}.
|
||||
* @see org.apache.geode.cache.client.ClientRegionShortcut
|
||||
* @see java.util.Optional
|
||||
*/
|
||||
public Optional<ClientRegionShortcut> getClientRegionShortcut() {
|
||||
return Optional.ofNullable(this.clientRegionShortcut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the {@link ClientRegionShortcut} used to configure and create client {@link Region Regions}.
|
||||
*
|
||||
* @return the resolved {@link ClientRegionShortcut}.
|
||||
* @see org.apache.geode.cache.client.ClientRegionShortcut
|
||||
* @see #getClientRegionShortcut()
|
||||
*/
|
||||
protected ClientRegionShortcut resolveClientRegionShortcut() {
|
||||
return getClientRegionShortcut().orElse(DEFAULT_CLIENT_REGION_SHORTCUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the configured {@link Logger} used to log messages.
|
||||
*
|
||||
* @return a reference to the configured {@link Logger}.
|
||||
* @see org.slf4j.Logger
|
||||
*/
|
||||
protected Logger getLogger() {
|
||||
return this.logger;
|
||||
}
|
||||
|
||||
@Nullable @Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
|
||||
if (bean instanceof ClientCache) {
|
||||
|
||||
ClientCache clientCache = (ClientCache) bean;
|
||||
|
||||
getBeanFactory().ifPresent(it -> createClientProxyRegions(it, clientCache, regionNames(clientCache)));
|
||||
}
|
||||
|
||||
return bean;
|
||||
}
|
||||
|
||||
|
||||
// TODO: remove this logic and delegate to o.s.d.g.config.remote.GemfireAdminOperations
|
||||
Iterable<String> regionNames(ClientCache clientCache) {
|
||||
|
||||
try {
|
||||
return execute(clientCache, new ListRegionsOnServerFunction());
|
||||
}
|
||||
catch (Exception ignore) {
|
||||
|
||||
try {
|
||||
|
||||
Object results = execute(clientCache, new GetRegionsFunction());
|
||||
|
||||
List<String> regionNames = Collections.emptyList();
|
||||
|
||||
if (containsRegionInformation(results)) {
|
||||
|
||||
Object[] resultsArray = (Object[]) results;
|
||||
|
||||
regionNames = new ArrayList<>(resultsArray.length);
|
||||
|
||||
for (Object result : resultsArray) {
|
||||
regionNames.add(((RegionInformation) result).getName());
|
||||
}
|
||||
}
|
||||
|
||||
return regionNames;
|
||||
}
|
||||
catch (Exception cause) {
|
||||
logDebug("Failed to determine the Regions available on the Server: %n%s", cause);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<T> T execute(ClientCache clientCache, Function gemfireFunction, Object... arguments) {
|
||||
return new GemfireOnServersFunctionTemplate(clientCache).executeAndExtract(gemfireFunction, arguments);
|
||||
}
|
||||
|
||||
boolean containsRegionInformation(Object results) {
|
||||
|
||||
return results instanceof Object[] && ((Object[]) results).length > 0
|
||||
&& ((Object[]) results)[0] instanceof RegionInformation;
|
||||
}
|
||||
|
||||
void createClientProxyRegions(ConfigurableBeanFactory beanFactory, ClientCache clientCache,
|
||||
Iterable<String> regionNames) {
|
||||
|
||||
if (regionNames.iterator().hasNext()) {
|
||||
|
||||
ClientRegionShortcut resolvedClientRegionShortcut = resolveClientRegionShortcut();
|
||||
|
||||
ClientRegionFactory<?, ?> clientRegionFactory =
|
||||
clientCache.createClientRegionFactory(resolvedClientRegionShortcut);
|
||||
|
||||
for (String regionName : regionNames) {
|
||||
|
||||
if (beanFactory.containsBean(regionName)) {
|
||||
|
||||
Object bean = beanFactory.getBean(regionName);
|
||||
|
||||
logWarn("Cannot create a client {} Region bean named {}; A bean with name {} having type {} already exists",
|
||||
resolvedClientRegionShortcut.name(), regionName, regionName, ObjectUtils.nullSafeClassName(bean));
|
||||
}
|
||||
else {
|
||||
logInfo("Creating Region bean with name {}...", regionName);
|
||||
beanFactory.registerSingleton(regionName, clientRegionFactory.create(regionName));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void logDebug(String message, Object... arguments) {
|
||||
|
||||
Logger logger = getLogger();
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format(message, arguments));
|
||||
}
|
||||
}
|
||||
|
||||
void logInfo(String message, Object... arguments) {
|
||||
|
||||
Logger logger = getLogger();
|
||||
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info(message, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
void logWarn(String message, Object... arguments) {
|
||||
|
||||
Logger logger = getLogger();
|
||||
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn(message, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
public GemfireDataSourcePostProcessor using(ClientRegionShortcut clientRegionShortcut) {
|
||||
|
||||
setClientRegionShortcut(clientRegionShortcut);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public GemfireDataSourcePostProcessor using(BeanFactory beanFactory) {
|
||||
|
||||
setBeanFactory(beanFactory);
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -1,449 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.client;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
import org.apache.geode.cache.InterestResultPolicy;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.Constants;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* The Interest class holds details for registering a client interest.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author John Blum
|
||||
* @see java.util.regex.Pattern
|
||||
* @see org.apache.geode.cache.InterestResultPolicy
|
||||
* @see org.springframework.beans.factory.InitializingBean
|
||||
* @see org.springframework.core.Constants
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class Interest<K> implements InitializingBean {
|
||||
|
||||
public static final String ALL_KEYS = "ALL_KEYS";
|
||||
|
||||
protected static final boolean DEFAULT_DURABLE = false;
|
||||
protected static final boolean DEFAULT_RECEIVE_VALUES = true;
|
||||
|
||||
private static final Constants constants = new Constants(InterestResultPolicy.class);
|
||||
|
||||
protected final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private boolean durable = false;
|
||||
private boolean receiveValues = true;
|
||||
|
||||
private InterestResultPolicy policy = InterestResultPolicy.DEFAULT;
|
||||
|
||||
private K key;
|
||||
|
||||
private Type type;
|
||||
|
||||
/**
|
||||
* Factory method to construct a new instance of {@link Interest} initialized with the given key.
|
||||
*
|
||||
* @param <K> {@link Class} type of the key.
|
||||
* @param key key of interest.
|
||||
* @return a new instance of {@link Interest} initialized with the given key.
|
||||
* @see #Interest(Object)
|
||||
*/
|
||||
public static <K> Interest newInterest(K key) {
|
||||
return new Interest<>(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance of non-durable {@link Interest} initialized with the given key to register interest in,
|
||||
* using the {@link InterestResultPolicy#DEFAULT} to initialize the client cache and receiving values by default.
|
||||
*
|
||||
* @param key key(s) of interest.
|
||||
* @see #Interest(Object, InterestResultPolicy, boolean, boolean)
|
||||
*/
|
||||
public Interest(K key) {
|
||||
this(key, InterestResultPolicy.DEFAULT, DEFAULT_DURABLE, DEFAULT_RECEIVE_VALUES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance of non-durable {@link Interest} initialized with the given key to register interest in,
|
||||
* the given {@link InterestResultPolicy} used to initialize the client cache, receiving values by default.
|
||||
*
|
||||
* @param key key(s) of interest.
|
||||
* @param policy initial {@link InterestResultPolicy} used to initialize the client cache.
|
||||
* @see #Interest(Object, InterestResultPolicy, boolean, boolean)
|
||||
*/
|
||||
public Interest(K key, InterestResultPolicy policy) {
|
||||
this(key, policy, DEFAULT_DURABLE, DEFAULT_RECEIVE_VALUES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance of {@link Interest} initialized with the given key to register interest in,
|
||||
* the given {@link InterestResultPolicy} used to initialize the client cache, the given boolean value
|
||||
* to indicate whether interest registration should be durable, receiving values by default.
|
||||
*
|
||||
* @param key key(s) of interest.
|
||||
* @param policy initial {@link InterestResultPolicy} used to initialize the client cache.
|
||||
* @param durable boolean value to indicate whether the interest registration should be durable.
|
||||
* @see #Interest(Object, InterestResultPolicy, boolean, boolean)
|
||||
*/
|
||||
public Interest(K key, InterestResultPolicy policy, boolean durable) {
|
||||
this(key, policy, durable, DEFAULT_RECEIVE_VALUES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance of {@link Interest} initialized with the given key to register interest in,
|
||||
* the given {@link InterestResultPolicy} used to initialize the client cache and the given boolean values
|
||||
* indicating whether interest registration should be durable and whether to receive values during notifications.
|
||||
*
|
||||
* @param key key(s) of interest.
|
||||
* @param policy initial {@link InterestResultPolicy} used to initialize the client cache.
|
||||
* @param durable boolean value to indicate whether the interest registration should be durable.
|
||||
* @param receiveValues boolean value to indicate whether to receive value in notifications.
|
||||
* @see #Interest(Object, InterestResultPolicy, boolean, boolean)
|
||||
* @see #afterPropertiesSet()
|
||||
*/
|
||||
public Interest(K key, InterestResultPolicy policy, boolean durable, boolean receiveValues) {
|
||||
|
||||
this.key = key;
|
||||
this.policy = policy;
|
||||
this.durable = durable;
|
||||
this.receiveValues = receiveValues;
|
||||
|
||||
afterPropertiesSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public void afterPropertiesSet() {
|
||||
|
||||
Assert.notNull(this.key, "Key is required");
|
||||
|
||||
setType(resolveType(getType()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to resolve the {@link Interest.Type} based on the configured {@link #getKey()}.
|
||||
*
|
||||
* @param type provided {@link Interest.Type} used if {@literal non-null}.
|
||||
* @return the resolved {@link Interest.Type}.
|
||||
* @see #isRegularExpression(Object)
|
||||
*/
|
||||
protected Type resolveType(Type type) {
|
||||
return (type != null ? type : (isRegularExpression(getKey()) ? Type.REGEX : Type.KEY));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given {@code key} is a Regular Expression (Regex).
|
||||
*
|
||||
* If the given {@code key} is {@literal "ALL_KEYS"}, a {@link List} or only contains letters, numbers and spaces,
|
||||
* then the {@code key} is not considered a Regular Expression by GemFire, and can be handled with normal
|
||||
* interest registration using {@link org.apache.geode.cache.Region#registerInterest(Object)}.
|
||||
*
|
||||
* @param key {@link Object} to evaluate.
|
||||
* @return a boolean value indicating whether the given {@link Object} {@code key} is a Regular Expression.
|
||||
* @see #isRegularExpression(String)
|
||||
*/
|
||||
protected boolean isRegularExpression(Object key) {
|
||||
return (!(ALL_KEYS.equals(key) || key instanceof List) && isRegularExpression(String.valueOf(key)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given {@code key} is a Regular Expression (Regex).
|
||||
*
|
||||
* If the given {@code value} contains at least 1 special character (e.g. *) and can be compiled
|
||||
* using {@link Pattern#compile(String)}, then the {@code key} is considered a Regular Expression
|
||||
* and interest will be registered using {@link org.apache.geode.cache.Region#registerInterestRegex(String)}.
|
||||
*
|
||||
* @param value {@link String} to evaluate.
|
||||
* @return a boolean value indicating whether the given {@link String} {@code value} is a Regular Expression.
|
||||
* @see #containsNonAlphaNumericWhitespace(String)
|
||||
* @see java.util.regex.Pattern#compile(String)
|
||||
*/
|
||||
@SuppressWarnings("all")
|
||||
protected boolean isRegularExpression(String value) {
|
||||
try {
|
||||
return (containsNonAlphaNumericWhitespace(value) && Pattern.compile(value) != null);
|
||||
}
|
||||
catch (PatternSyntaxException ignore) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given {@link String} value contains at least 1 special character.
|
||||
*
|
||||
* @param value {@link String} to evaluate.
|
||||
* @return a boolean value indicating whether the given {@link String} contains at least 1 special character,
|
||||
* a non-alphanumeric, non-whitespace character.
|
||||
* @see #isAlphaNumericWhitespace(char)
|
||||
* @see #isNotAlphaNumericWhitespace(char)
|
||||
*/
|
||||
protected boolean containsNonAlphaNumericWhitespace(String value) {
|
||||
for (char character : String.valueOf(value).toCharArray()) {
|
||||
if (isNotAlphaNumericWhitespace(character)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given {@code character} is a special character (non-alphanumeric, non-whitespace).
|
||||
*
|
||||
* @param character {@link Character} to evaluate.
|
||||
* @return a boolean value indicating whether the given {@code character} is a special character.
|
||||
* @see #isAlphaNumericWhitespace(char)
|
||||
*/
|
||||
protected boolean isNotAlphaNumericWhitespace(char character) {
|
||||
return !isAlphaNumericWhitespace(character);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given {@code character} is an alphanumeric or whitespace character.
|
||||
*
|
||||
* @param character {@link Character} to evaluate.
|
||||
* @return a boolean value indicating whether the given {@code character} is an alphanumeric
|
||||
* or whitespace character.
|
||||
* @see java.lang.Character#isDigit(char)
|
||||
* @see java.lang.Character#isLetter(char)
|
||||
* @see java.lang.Character#isWhitespace(char)
|
||||
*/
|
||||
protected boolean isAlphaNumericWhitespace(char character) {
|
||||
return (Character.isDigit(character) || Character.isLetter(character) || Character.isWhitespace(character));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the interest registration is durable and persists between cache client sessions.
|
||||
*
|
||||
* @return a boolean value indicating whether this interest registration is durable.
|
||||
*/
|
||||
public boolean isDurable() {
|
||||
return this.durable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether interest registration is durable and persists between cache client sessions.
|
||||
*
|
||||
* @param durable boolean value to indicate whether this interest registration is durable.
|
||||
*/
|
||||
public void setDurable(boolean durable) {
|
||||
this.durable = durable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key on which interest is registered.
|
||||
*
|
||||
* @return the key of interest.
|
||||
*/
|
||||
public K getKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the key on which interest is registered.
|
||||
*
|
||||
* @param key the key of interest.
|
||||
*/
|
||||
public void setKey(K key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link InterestResultPolicy} used when interest is registered and determines whether KEYS,
|
||||
* KEYS_VALUES or nothing (NONE) is initially fetched on initial registration.
|
||||
*
|
||||
* @return the policy
|
||||
*/
|
||||
public InterestResultPolicy getPolicy() {
|
||||
return this.policy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the initial {@link InterestResultPolicy} used when interest is first registered and determines whether KEYS,
|
||||
* KEYS_VALUE or nothing (NONE) is initially fetched.
|
||||
*
|
||||
* The argument is set as an {@link Object} to be able to accept both {@link InterestResultPolicy}
|
||||
* and {@link String Strings}, used in XML configuration meta-data.
|
||||
*
|
||||
* @param policy initial {@link InterestResultPolicy} to set.
|
||||
* @throws IllegalArgumentException if the given {@code policy} is not a valid type.
|
||||
* @see org.apache.geode.cache.InterestResultPolicy
|
||||
*/
|
||||
public void setPolicy(Object policy) {
|
||||
|
||||
if (policy instanceof InterestResultPolicy) {
|
||||
this.policy = (InterestResultPolicy) policy;
|
||||
}
|
||||
else if (policy instanceof String) {
|
||||
this.policy = (InterestResultPolicy) constants.asObject(String.valueOf(policy));
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(String.format("Unknown argument type [%s] for property policy", policy));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of values received by the listener.
|
||||
*
|
||||
* @return the receiveValues
|
||||
*/
|
||||
public boolean isReceiveValues() {
|
||||
return this.receiveValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches between the different entities received by the listener.
|
||||
*
|
||||
* @param receiveValues the receiveValues to set
|
||||
*/
|
||||
public void setReceiveValues(boolean receiveValues) {
|
||||
this.receiveValues = receiveValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of interest registration (e.g. based on KEY or Regex).
|
||||
*
|
||||
* @return a {@link Interest.Type} determining the type of interest.
|
||||
* @see Interest.Type
|
||||
*/
|
||||
public Type getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the type of interest registration (e.g. based on KEY or Regex).
|
||||
*
|
||||
* @param type {@link Interest.Type} qualifying the type of interest.
|
||||
* @see Interest.Type
|
||||
*/
|
||||
public void setType(Type type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether this {@link Interest} is a KEY interest registration.
|
||||
*
|
||||
* @return a boolean value indicating whether this is KEY interest.
|
||||
* @see Interest.Type#KEY
|
||||
* @see #getType()
|
||||
*/
|
||||
public boolean isKeyType() {
|
||||
return Type.KEY.equals(getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether this {@link Interest} is a REGEX interest registration.
|
||||
*
|
||||
* @return a boolean value indicating whether this is REGEX interest.
|
||||
* @see Interest.Type#REGEX
|
||||
* @see #getType()
|
||||
*/
|
||||
public boolean isRegexType() {
|
||||
return Type.REGEX.equals(getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("{ @type = %1$s, key = %2$s, durable = %3$s, policy = %4$s, receiveValues = %5$s, type = %6$s }",
|
||||
getClass().getName(), getKey(), isDurable(), getPolicy(), isReceiveValues(), getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder method to specify the type of interest registration.
|
||||
*
|
||||
* @param type {@link Interest.Type} of interest registration.
|
||||
* @return this {@link Interest}.
|
||||
* @see Interest.Type
|
||||
* @see #resolveType(Type)
|
||||
* @see #setType(Type)
|
||||
*/
|
||||
public Interest asType(Type type) {
|
||||
setType(resolveType(type));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder method to mark this {@link Interest} as durable.
|
||||
*
|
||||
* @return this {@link Interest}.
|
||||
* @see #setDurable(boolean)
|
||||
*/
|
||||
public Interest makeDurable() {
|
||||
setDurable(true);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder method to set whether the interest event notifications will receive values along with keys.
|
||||
*
|
||||
* @param receiveValues boolean to indicate that value should be sent along with keys
|
||||
* on interest event notifications.
|
||||
* @return this {@link Interest}.
|
||||
* @see #setReceiveValues(boolean)
|
||||
*/
|
||||
public Interest receivesValues(boolean receiveValues) {
|
||||
setReceiveValues(receiveValues);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder method to set the {@link InterestResultPolicy} used to initialize the cache.
|
||||
*
|
||||
* @param policy {@link InterestResultPolicy}.
|
||||
* @return this {@link Interest}.
|
||||
* @see org.apache.geode.cache.InterestResultPolicy
|
||||
* @see #setPolicy(Object)
|
||||
*/
|
||||
public Interest usingPolicy(InterestResultPolicy policy) {
|
||||
setPolicy(policy);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder method to express the key of interest.
|
||||
*
|
||||
* @param key key of interests.
|
||||
* @return this {@link Interest}.
|
||||
* @see #setKey(Object)
|
||||
* @see #getType()
|
||||
* @see #resolveType(Type)
|
||||
* @see #setType(Type)
|
||||
*/
|
||||
public Interest withKey(K key) {
|
||||
setKey(key);
|
||||
setType(resolveType(getType()));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type of interest registration.
|
||||
*/
|
||||
public enum Type {
|
||||
KEY, REGEX
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.client;
|
||||
|
||||
import org.apache.geode.cache.InterestResultPolicy;
|
||||
|
||||
import org.springframework.data.gemfire.support.AbstractPropertyEditorConverterSupport;
|
||||
|
||||
/**
|
||||
* The InterestResultPolicyConverter class is a Spring Converter and JavaBeans PropertyEditor capable of converting
|
||||
* a String into a GemFire InterestResultPolicyConverter.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.data.gemfire.support.AbstractPropertyEditorConverterSupport
|
||||
* @see org.apache.geode.cache.InterestResultPolicy
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public class InterestResultPolicyConverter extends AbstractPropertyEditorConverterSupport<InterestResultPolicy> {
|
||||
|
||||
/**
|
||||
* Converts the given String into an instance of GemFire InterestResultPolicy.
|
||||
*
|
||||
* @param source the String to convert into an InterestResultPolicy value.
|
||||
* @return a GemFire InterestResultPolicy value for the given String.
|
||||
* @throws java.lang.IllegalArgumentException if the String is not a valid GemFire InterestResultPolicy.
|
||||
* @see org.springframework.data.gemfire.client.InterestResultPolicyType#getInterestResultPolicy(InterestResultPolicyType)
|
||||
* @see org.springframework.data.gemfire.client.InterestResultPolicyType#valueOfIgnoreCase(String)
|
||||
* @see #assertConverted(String, Object, Class)
|
||||
* @see org.apache.geode.cache.InterestResultPolicy
|
||||
*/
|
||||
@Override
|
||||
public InterestResultPolicy convert(final String source) {
|
||||
return assertConverted(source, InterestResultPolicyType.getInterestResultPolicy(
|
||||
InterestResultPolicyType.valueOfIgnoreCase(source)), InterestResultPolicy.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.client;
|
||||
|
||||
import org.apache.geode.cache.InterestResultPolicy;
|
||||
|
||||
/**
|
||||
* The InterestResultPolicyType enum is an enumeration of all client Register Interests (result) policy values.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.apache.geode.cache.InterestResultPolicy
|
||||
* @since 1.6.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public enum InterestResultPolicyType {
|
||||
KEYS(InterestResultPolicy.KEYS),
|
||||
KEYS_VALUES(InterestResultPolicy.KEYS_VALUES),
|
||||
NONE(InterestResultPolicy.NONE);
|
||||
|
||||
public static final InterestResultPolicyType DEFAULT = InterestResultPolicyType.valueOf(
|
||||
InterestResultPolicy.DEFAULT);
|
||||
|
||||
private final InterestResultPolicy interestResultPolicy;
|
||||
|
||||
InterestResultPolicyType(final InterestResultPolicy interestResultPolicy) {
|
||||
this.interestResultPolicy = interestResultPolicy;
|
||||
}
|
||||
|
||||
public static InterestResultPolicy getInterestResultPolicy(final InterestResultPolicyType interestResultPolicyType) {
|
||||
return (interestResultPolicyType != null ? interestResultPolicyType.getInterestResultPolicy() : null);
|
||||
}
|
||||
|
||||
public static InterestResultPolicyType valueOf(final InterestResultPolicy interestResultPolicy) {
|
||||
for (InterestResultPolicyType interestResultPolicyType : values()) {
|
||||
if (interestResultPolicyType.getInterestResultPolicy().equals(interestResultPolicy)) {
|
||||
return interestResultPolicyType;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static InterestResultPolicyType valueOfIgnoreCase(final String name) {
|
||||
for (InterestResultPolicyType interestResultPolicyType : values()) {
|
||||
if (interestResultPolicyType.name().equalsIgnoreCase(name)) {
|
||||
return interestResultPolicyType;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public InterestResultPolicy getInterestResultPolicy() {
|
||||
return interestResultPolicy;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.client;
|
||||
|
||||
import org.apache.geode.cache.InterestResultPolicy;
|
||||
|
||||
/**
|
||||
* Cache Region interest based on individual keys.
|
||||
*
|
||||
* @author John Blum
|
||||
* @param <K> {@link Class} type of the key.
|
||||
* @see org.springframework.data.gemfire.client.Interest
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class KeyInterest<K> extends Interest<K> {
|
||||
|
||||
public KeyInterest(K key) {
|
||||
super(key);
|
||||
}
|
||||
|
||||
public KeyInterest(K key, InterestResultPolicy policy) {
|
||||
super(key, policy);
|
||||
}
|
||||
|
||||
public KeyInterest(K key, InterestResultPolicy policy, boolean durable) {
|
||||
super(key, policy, durable);
|
||||
}
|
||||
|
||||
public KeyInterest(K key, InterestResultPolicy policy, boolean durable, boolean receiveValues) {
|
||||
super(key, policy, durable, receiveValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public void setType(Type type) {
|
||||
logger.warn(String.format("Setting the Type [%1$s] of Interest on [%2$s] is ignored",
|
||||
type, getClass().getName()));
|
||||
}
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.client;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.geode.cache.client.Pool;
|
||||
import org.apache.geode.cache.query.QueryService;
|
||||
|
||||
/**
|
||||
* The {@link PoolAdapter} class is an abstract base class and default, no-op implementation of
|
||||
* the {@link Pool} interface that conveniently enables implementing classes to extend this adapter
|
||||
* and choose which {@link Pool} methods/operations are supported by this implementation.
|
||||
*
|
||||
* For instance, one possible implementation is Spring Data GemFire's {@link PoolFactoryBean}, which can act as
|
||||
* a {@link Pool} in a context where only the {@link Pool}'s "configuration" and meta-data are required,
|
||||
* but no actual connections or operating state information (e.g. pendingEventCount) is needed.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.data.gemfire.client.PoolFactoryBean
|
||||
* @see org.apache.geode.cache.client.Pool
|
||||
* @since 1.8.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class PoolAdapter implements Pool {
|
||||
|
||||
public static final String NOT_IMPLEMENTED = "Not Implemented";
|
||||
|
||||
public boolean isDestroyed() {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
public int getFreeConnectionTimeout() {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
public long getIdleTimeout() {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
public int getLoadConditioningInterval() {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
public List<InetSocketAddress> getLocators() {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
public int getMaxConnections() {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
public int getMinConnections() {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
public boolean getMultiuserAuthentication() {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
public List<InetSocketAddress> getOnlineLocators() {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
public int getPendingEventCount() {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
public long getPingInterval() {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
public boolean getPRSingleHopEnabled() {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
public QueryService getQueryService() {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
public int getReadTimeout() {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
public int getRetryAttempts() {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
public String getServerGroup() {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
public List<InetSocketAddress> getServers() {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
public int getSocketBufferSize() {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
public int getSocketConnectTimeout() {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
public int getStatisticInterval() {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
public int getSubscriptionAckInterval() {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
public boolean getSubscriptionEnabled() {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
public int getSubscriptionMessageTrackingTimeout() {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
public int getSubscriptionRedundancy() {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
public int getSubscriptionTimeoutMultiplier() {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
public boolean getThreadLocalConnections() {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
public void destroy(boolean keepAlive) {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
public void releaseThreadLocalConnection() {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
}
|
||||
@@ -1,783 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.gemfire.client;
|
||||
|
||||
import static java.util.stream.StreamSupport.stream;
|
||||
import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
|
||||
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeCollection;
|
||||
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeIterable;
|
||||
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalArgumentException;
|
||||
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.geode.cache.client.ClientCache;
|
||||
import org.apache.geode.cache.client.Pool;
|
||||
import org.apache.geode.cache.client.PoolFactory;
|
||||
import org.apache.geode.cache.client.PoolManager;
|
||||
import org.apache.geode.cache.query.QueryService;
|
||||
import org.apache.geode.distributed.DistributedSystem;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.data.gemfire.GemfireUtils;
|
||||
import org.springframework.data.gemfire.config.annotation.PoolConfigurer;
|
||||
import org.springframework.data.gemfire.support.AbstractFactoryBeanSupport;
|
||||
import org.springframework.data.gemfire.support.ConnectionEndpoint;
|
||||
import org.springframework.data.gemfire.support.ConnectionEndpointList;
|
||||
import org.springframework.data.gemfire.util.DistributedSystemUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Spring {@link FactoryBean} to construct, configure and initialize a {@link Pool}.
|
||||
*
|
||||
* If a new {@link Pool} is created, its lifecycle is bound to that of this declaring {@link FactoryBean}
|
||||
* and indirectly, the Spring container.
|
||||
*
|
||||
* If a {@link Pool} having the configured {@link String name} already exists, then the existing {@link Pool}
|
||||
* will be returned as is without any modifications and its lifecycle will be unaffected by this {@link FactoryBean}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author John Blum
|
||||
* @see java.net.InetSocketAddress
|
||||
* @see org.apache.geode.cache.client.ClientCache
|
||||
* @see org.apache.geode.cache.client.Pool
|
||||
* @see org.apache.geode.cache.client.PoolFactory
|
||||
* @see org.apache.geode.cache.client.PoolManager
|
||||
* @see org.apache.geode.distributed.DistributedSystem
|
||||
* @see org.springframework.beans.factory.DisposableBean
|
||||
* @see org.springframework.beans.factory.InitializingBean
|
||||
* @see org.springframework.data.gemfire.config.annotation.PoolConfigurer
|
||||
* @see org.springframework.data.gemfire.support.AbstractFactoryBeanSupport
|
||||
* @see org.springframework.data.gemfire.support.ConnectionEndpoint
|
||||
* @see org.springframework.data.gemfire.support.ConnectionEndpointList
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class PoolFactoryBean extends AbstractFactoryBeanSupport<Pool> implements DisposableBean, InitializingBean {
|
||||
|
||||
protected static final int DEFAULT_LOCATOR_PORT = DistributedSystemUtils.DEFAULT_LOCATOR_PORT;
|
||||
protected static final int DEFAULT_SERVER_PORT = DistributedSystemUtils.DEFAULT_CACHE_SERVER_PORT;
|
||||
|
||||
// Indicates whether the Pool has been created by this FactoryBean, or not
|
||||
volatile boolean springManagedPool = true;
|
||||
|
||||
// GemFire Pool Configuration Settings
|
||||
private boolean keepAlive = false;
|
||||
private boolean multiUserAuthentication = PoolFactory.DEFAULT_MULTIUSER_AUTHENTICATION;
|
||||
private boolean prSingleHopEnabled = PoolFactory.DEFAULT_PR_SINGLE_HOP_ENABLED;
|
||||
private boolean subscriptionEnabled = PoolFactory.DEFAULT_SUBSCRIPTION_ENABLED;
|
||||
private boolean threadLocalConnections = PoolFactory.DEFAULT_THREAD_LOCAL_CONNECTIONS;
|
||||
|
||||
private int freeConnectionTimeout = PoolFactory.DEFAULT_FREE_CONNECTION_TIMEOUT;
|
||||
private int loadConditioningInterval = PoolFactory.DEFAULT_LOAD_CONDITIONING_INTERVAL;
|
||||
private int maxConnections = PoolFactory.DEFAULT_MAX_CONNECTIONS;
|
||||
private int minConnections = PoolFactory.DEFAULT_MIN_CONNECTIONS;
|
||||
private int readTimeout = PoolFactory.DEFAULT_READ_TIMEOUT;
|
||||
private int retryAttempts = PoolFactory.DEFAULT_RETRY_ATTEMPTS;
|
||||
private int socketBufferSize = PoolFactory.DEFAULT_SOCKET_BUFFER_SIZE;
|
||||
private int socketConnectTimeout = PoolFactory.DEFAULT_SOCKET_CONNECT_TIMEOUT;
|
||||
private int statisticInterval = PoolFactory.DEFAULT_STATISTIC_INTERVAL;
|
||||
private int subscriptionAckInterval = PoolFactory.DEFAULT_SUBSCRIPTION_ACK_INTERVAL;
|
||||
private int subscriptionMessageTrackingTimeout = PoolFactory.DEFAULT_SUBSCRIPTION_MESSAGE_TRACKING_TIMEOUT;
|
||||
private int subscriptionRedundancy = PoolFactory.DEFAULT_SUBSCRIPTION_REDUNDANCY;
|
||||
private int subscriptionTimeoutMultiplier = PoolFactory.DEFAULT_SUBSCRIPTION_TIMEOUT_MULTIPLIER;
|
||||
|
||||
private long idleTimeout = PoolFactory.DEFAULT_IDLE_TIMEOUT;
|
||||
private long pingInterval = PoolFactory.DEFAULT_PING_INTERVAL;
|
||||
|
||||
private ConnectionEndpointList locators = new ConnectionEndpointList();
|
||||
private ConnectionEndpointList servers = new ConnectionEndpointList();
|
||||
|
||||
private List<PoolConfigurer> poolConfigurers = Collections.emptyList();
|
||||
|
||||
private volatile Pool pool;
|
||||
|
||||
private PoolConfigurer compositePoolConfigurer = (beanName, bean) ->
|
||||
nullSafeCollection(poolConfigurers).forEach(poolConfigurer -> poolConfigurer.configure(beanName, bean));
|
||||
|
||||
private PoolFactoryInitializer poolFactoryInitializer;
|
||||
|
||||
private String name;
|
||||
private String serverGroup = PoolFactory.DEFAULT_SERVER_GROUP;
|
||||
|
||||
/**
|
||||
* Prepares the construction, configuration and initialization of a new {@link Pool}.
|
||||
*
|
||||
* @throws Exception if {@link Pool} initialization fails.
|
||||
* @see org.apache.geode.cache.client.PoolManager
|
||||
* @see org.apache.geode.cache.client.PoolFactory
|
||||
* @see org.apache.geode.cache.client.Pool
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
init(Optional.ofNullable(find(resolvePoolName())));
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private void init(Optional<Pool> existingPool) {
|
||||
|
||||
if (existingPool.isPresent()) {
|
||||
|
||||
this.pool = existingPool.get();
|
||||
this.springManagedPool = false;
|
||||
|
||||
logDebug(() -> String.format("A Pool with name [%s] already exists; Using existing Pool",
|
||||
this.pool.getName()));
|
||||
|
||||
logDebug("PoolConfigurers will not be applied");
|
||||
}
|
||||
else {
|
||||
logDebug("Pool [%s] not found; Lazily creating new Pool...", getName());
|
||||
applyPoolConfigurers();
|
||||
}
|
||||
}
|
||||
|
||||
private String resolvePoolName() {
|
||||
|
||||
if (!StringUtils.hasText(getName())) {
|
||||
setName(Optional.ofNullable(getBeanName())
|
||||
.filter(StringUtils::hasText)
|
||||
.orElseThrow(() -> newIllegalArgumentException("Pool name is required")));
|
||||
}
|
||||
|
||||
return getName();
|
||||
}
|
||||
|
||||
private Pool find(String name) {
|
||||
return PoolManager.find(name);
|
||||
}
|
||||
|
||||
private void applyPoolConfigurers() {
|
||||
applyPoolConfigurers(getCompositePoolConfigurer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe operation to apply the given array of {@link PoolConfigurer PoolConfigurers}
|
||||
* to this {@link PoolFactoryBean}.
|
||||
*
|
||||
* @param poolConfigurers array of {@link PoolConfigurer PoolConfigurers} applied to this {@link PoolFactoryBean}.
|
||||
* @see org.springframework.data.gemfire.config.annotation.PoolConfigurer
|
||||
* @see #applyPoolConfigurers(Iterable)
|
||||
*/
|
||||
protected void applyPoolConfigurers(PoolConfigurer... poolConfigurers) {
|
||||
applyPoolConfigurers(Arrays.asList(nullSafeArray(poolConfigurers, PoolConfigurer.class)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe operation to apply the given {@link Iterable} of {@link PoolConfigurer PoolConfigurers}
|
||||
* to this {@link PoolFactoryBean}.
|
||||
*
|
||||
* @param poolConfigurers {@link Iterable} of {@link PoolConfigurer PoolConfigurers}
|
||||
* applied to this {@link PoolFactoryBean}.
|
||||
* @see org.springframework.data.gemfire.config.annotation.PoolConfigurer
|
||||
*/
|
||||
protected void applyPoolConfigurers(Iterable<PoolConfigurer> poolConfigurers) {
|
||||
stream(nullSafeIterable(poolConfigurers).spliterator(), false)
|
||||
.forEach(poolConfigurer -> poolConfigurer.configure(getName(), this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases all system resources and destroys the {@link Pool} when created by this {@link PoolFactoryBean}.
|
||||
*
|
||||
* @throws Exception if the {@link Pool} destruction caused an error.
|
||||
* @see org.springframework.beans.factory.DisposableBean#destroy()
|
||||
*/
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
|
||||
Optional.ofNullable(this.pool)
|
||||
.filter(pool -> this.springManagedPool)
|
||||
.filter(pool -> !pool.isDestroyed())
|
||||
.ifPresent(pool -> {
|
||||
pool.releaseThreadLocalConnection();
|
||||
pool.destroy(this.keepAlive);
|
||||
setPool(null);
|
||||
logDebug("Destroyed Pool [%s]", pool.getName());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object reference to the {@link Pool} created by this {@link PoolFactoryBean}.
|
||||
*
|
||||
* @return an object reference to the {@link Pool} created by this {@link PoolFactoryBean}.
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObject()
|
||||
* @see org.apache.geode.cache.client.Pool
|
||||
*/
|
||||
@Override
|
||||
public Pool getObject() throws Exception {
|
||||
|
||||
return Optional.ofNullable(this.pool).orElseGet(() -> {
|
||||
|
||||
eagerlyInitializeClientCache();
|
||||
|
||||
Pool namedPool = find(getName());
|
||||
|
||||
this.pool = namedPool != null ? namedPool
|
||||
: postProcess(create(postProcess(configure(initialize(createPoolFactory()))), getName()));
|
||||
|
||||
return this.pool;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to eagerly initialize the {@link ClientCache} if not already present so that a single
|
||||
* {@link DistributedSystem} will exist, which is required to create a {@link Pool} instance.
|
||||
*
|
||||
* @see org.springframework.beans.factory.BeanFactory#getBean(Class)
|
||||
* @see org.apache.geode.cache.client.ClientCache
|
||||
* @see org.apache.geode.distributed.DistributedSystem
|
||||
* @see #isClientCachePresent()
|
||||
*/
|
||||
private void eagerlyInitializeClientCache() {
|
||||
|
||||
if (!isClientCachePresent()) {
|
||||
getBeanFactory().getBean(ClientCache.class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the {@link ClientCache} exists yet or not.
|
||||
*
|
||||
* @return a boolean value indicating whether the single {@link ClientCache} instance
|
||||
* has been created yet.
|
||||
* @see org.springframework.data.gemfire.GemfireUtils#getClientCache()
|
||||
* @see org.apache.geode.distributed.DistributedSystem
|
||||
* @see org.apache.geode.cache.client.ClientCache
|
||||
*/
|
||||
boolean isClientCachePresent() {
|
||||
|
||||
return Optional.ofNullable(GemfireUtils.getClientCache())
|
||||
.filter(clientCache -> !clientCache.isClosed())
|
||||
.map(ClientCache::getDistributedSystem)
|
||||
.filter(GemfireUtils::isConnected)
|
||||
.isPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of the {@link PoolFactory} interface to construct, configure and initialize a {@link Pool}.
|
||||
*
|
||||
* @return a {@link PoolFactory} implementation to create a {@link Pool}.
|
||||
* @see org.apache.geode.cache.client.PoolManager#createFactory()
|
||||
* @see org.apache.geode.cache.client.PoolFactory
|
||||
*/
|
||||
protected PoolFactory createPoolFactory() {
|
||||
return PoolManager.createFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the given {@link PoolFactory} from this {@link PoolFactoryBean}.
|
||||
*
|
||||
* @param poolFactory {@link PoolFactory} to configure.
|
||||
* @return the given {@link PoolFactory}.
|
||||
* @see org.apache.geode.cache.client.PoolFactory
|
||||
*/
|
||||
protected PoolFactory configure(PoolFactory poolFactory) {
|
||||
|
||||
Optional.ofNullable(poolFactory).ifPresent(it -> {
|
||||
|
||||
it.setFreeConnectionTimeout(this.freeConnectionTimeout);
|
||||
it.setIdleTimeout(this.idleTimeout);
|
||||
it.setLoadConditioningInterval(this.loadConditioningInterval);
|
||||
it.setMaxConnections(this.maxConnections);
|
||||
it.setMinConnections(this.minConnections);
|
||||
it.setMultiuserAuthentication(this.multiUserAuthentication);
|
||||
it.setPingInterval(this.pingInterval);
|
||||
it.setPRSingleHopEnabled(this.prSingleHopEnabled);
|
||||
it.setReadTimeout(this.readTimeout);
|
||||
it.setRetryAttempts(this.retryAttempts);
|
||||
it.setServerGroup(this.serverGroup);
|
||||
it.setSocketBufferSize(this.socketBufferSize);
|
||||
it.setSocketConnectTimeout(this.socketConnectTimeout);
|
||||
it.setStatisticInterval(this.statisticInterval);
|
||||
it.setSubscriptionAckInterval(this.subscriptionAckInterval);
|
||||
it.setSubscriptionEnabled(this.subscriptionEnabled);
|
||||
it.setSubscriptionMessageTrackingTimeout(this.subscriptionMessageTrackingTimeout);
|
||||
it.setSubscriptionRedundancy(this.subscriptionRedundancy);
|
||||
it.setSubscriptionTimeoutMultiplier(this.subscriptionTimeoutMultiplier);
|
||||
it.setThreadLocalConnections(this.threadLocalConnections);
|
||||
|
||||
nullSafeCollection(this.locators).forEach(locator ->
|
||||
it.addLocator(locator.getHost(), locator.getPort()));
|
||||
|
||||
nullSafeCollection(this.servers).forEach(server ->
|
||||
it.addServer(server.getHost(), server.getPort()));
|
||||
});
|
||||
|
||||
return poolFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the given {@link PoolFactory} with any configured {@link PoolFactoryInitializer}.
|
||||
*
|
||||
* @param poolFactory {@link PoolFactory} to initialize.
|
||||
* @return the initialized {@link PoolFactory}.
|
||||
* @see org.apache.geode.cache.client.PoolFactory
|
||||
*/
|
||||
protected PoolFactory initialize(PoolFactory poolFactory) {
|
||||
|
||||
return Optional.ofNullable(this.poolFactoryInitializer)
|
||||
.map(initializer -> initializer.initialize(poolFactory))
|
||||
.orElse(poolFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Post processes the fully configured {@link PoolFactory}.
|
||||
*
|
||||
* @param poolFactory {@link PoolFactory} to post process.
|
||||
* @return the post processed {@link PoolFactory}.
|
||||
* @see org.apache.geode.cache.client.PoolFactory
|
||||
*/
|
||||
protected PoolFactory postProcess(PoolFactory poolFactory) {
|
||||
return poolFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Pool} with the given {@link String name} using the provided {@link PoolFactory}.
|
||||
*
|
||||
* @param poolFactory {@link PoolFactory} used to create the {@link Pool}.
|
||||
* @param poolName {@link String name} of the new {@link Pool}.
|
||||
* @return a new instance of {@link Pool} with the given {@link String name}.
|
||||
* @see org.apache.geode.cache.client.PoolFactory#create(String)
|
||||
* @see org.apache.geode.cache.client.Pool
|
||||
*/
|
||||
protected Pool create(PoolFactory poolFactory, String poolName) {
|
||||
return poolFactory.create(poolName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Post processes the {@link Pool} created by this {@link PoolFactoryBean}.
|
||||
*
|
||||
* @param pool {@link Pool} to post process.
|
||||
* @return the post processed {@link Pool}.
|
||||
* @see org.apache.geode.cache.client.Pool
|
||||
*/
|
||||
protected Pool postProcess(Pool pool) {
|
||||
return pool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Class type} of {@link Pool} produced by this {@link PoolFactoryBean}.
|
||||
*
|
||||
* @return the {@link Class type} of {@link Pool} produced by this {@link PoolFactoryBean}.
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Class<?> getObjectType() {
|
||||
return this.pool != null ? this.pool.getClass() : Pool.class;
|
||||
}
|
||||
|
||||
public void addLocators(ConnectionEndpoint... locators) {
|
||||
this.locators.add(locators);
|
||||
}
|
||||
|
||||
public void addLocators(Iterable<ConnectionEndpoint> locators) {
|
||||
this.locators.add(locators);
|
||||
}
|
||||
|
||||
public void addServers(ConnectionEndpoint... servers) {
|
||||
this.servers.add(servers);
|
||||
}
|
||||
|
||||
public void addServers(Iterable<ConnectionEndpoint> servers) {
|
||||
this.servers.add(servers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the Composite {@link PoolConfigurer} used to apply additional configuration
|
||||
* to this {@link PoolFactoryBean} on Spring container initialization.
|
||||
*
|
||||
* @return the Composite {@link PoolConfigurer}.
|
||||
* @see org.springframework.data.gemfire.config.annotation.PoolConfigurer
|
||||
*/
|
||||
protected PoolConfigurer getCompositePoolConfigurer() {
|
||||
return this.compositePoolConfigurer;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
protected String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setPool(Pool pool) {
|
||||
this.pool = pool;
|
||||
}
|
||||
|
||||
public Pool getPool() {
|
||||
|
||||
return Optional.ofNullable(this.pool).orElseGet(() -> new PoolAdapter() {
|
||||
|
||||
@Override
|
||||
public boolean isDestroyed() {
|
||||
|
||||
Pool pool = PoolFactoryBean.this.pool;
|
||||
|
||||
return pool != null && pool.isDestroyed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFreeConnectionTimeout() {
|
||||
return PoolFactoryBean.this.freeConnectionTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getIdleTimeout() {
|
||||
return PoolFactoryBean.this.idleTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLoadConditioningInterval() {
|
||||
return PoolFactoryBean.this.loadConditioningInterval;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InetSocketAddress> getLocators() {
|
||||
return PoolFactoryBean.this.locators.toInetSocketAddresses();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InetSocketAddress> getOnlineLocators() {
|
||||
|
||||
return Optional.ofNullable(PoolFactoryBean.this.pool)
|
||||
.map(Pool::getOnlineLocators)
|
||||
.orElseThrow(() -> newIllegalStateException("Pool [%s] has not been initialized", getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxConnections() {
|
||||
return PoolFactoryBean.this.maxConnections;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinConnections() {
|
||||
return PoolFactoryBean.this.minConnections;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getMultiuserAuthentication() {
|
||||
return PoolFactoryBean.this.multiUserAuthentication;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
|
||||
return Optional.ofNullable(PoolFactoryBean.this.getName())
|
||||
.filter(StringUtils::hasText)
|
||||
.orElseGet(PoolFactoryBean.this::getBeanName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPendingEventCount() {
|
||||
|
||||
return Optional.ofNullable(PoolFactoryBean.this.pool)
|
||||
.map(Pool::getPendingEventCount)
|
||||
.orElseThrow(() -> newIllegalStateException("Pool [%s] has not been initialized", getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getPingInterval() {
|
||||
return PoolFactoryBean.this.pingInterval;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getPRSingleHopEnabled() {
|
||||
return PoolFactoryBean.this.prSingleHopEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryService getQueryService() {
|
||||
|
||||
return Optional.ofNullable(PoolFactoryBean.this.pool)
|
||||
.map(Pool::getQueryService)
|
||||
.orElseThrow(() -> newIllegalStateException("Pool [%s] has not been initialized", getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getReadTimeout() {
|
||||
return PoolFactoryBean.this.readTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRetryAttempts() {
|
||||
return PoolFactoryBean.this.retryAttempts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerGroup() {
|
||||
return PoolFactoryBean.this.serverGroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InetSocketAddress> getServers() {
|
||||
return PoolFactoryBean.this.servers.toInetSocketAddresses();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSocketBufferSize() {
|
||||
return PoolFactoryBean.this.socketBufferSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSocketConnectTimeout() {
|
||||
return PoolFactoryBean.this.socketConnectTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStatisticInterval() {
|
||||
return PoolFactoryBean.this.statisticInterval;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSubscriptionAckInterval() {
|
||||
return PoolFactoryBean.this.subscriptionAckInterval;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getSubscriptionEnabled() {
|
||||
return PoolFactoryBean.this.subscriptionEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSubscriptionMessageTrackingTimeout() {
|
||||
return PoolFactoryBean.this.subscriptionMessageTrackingTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSubscriptionRedundancy() {
|
||||
return PoolFactoryBean.this.subscriptionRedundancy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSubscriptionTimeoutMultiplier() {
|
||||
return PoolFactoryBean.this.subscriptionTimeoutMultiplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getThreadLocalConnections() {
|
||||
return PoolFactoryBean.this.threadLocalConnections;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
destroy(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy(boolean keepAlive) {
|
||||
|
||||
try {
|
||||
PoolFactoryBean.this.destroy();
|
||||
}
|
||||
catch (Exception ignore) {
|
||||
Optional.ofNullable(PoolFactoryBean.this.pool).ifPresent(pool -> pool.destroy(keepAlive));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releaseThreadLocalConnection() {
|
||||
|
||||
Optional.ofNullable(PoolFactoryBean.this.pool)
|
||||
.map(it -> {
|
||||
it.releaseThreadLocalConnection();
|
||||
return it;
|
||||
})
|
||||
.orElseThrow(() -> newIllegalStateException("Pool [%s] has not been initialized", getName()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setFreeConnectionTimeout(int freeConnectionTimeout) {
|
||||
this.freeConnectionTimeout = freeConnectionTimeout;
|
||||
}
|
||||
|
||||
public void setIdleTimeout(long idleTimeout) {
|
||||
this.idleTimeout = idleTimeout;
|
||||
}
|
||||
|
||||
public void setKeepAlive(boolean keepAlive) {
|
||||
this.keepAlive = keepAlive;
|
||||
}
|
||||
|
||||
public void setLoadConditioningInterval(int loadConditioningInterval) {
|
||||
this.loadConditioningInterval = loadConditioningInterval;
|
||||
}
|
||||
|
||||
public void setLocators(ConnectionEndpoint[] connectionEndpoints) {
|
||||
setLocators(ConnectionEndpointList.from(connectionEndpoints));
|
||||
}
|
||||
|
||||
public void setLocators(Iterable<ConnectionEndpoint> connectionEndpoints) {
|
||||
getLocators().clear();
|
||||
getLocators().add(connectionEndpoints);
|
||||
}
|
||||
|
||||
ConnectionEndpointList getLocators() {
|
||||
return locators;
|
||||
}
|
||||
|
||||
public void setMaxConnections(int maxConnections) {
|
||||
this.maxConnections = maxConnections;
|
||||
}
|
||||
|
||||
public void setMinConnections(int minConnections) {
|
||||
this.minConnections = minConnections;
|
||||
}
|
||||
|
||||
public void setMultiUserAuthentication(boolean multiUserAuthentication) {
|
||||
this.multiUserAuthentication = multiUserAuthentication;
|
||||
}
|
||||
|
||||
public void setPingInterval(long pingInterval) {
|
||||
this.pingInterval = pingInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe operation to set an array of {@link PoolConfigurer PoolConfigurers} used to apply
|
||||
* additional configuration to this {@link PoolFactoryBean} when using Annotation-based configuration.
|
||||
*
|
||||
* @param poolConfigurers array of {@link PoolConfigurer PoolConfigurers} used to apply
|
||||
* additional configuration to this {@link PoolFactoryBean}.
|
||||
* @see org.springframework.data.gemfire.config.annotation.PoolConfigurer
|
||||
* @see #setPoolConfigurers(List)
|
||||
*/
|
||||
public void setPoolConfigurers(PoolConfigurer... poolConfigurers) {
|
||||
setPoolConfigurers(Arrays.asList(nullSafeArray(poolConfigurers, PoolConfigurer.class)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe operation to set an {@link Iterable} of {@link PoolConfigurer PoolConfigurers} used to apply
|
||||
* additional configuration to this {@link PoolFactoryBean} when using Annotation-based configuration.
|
||||
*
|
||||
* @param poolConfigurers {@link Iterable} of {@link PoolConfigurer PoolConfigurers} used to apply
|
||||
* additional configuration to this {@link PoolFactoryBean}.
|
||||
* @see org.springframework.data.gemfire.config.annotation.PoolConfigurer
|
||||
*/
|
||||
public void setPoolConfigurers(List<PoolConfigurer> poolConfigurers) {
|
||||
this.poolConfigurers = Optional.ofNullable(poolConfigurers).orElseGet(Collections::emptyList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link PoolFactoryInitializer} to initialize the {@link PoolFactory} used by
|
||||
* this {@link PoolFactoryBean} to create a {@link Pool}.
|
||||
*
|
||||
* @param poolFactoryInitializer {@link PoolFactoryInitializer} user provided callback interface invoked
|
||||
* by this {@link PoolFactoryBean} to initialize the {@link PoolFactory} constructed to create the {@link Pool}.
|
||||
* @see org.springframework.data.gemfire.client.PoolFactoryBean.PoolFactoryInitializer
|
||||
*/
|
||||
public void setPoolFactoryInitializer(PoolFactoryInitializer poolFactoryInitializer) {
|
||||
this.poolFactoryInitializer = poolFactoryInitializer;
|
||||
}
|
||||
|
||||
public void setPrSingleHopEnabled(boolean prSingleHopEnabled) {
|
||||
this.prSingleHopEnabled = prSingleHopEnabled;
|
||||
}
|
||||
|
||||
public void setReadTimeout(int readTimeout) {
|
||||
this.readTimeout = readTimeout;
|
||||
}
|
||||
|
||||
public void setRetryAttempts(int retryAttempts) {
|
||||
this.retryAttempts = retryAttempts;
|
||||
}
|
||||
|
||||
public void setServerGroup(String serverGroup) {
|
||||
this.serverGroup = serverGroup;
|
||||
}
|
||||
|
||||
public void setServers(ConnectionEndpoint[] connectionEndpoints) {
|
||||
setServers(ConnectionEndpointList.from(connectionEndpoints));
|
||||
}
|
||||
|
||||
public void setServers(Iterable<ConnectionEndpoint> connectionEndpoints) {
|
||||
getServers().clear();
|
||||
getServers().add(connectionEndpoints);
|
||||
}
|
||||
|
||||
ConnectionEndpointList getServers() {
|
||||
return servers;
|
||||
}
|
||||
|
||||
public void setSocketBufferSize(int socketBufferSize) {
|
||||
this.socketBufferSize = socketBufferSize;
|
||||
}
|
||||
|
||||
public void setSocketConnectTimeout(int socketConnectTimeout) {
|
||||
this.socketConnectTimeout = socketConnectTimeout;
|
||||
}
|
||||
|
||||
public void setStatisticInterval(int statisticInterval) {
|
||||
this.statisticInterval = statisticInterval;
|
||||
}
|
||||
|
||||
public void setSubscriptionAckInterval(int subscriptionAckInterval) {
|
||||
this.subscriptionAckInterval = subscriptionAckInterval;
|
||||
}
|
||||
|
||||
public void setSubscriptionEnabled(boolean subscriptionEnabled) {
|
||||
this.subscriptionEnabled = subscriptionEnabled;
|
||||
}
|
||||
|
||||
public void setSubscriptionMessageTrackingTimeout(int subscriptionMessageTrackingTimeout) {
|
||||
this.subscriptionMessageTrackingTimeout = subscriptionMessageTrackingTimeout;
|
||||
}
|
||||
|
||||
public void setSubscriptionRedundancy(int subscriptionRedundancy) {
|
||||
this.subscriptionRedundancy = subscriptionRedundancy;
|
||||
}
|
||||
|
||||
public void setSubscriptionTimeoutMultiplier(int subscriptionTimeoutMultiplier) {
|
||||
this.subscriptionTimeoutMultiplier = subscriptionTimeoutMultiplier;
|
||||
}
|
||||
|
||||
public void setThreadLocalConnections(boolean threadLocalConnections) {
|
||||
this.threadLocalConnections = threadLocalConnections;
|
||||
}
|
||||
|
||||
// Internal framework use only.
|
||||
public final void setLocatorsConfiguration(Object locatorsConfiguration) {
|
||||
}
|
||||
|
||||
// Internal framework use only.
|
||||
public final void setServersConfiguration(Object serversConfiguration) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback interface to initialize the {@link PoolFactory} used by this {@link PoolFactoryBean}
|
||||
* to create a {@link Pool} by providing additional or alternative configuration for the factory.
|
||||
*
|
||||
* @see org.apache.geode.cache.client.PoolFactory
|
||||
*/
|
||||
public interface PoolFactoryInitializer {
|
||||
|
||||
/**
|
||||
* Initializes the given {@link PoolFactory}.
|
||||
*
|
||||
* @param poolFactory {@link PoolFactory} to initialize.
|
||||
* @return the given {@link PoolFactory}.
|
||||
* @see org.apache.geode.cache.client.PoolFactory
|
||||
*/
|
||||
PoolFactory initialize(PoolFactory poolFactory);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.client;
|
||||
|
||||
import org.apache.geode.cache.InterestResultPolicy;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Cache interest based on regular expression rather then individual key types.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author John Blum
|
||||
* @see org.springframework.data.gemfire.client.Interest
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class RegexInterest extends Interest<String> {
|
||||
|
||||
public RegexInterest(String regex) {
|
||||
super(regex);
|
||||
}
|
||||
|
||||
public RegexInterest(String regex, InterestResultPolicy policy) {
|
||||
super(regex, policy);
|
||||
}
|
||||
|
||||
public RegexInterest(String regex, InterestResultPolicy policy, boolean durable) {
|
||||
super(regex, policy, durable);
|
||||
}
|
||||
|
||||
public RegexInterest(String regex, InterestResultPolicy policy, boolean durable, boolean receiveValues) {
|
||||
super(regex, policy, durable, receiveValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
Assert.hasText(getKey(), "Regex is required");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Regular Expression sent to the cache server to express interests in keys matching Regex pattern.
|
||||
*
|
||||
* Alias for {@link #getKey()}.
|
||||
*
|
||||
* @return the Regex pattern used in the interest registration.
|
||||
* @see org.apache.geode.cache.Region#registerInterestRegex(String)
|
||||
*/
|
||||
public String getRegex() {
|
||||
return getKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.REGEX;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public void setType(Type type) {
|
||||
logger.warn(String.format("Setting the Type [%1$s] of Interest on [%2$s] is ignored",
|
||||
type, getClass().getName()));
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package org.springframework.data.gemfire.client.function;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.geode.cache.Cache;
|
||||
import org.apache.geode.cache.CacheFactory;
|
||||
import org.apache.geode.cache.Region;
|
||||
import org.apache.geode.cache.execute.Function;
|
||||
import org.apache.geode.cache.execute.FunctionContext;
|
||||
|
||||
/**
|
||||
* ListRegionsOnServerFunction is a GemFire Function class that returns a List of names for all Regions
|
||||
* defined in the GemFire cluster.
|
||||
*
|
||||
* @author David Turanski
|
||||
* @author John Blum
|
||||
* @see org.apache.geode.cache.execute.Function
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class ListRegionsOnServerFunction implements Function {
|
||||
|
||||
private static final long serialVersionUID = 867530169L;
|
||||
|
||||
public static final String ID = ListRegionsOnServerFunction.class.getName();
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.apache.geode.cache.execute.Function#execute(org.apache.geode.cache.execute.FunctionContext)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void execute(FunctionContext functionContext) {
|
||||
|
||||
List<String> regionNames = new ArrayList<>();
|
||||
|
||||
for (Region<?, ?> region : getCache().rootRegions()) {
|
||||
regionNames.add(region.getName());
|
||||
}
|
||||
|
||||
functionContext.getResultSender().lastResult(regionNames);
|
||||
}
|
||||
|
||||
Cache getCache() {
|
||||
return CacheFactory.getAnyInstance();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.apache.geode.cache.execute.Function#getId()
|
||||
*/
|
||||
@Override
|
||||
public String getId() {
|
||||
return this.getClass().getName();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.apache.geode.cache.execute.Function#hasResult()
|
||||
*/
|
||||
@Override
|
||||
public boolean hasResult() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.apache.geode.cache.execute.Function#isHA()
|
||||
*/
|
||||
@Override
|
||||
public boolean isHA() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.apache.geode.cache.execute.Function#optimizeForWrite()
|
||||
*/
|
||||
@Override
|
||||
public boolean optimizeForWrite() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* Classes providing support for GemFire client configuration.
|
||||
*
|
||||
*/
|
||||
package org.springframework.data.gemfire.client;
|
||||
@@ -1,230 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package org.springframework.data.gemfire.client.support;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.geode.cache.client.Pool;
|
||||
import org.apache.geode.cache.query.QueryService;
|
||||
|
||||
import org.springframework.data.gemfire.util.CollectionUtils;
|
||||
import org.springframework.data.gemfire.util.SpringUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* The {@link DefaultableDelegatingPoolAdapter} class is a wrapper class around {@link Pool}
|
||||
* allowing default configuration property values to be provided in the case that the {@link Pool Pool's}
|
||||
* settings were {@literal null}.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.apache.geode.cache.client.Pool
|
||||
* @since 1.8.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class DefaultableDelegatingPoolAdapter {
|
||||
|
||||
private final Pool delegate;
|
||||
|
||||
private Preference preference = Preference.PREFER_POOL;
|
||||
|
||||
public static DefaultableDelegatingPoolAdapter from(Pool delegate) {
|
||||
return new DefaultableDelegatingPoolAdapter(delegate) {};
|
||||
}
|
||||
|
||||
protected DefaultableDelegatingPoolAdapter(Pool delegate) {
|
||||
Assert.notNull(delegate, "Pool delegate must not be null");
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
protected Pool getDelegate() {
|
||||
return this.delegate;
|
||||
}
|
||||
|
||||
protected DefaultableDelegatingPoolAdapter setPreference(Preference preference) {
|
||||
this.preference = preference;
|
||||
return this;
|
||||
}
|
||||
|
||||
protected Preference getPreference() {
|
||||
return this.preference;
|
||||
}
|
||||
|
||||
protected <T> T defaultIfNull(T defaultValue, Supplier<T> valueProvider) {
|
||||
|
||||
return prefersPool() ? SpringUtils.defaultIfNull(valueProvider.get(), defaultValue) :
|
||||
(defaultValue != null ? defaultValue : valueProvider.get());
|
||||
}
|
||||
|
||||
protected <E, T extends Collection<E>> T defaultIfEmpty(T defaultValue, Supplier<T> valueProvider) {
|
||||
|
||||
if (prefersPool()) {
|
||||
T value = valueProvider.get();
|
||||
return CollectionUtils.isEmpty(value) ? defaultValue : value;
|
||||
}
|
||||
else {
|
||||
return CollectionUtils.isEmpty(defaultValue) ? valueProvider.get() : defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public DefaultableDelegatingPoolAdapter preferDefault() {
|
||||
return setPreference(Preference.PREFER_DEFAULT);
|
||||
}
|
||||
|
||||
protected boolean prefersDefault() {
|
||||
return Preference.PREFER_DEFAULT.equals(getPreference());
|
||||
}
|
||||
|
||||
public DefaultableDelegatingPoolAdapter preferPool() {
|
||||
return setPreference(Preference.PREFER_POOL);
|
||||
}
|
||||
|
||||
protected boolean prefersPool() {
|
||||
return Preference.PREFER_POOL.equals(getPreference());
|
||||
}
|
||||
|
||||
public boolean isDestroyed() {
|
||||
return getDelegate().isDestroyed();
|
||||
}
|
||||
|
||||
public int getFreeConnectionTimeout(Integer defaultFreeConnectionTimeout) {
|
||||
return defaultIfNull(defaultFreeConnectionTimeout, () -> getDelegate().getFreeConnectionTimeout());
|
||||
}
|
||||
|
||||
public long getIdleTimeout(Long defaultIdleTimeout) {
|
||||
return defaultIfNull(defaultIdleTimeout, () -> getDelegate().getIdleTimeout());
|
||||
}
|
||||
|
||||
public int getLoadConditioningInterval(Integer defaultLoadConditioningInterval) {
|
||||
return defaultIfNull(defaultLoadConditioningInterval, () -> getDelegate().getLoadConditioningInterval());
|
||||
}
|
||||
|
||||
public List<InetSocketAddress> getLocators(List<InetSocketAddress> defaultLocators) {
|
||||
return defaultIfEmpty(defaultLocators, () -> getDelegate().getLocators());
|
||||
}
|
||||
|
||||
public int getMaxConnections(Integer defaultMaxConnections) {
|
||||
return defaultIfNull(defaultMaxConnections, () -> getDelegate().getMaxConnections());
|
||||
}
|
||||
|
||||
public int getMinConnections(Integer defaultMinConnections) {
|
||||
return defaultIfNull(defaultMinConnections, () -> getDelegate().getMinConnections());
|
||||
}
|
||||
|
||||
public boolean getMultiuserAuthentication(Boolean defaultMultiUserAuthentication) {
|
||||
return defaultIfNull(defaultMultiUserAuthentication, () -> getDelegate().getMultiuserAuthentication());
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return getDelegate().getName();
|
||||
}
|
||||
|
||||
public int getPendingEventCount() {
|
||||
return getDelegate().getPendingEventCount();
|
||||
}
|
||||
|
||||
public long getPingInterval(Long defaultPingInterval) {
|
||||
return defaultIfNull(defaultPingInterval, () -> getDelegate().getPingInterval());
|
||||
}
|
||||
|
||||
public boolean getPRSingleHopEnabled(Boolean defaultPrSingleHopEnabled) {
|
||||
return defaultIfNull(defaultPrSingleHopEnabled, () -> getDelegate().getPRSingleHopEnabled());
|
||||
}
|
||||
|
||||
public QueryService getQueryService(QueryService defaultQueryService) {
|
||||
return defaultIfNull(defaultQueryService, () -> getDelegate().getQueryService());
|
||||
}
|
||||
|
||||
public int getReadTimeout(Integer defaultReadTimeout) {
|
||||
return defaultIfNull(defaultReadTimeout, () -> getDelegate().getReadTimeout());
|
||||
}
|
||||
|
||||
public int getRetryAttempts(Integer defaultRetryAttempts) {
|
||||
return defaultIfNull(defaultRetryAttempts, () -> getDelegate().getRetryAttempts());
|
||||
}
|
||||
|
||||
public String getServerGroup(String defaultServerGroup) {
|
||||
return defaultIfNull(defaultServerGroup, () -> getDelegate().getServerGroup());
|
||||
}
|
||||
|
||||
public List<InetSocketAddress> getServers(List<InetSocketAddress> defaultServers) {
|
||||
return defaultIfEmpty(defaultServers, () -> getDelegate().getServers());
|
||||
}
|
||||
|
||||
public int getSocketBufferSize(Integer defaultSocketBufferSize) {
|
||||
return defaultIfNull(defaultSocketBufferSize, () -> getDelegate().getSocketBufferSize());
|
||||
}
|
||||
|
||||
public int getSocketConnectTimeout(Integer defaultSocketConnectTimeout) {
|
||||
return defaultIfNull(defaultSocketConnectTimeout, () -> getDelegate().getSocketConnectTimeout());
|
||||
}
|
||||
|
||||
public int getStatisticInterval(Integer defaultStatisticInterval) {
|
||||
return defaultIfNull(defaultStatisticInterval, () -> getDelegate().getStatisticInterval());
|
||||
}
|
||||
|
||||
public int getSubscriptionAckInterval(Integer defaultSubscriptionAckInterval) {
|
||||
return defaultIfNull(defaultSubscriptionAckInterval, () -> getDelegate().getSubscriptionAckInterval());
|
||||
}
|
||||
|
||||
public boolean getSubscriptionEnabled(Boolean defaultSubscriptionEnabled) {
|
||||
return defaultIfNull(defaultSubscriptionEnabled, () -> getDelegate().getSubscriptionEnabled());
|
||||
}
|
||||
|
||||
public int getSubscriptionMessageTrackingTimeout(Integer defaultSubscriptionMessageTrackingTimeout) {
|
||||
return defaultIfNull(defaultSubscriptionMessageTrackingTimeout,
|
||||
() -> getDelegate().getSubscriptionMessageTrackingTimeout());
|
||||
}
|
||||
|
||||
public int getSubscriptionRedundancy(Integer defaultSubscriptionRedundancy) {
|
||||
return defaultIfNull(defaultSubscriptionRedundancy, () -> getDelegate().getSubscriptionRedundancy());
|
||||
}
|
||||
|
||||
public int getSubscriptionTimeoutMultiplier(Integer defaultSubscriptionTimeoutMultiplier) {
|
||||
return defaultIfNull(defaultSubscriptionTimeoutMultiplier,
|
||||
() -> getDelegate().getSubscriptionTimeoutMultiplier());
|
||||
}
|
||||
|
||||
public boolean getThreadLocalConnections(Boolean defaultThreadLocalConnections) {
|
||||
return defaultIfNull(defaultThreadLocalConnections, () -> getDelegate().getThreadLocalConnections());
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
getDelegate().destroy();
|
||||
}
|
||||
|
||||
public void destroy(boolean keepAlive) {
|
||||
getDelegate().destroy(keepAlive);
|
||||
}
|
||||
|
||||
public void releaseThreadLocalConnection() {
|
||||
getDelegate().releaseThreadLocalConnection();
|
||||
}
|
||||
|
||||
enum Preference {
|
||||
|
||||
PREFER_DEFAULT,
|
||||
PREFER_POOL
|
||||
|
||||
}
|
||||
|
||||
interface ValueProvider<T> {
|
||||
T getValue();
|
||||
}
|
||||
}
|
||||
@@ -1,289 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.client.support;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.geode.cache.client.Pool;
|
||||
import org.apache.geode.cache.query.QueryService;
|
||||
|
||||
/**
|
||||
* {@link DelegatingPoolAdapter} is an abstract implementation of GemFire's {@link Pool} interface and extension of
|
||||
* {@link FactoryDefaultsPoolAdapter} that delegates operations to the provided {@link Pool} instance.
|
||||
*
|
||||
* However, this implementation guards against a potentially <code>null</code> {@link Pool} reference by returning
|
||||
* default factory settings for the {@link Pool}'s configuration properties along with default behavior for operations
|
||||
* when the {@link Pool} reference is <code>null</code>.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.apache.geode.cache.client.Pool
|
||||
* @see org.apache.geode.cache.query.QueryService
|
||||
* @see org.springframework.data.gemfire.client.support.FactoryDefaultsPoolAdapter
|
||||
* @since 1.8.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class DelegatingPoolAdapter extends FactoryDefaultsPoolAdapter {
|
||||
|
||||
private final Pool delegate;
|
||||
|
||||
public static DelegatingPoolAdapter from(Pool delegate) {
|
||||
return new DelegatingPoolAdapter(delegate) {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance of {@link DelegatingPoolAdapter} initialized with the specified {@link Pool}.
|
||||
*
|
||||
* @param delegate {@link Pool} used as the delegate; can be {@literal null}.
|
||||
* @see org.apache.geode.cache.client.Pool
|
||||
*/
|
||||
public DelegatingPoolAdapter(Pool delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
protected Pool getDelegate() {
|
||||
return this.delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDestroyed() {
|
||||
return Optional.ofNullable(getDelegate()).map(Pool::isDestroyed).orElseGet(super::isDestroyed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFreeConnectionTimeout() {
|
||||
|
||||
return Optional.ofNullable(getDelegate())
|
||||
.map(Pool::getFreeConnectionTimeout)
|
||||
.orElseGet(super::getFreeConnectionTimeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getIdleTimeout() {
|
||||
|
||||
return Optional.ofNullable(getDelegate())
|
||||
.map(Pool::getIdleTimeout)
|
||||
.orElseGet(super::getIdleTimeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLoadConditioningInterval() {
|
||||
|
||||
return Optional.ofNullable(getDelegate())
|
||||
.map(Pool::getLoadConditioningInterval)
|
||||
.orElseGet(super::getLoadConditioningInterval);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InetSocketAddress> getLocators() {
|
||||
|
||||
return Optional.ofNullable(getDelegate())
|
||||
.map(Pool::getLocators)
|
||||
.orElseGet(super::getLocators);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxConnections() {
|
||||
|
||||
return Optional.ofNullable(getDelegate())
|
||||
.map(Pool::getMaxConnections)
|
||||
.orElseGet(super::getMaxConnections);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinConnections() {
|
||||
|
||||
return Optional.ofNullable(getDelegate())
|
||||
.map(Pool::getMinConnections)
|
||||
.orElseGet(super::getMinConnections);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getMultiuserAuthentication() {
|
||||
|
||||
return Optional.ofNullable(getDelegate())
|
||||
.map(Pool::getMultiuserAuthentication)
|
||||
.orElseGet(super::getMultiuserAuthentication);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
|
||||
return Optional.ofNullable(getDelegate())
|
||||
.map(Pool::getName)
|
||||
.orElseGet(super::getName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InetSocketAddress> getOnlineLocators() {
|
||||
|
||||
return Optional.ofNullable(getDelegate())
|
||||
.map(Pool::getOnlineLocators)
|
||||
.orElseGet(super::getOnlineLocators);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPendingEventCount() {
|
||||
|
||||
return Optional.ofNullable(getDelegate())
|
||||
.map(Pool::getPendingEventCount)
|
||||
.orElse(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getPingInterval() {
|
||||
|
||||
return Optional.ofNullable(getDelegate())
|
||||
.map(Pool::getPingInterval)
|
||||
.orElseGet(super::getPingInterval);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getPRSingleHopEnabled() {
|
||||
|
||||
return Optional.ofNullable(getDelegate())
|
||||
.map(Pool::getPRSingleHopEnabled)
|
||||
.orElseGet(super::getPRSingleHopEnabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryService getQueryService() {
|
||||
|
||||
return Optional.ofNullable(getDelegate())
|
||||
.map(Pool::getQueryService)
|
||||
.orElseGet(super::getQueryService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getReadTimeout() {
|
||||
|
||||
return Optional.ofNullable(getDelegate())
|
||||
.map(Pool::getReadTimeout)
|
||||
.orElseGet(super::getReadTimeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRetryAttempts() {
|
||||
|
||||
return Optional.ofNullable(getDelegate())
|
||||
.map(Pool::getRetryAttempts)
|
||||
.orElseGet(super::getRetryAttempts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerGroup() {
|
||||
|
||||
return Optional.ofNullable(getDelegate())
|
||||
.map(Pool::getServerGroup)
|
||||
.orElseGet(super::getServerGroup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InetSocketAddress> getServers() {
|
||||
|
||||
return Optional.ofNullable(getDelegate())
|
||||
.map(Pool::getServers)
|
||||
.orElseGet(super::getServers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSocketBufferSize() {
|
||||
|
||||
return Optional.ofNullable(getDelegate())
|
||||
.map(Pool::getSocketBufferSize)
|
||||
.orElseGet(super::getSocketBufferSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSocketConnectTimeout() {
|
||||
|
||||
return Optional.ofNullable(getDelegate())
|
||||
.map(Pool::getSocketConnectTimeout)
|
||||
.orElseGet(super::getSocketConnectTimeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStatisticInterval() {
|
||||
|
||||
return Optional.ofNullable(getDelegate())
|
||||
.map(Pool::getStatisticInterval)
|
||||
.orElseGet(super::getStatisticInterval);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSubscriptionAckInterval() {
|
||||
|
||||
return Optional.ofNullable(getDelegate())
|
||||
.map(Pool::getSubscriptionAckInterval)
|
||||
.orElseGet(super::getSubscriptionAckInterval);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getSubscriptionEnabled() {
|
||||
|
||||
return Optional.ofNullable(getDelegate())
|
||||
.map(Pool::getSubscriptionEnabled)
|
||||
.orElseGet(super::getSubscriptionEnabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSubscriptionMessageTrackingTimeout() {
|
||||
|
||||
return Optional.ofNullable(getDelegate())
|
||||
.map(Pool::getSubscriptionMessageTrackingTimeout)
|
||||
.orElseGet(super::getSubscriptionMessageTrackingTimeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSubscriptionRedundancy() {
|
||||
|
||||
return Optional.ofNullable(getDelegate())
|
||||
.map(Pool::getSubscriptionRedundancy)
|
||||
.orElseGet(super::getSubscriptionRedundancy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSubscriptionTimeoutMultiplier() {
|
||||
return Optional.ofNullable(getDelegate()).map(Pool::getSubscriptionTimeoutMultiplier)
|
||||
.orElseGet(super::getSubscriptionTimeoutMultiplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getThreadLocalConnections() {
|
||||
|
||||
return Optional.ofNullable(getDelegate())
|
||||
.map(Pool::getThreadLocalConnections)
|
||||
.orElseGet(super::getThreadLocalConnections);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
Optional.ofNullable(getDelegate()).ifPresent(Pool::destroy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy(boolean keepAlive) {
|
||||
Optional.ofNullable(getDelegate()).ifPresent(delegate -> delegate.destroy(keepAlive));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releaseThreadLocalConnection() {
|
||||
Optional.ofNullable(getDelegate()).ifPresent(Pool::releaseThreadLocalConnection);
|
||||
}
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.client.support;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.geode.cache.client.Pool;
|
||||
import org.apache.geode.cache.client.PoolFactory;
|
||||
import org.apache.geode.cache.query.QueryService;
|
||||
|
||||
import org.springframework.data.gemfire.GemfireUtils;
|
||||
import org.springframework.data.gemfire.client.PoolAdapter;
|
||||
|
||||
/**
|
||||
* {@link FactoryDefaultsPoolAdapter} is an abstract implementation of the {@link Pool} interface and extension of
|
||||
* {@link PoolAdapter} that provides default factory values for all configuration properties
|
||||
* (e.g. freeConnectionTimeout, idleTimeout, etc).
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.apache.geode.cache.client.Pool
|
||||
* @see org.apache.geode.cache.client.PoolFactory
|
||||
* @see org.springframework.data.gemfire.client.PoolAdapter
|
||||
* @since 1.8.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class FactoryDefaultsPoolAdapter extends PoolAdapter {
|
||||
|
||||
protected static final boolean DEFAULT_KEEP_ALIVE = false;
|
||||
|
||||
protected static final String DEFAULT_POOL_NAME = "DEFAULT";
|
||||
protected static final String LOCALHOST = "localhost";
|
||||
|
||||
@Override
|
||||
public int getFreeConnectionTimeout() {
|
||||
return PoolFactory.DEFAULT_FREE_CONNECTION_TIMEOUT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getIdleTimeout() {
|
||||
return PoolFactory.DEFAULT_IDLE_TIMEOUT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLoadConditioningInterval() {
|
||||
return PoolFactory.DEFAULT_LOAD_CONDITIONING_INTERVAL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InetSocketAddress> getLocators() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxConnections() {
|
||||
return PoolFactory.DEFAULT_MAX_CONNECTIONS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinConnections() {
|
||||
return PoolFactory.DEFAULT_MIN_CONNECTIONS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getMultiuserAuthentication() {
|
||||
return PoolFactory.DEFAULT_MULTIUSER_AUTHENTICATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return DEFAULT_POOL_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InetSocketAddress> getOnlineLocators() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getPingInterval() {
|
||||
return PoolFactory.DEFAULT_PING_INTERVAL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getPRSingleHopEnabled() {
|
||||
return PoolFactory.DEFAULT_PR_SINGLE_HOP_ENABLED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryService getQueryService() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getReadTimeout() {
|
||||
return PoolFactory.DEFAULT_READ_TIMEOUT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRetryAttempts() {
|
||||
return PoolFactory.DEFAULT_RETRY_ATTEMPTS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerGroup() {
|
||||
return PoolFactory.DEFAULT_SERVER_GROUP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InetSocketAddress> getServers() {
|
||||
return Collections.singletonList(new InetSocketAddress(LOCALHOST, GemfireUtils.DEFAULT_CACHE_SERVER_PORT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSocketBufferSize() {
|
||||
return PoolFactory.DEFAULT_SOCKET_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSocketConnectTimeout() {
|
||||
return PoolFactory.DEFAULT_SOCKET_CONNECT_TIMEOUT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStatisticInterval() {
|
||||
return PoolFactory.DEFAULT_STATISTIC_INTERVAL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSubscriptionAckInterval() {
|
||||
return PoolFactory.DEFAULT_SUBSCRIPTION_ACK_INTERVAL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getSubscriptionEnabled() {
|
||||
return PoolFactory.DEFAULT_SUBSCRIPTION_ENABLED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSubscriptionMessageTrackingTimeout() {
|
||||
return PoolFactory.DEFAULT_SUBSCRIPTION_MESSAGE_TRACKING_TIMEOUT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSubscriptionRedundancy() {
|
||||
return PoolFactory.DEFAULT_SUBSCRIPTION_REDUNDANCY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSubscriptionTimeoutMultiplier() {
|
||||
return PoolFactory.DEFAULT_SUBSCRIPTION_TIMEOUT_MULTIPLIER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getThreadLocalConnections() {
|
||||
return PoolFactory.DEFAULT_THREAD_LOCAL_CONNECTIONS;
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
destroy(DEFAULT_KEEP_ALIVE);
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.config.admin;
|
||||
|
||||
import org.apache.geode.cache.DiskStore;
|
||||
import org.apache.geode.cache.Region;
|
||||
import org.apache.geode.cache.lucene.LuceneIndex;
|
||||
import org.apache.geode.cache.query.Index;
|
||||
|
||||
import org.springframework.data.gemfire.config.schema.SchemaObjectDefinition;
|
||||
import org.springframework.data.gemfire.config.schema.definitions.IndexDefinition;
|
||||
import org.springframework.data.gemfire.config.schema.definitions.RegionDefinition;
|
||||
|
||||
/**
|
||||
* {@link AbstractGemfireAdminOperations} is an abstract base class encapsulating common functionality
|
||||
* supporting administrative (management) operations against a Pivotal GemFire or Apache Geode cluster.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.data.gemfire.config.admin.GemfireAdminOperations
|
||||
* @see org.springframework.data.gemfire.config.schema.SchemaObjectDefinition
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class AbstractGemfireAdminOperations implements GemfireAdminOperations {
|
||||
|
||||
protected static final String NOT_IMPLEMENTED = "Not Implemented";
|
||||
|
||||
/**
|
||||
* Returns a {@link Iterable collection} of {@link Region} names defined on the GemFire Servers in the cluster.
|
||||
*
|
||||
* @return an {@link Iterable} of {@link Region} names defined on the GemFire Servers in the cluster.
|
||||
* @see org.apache.geode.cache.Region#getName()
|
||||
* @see java.lang.Iterable
|
||||
*/
|
||||
@Override
|
||||
public Iterable<String> getAvailableServerRegions() {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an {@link Iterable} of all the server {@link Region} {@link Index Indexes}.
|
||||
*
|
||||
* @return an {@link Iterable} of all the server {@link Region} {@link Index Indexes}.
|
||||
* @see org.apache.geode.cache.query.Index#getName()
|
||||
* @see java.lang.Iterable
|
||||
*/
|
||||
@Override
|
||||
public Iterable<String> getAvailableServerRegionIndexes() {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a cache {@link Region} based on the given {@link RegionDefinition schema object definition}.
|
||||
*
|
||||
* @param regionDefinition {@link RegionDefinition} encapsulating configuration meta-data defining
|
||||
* a cache {@link Region}.
|
||||
* @see org.springframework.data.gemfire.config.schema.definitions.RegionDefinition
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
* @see org.apache.geode.cache.Region
|
||||
*/
|
||||
@Override
|
||||
public void createRegion(RegionDefinition regionDefinition) {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Region} {@link LuceneIndex} based on the given
|
||||
* {@link SchemaObjectDefinition schema object definition}.
|
||||
*
|
||||
* @param luceneIndexDefinition {@link SchemaObjectDefinition} encapsulating the configuration meta-data
|
||||
* defining a {@link Region} {@link LuceneIndex}.
|
||||
* @see org.springframework.data.gemfire.config.schema.SchemaObjectDefinition
|
||||
* @see org.apache.geode.cache.lucene.LuceneIndex
|
||||
* @see org.apache.geode.cache.Region
|
||||
*/
|
||||
@Override
|
||||
public void createLuceneIndex(SchemaObjectDefinition luceneIndexDefinition) {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Region} OQL {@link Index} based on the given {@link IndexDefinition schema object definition}.
|
||||
*
|
||||
* @param indexDefinition {@link IndexDefinition} encapsulating the configuration meta-data
|
||||
* defining a {@link Region} OQL {@link Index}.
|
||||
* @see org.springframework.data.gemfire.config.schema.definitions.IndexDefinition
|
||||
* @see org.apache.geode.cache.query.Index
|
||||
* @see org.apache.geode.cache.Region
|
||||
*/
|
||||
@Override
|
||||
public void createIndex(IndexDefinition indexDefinition) {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link DiskStore} based on the given {@link SchemaObjectDefinition schema object definition}.
|
||||
*
|
||||
* @param diskStoreDefinition {@link SchemaObjectDefinition} encapsulating the configuration meta-data
|
||||
* defining a {@link DiskStore}.
|
||||
* @see org.springframework.data.gemfire.config.schema.SchemaObjectDefinition
|
||||
* @see org.apache.geode.cache.DiskStore
|
||||
*/
|
||||
@Override
|
||||
public void createDiskStore(SchemaObjectDefinition diskStoreDefinition) {
|
||||
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.config.admin;
|
||||
|
||||
import static java.util.Arrays.stream;
|
||||
import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
|
||||
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeIterable;
|
||||
|
||||
import org.apache.geode.cache.DiskStore;
|
||||
import org.apache.geode.cache.Region;
|
||||
import org.apache.geode.cache.lucene.LuceneIndex;
|
||||
import org.apache.geode.cache.query.Index;
|
||||
|
||||
import org.springframework.data.gemfire.config.schema.SchemaObjectDefinition;
|
||||
import org.springframework.data.gemfire.config.schema.definitions.IndexDefinition;
|
||||
import org.springframework.data.gemfire.config.schema.definitions.RegionDefinition;
|
||||
|
||||
/**
|
||||
* The {@link GemfireAdminOperations} interface defines a set of operations to define schema objects in a remote
|
||||
* Apache Geode or Pivotal GemFire cluster.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.apache.geode.cache.DiskStore
|
||||
* @see org.apache.geode.cache.Region
|
||||
* @see org.apache.geode.cache.lucene.LuceneIndex
|
||||
* @see org.apache.geode.cache.query.Index
|
||||
* @see org.springframework.data.gemfire.config.schema.definitions.IndexDefinition
|
||||
* @see org.springframework.data.gemfire.config.schema.definitions.RegionDefinition
|
||||
* @see org.springframework.data.gemfire.config.schema.SchemaObjectDefinition
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public interface GemfireAdminOperations {
|
||||
|
||||
/**
|
||||
* Returns a {@link Iterable collection} of {@link Region} names defined on the GemFire Servers in the cluster.
|
||||
*
|
||||
* @return an {@link Iterable} of {@link Region} names defined on the GemFire Servers in the cluster.
|
||||
* @see org.apache.geode.cache.Region#getName()
|
||||
* @see java.lang.Iterable
|
||||
*/
|
||||
Iterable<String> getAvailableServerRegions();
|
||||
|
||||
/**
|
||||
* Returns an {@link Iterable} of all the server {@link Region} {@link Index Indexes}.
|
||||
*
|
||||
* @return an {@link Iterable} of all the server {@link Region} {@link Index Indexes}.
|
||||
* @see org.apache.geode.cache.query.Index#getName()
|
||||
* @see java.lang.Iterable
|
||||
*/
|
||||
Iterable<String> getAvailableServerRegionIndexes();
|
||||
|
||||
/**
|
||||
* Creates a cache {@link Region} based on the given {@link RegionDefinition schema object definition}.
|
||||
*
|
||||
* @param regionDefinition {@link RegionDefinition} encapsulating configuration meta-data defining
|
||||
* a cache {@link Region}.
|
||||
* @see org.springframework.data.gemfire.config.schema.definitions.RegionDefinition
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
* @see org.apache.geode.cache.Region
|
||||
*/
|
||||
void createRegion(RegionDefinition regionDefinition);
|
||||
|
||||
default void createRegions(RegionDefinition... regionDefinitions) {
|
||||
stream(nullSafeArray(regionDefinitions, RegionDefinition.class)).forEach(this::createRegion);
|
||||
}
|
||||
|
||||
default void createRegions(Iterable<RegionDefinition> regionDefinitions) {
|
||||
nullSafeIterable(regionDefinitions).forEach(this::createRegion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Region} {@link LuceneIndex} based on the given
|
||||
* {@link SchemaObjectDefinition schema object definition}.
|
||||
*
|
||||
* @param luceneIndexDefinition {@link SchemaObjectDefinition} encapsulating the configuration meta-data
|
||||
* defining a {@link Region} {@link LuceneIndex}.
|
||||
* @see org.springframework.data.gemfire.config.schema.SchemaObjectDefinition
|
||||
* @see org.apache.geode.cache.lucene.LuceneIndex
|
||||
* @see org.apache.geode.cache.Region
|
||||
*/
|
||||
void createLuceneIndex(SchemaObjectDefinition luceneIndexDefinition);
|
||||
|
||||
default void createLuceneIndexes(SchemaObjectDefinition... luceneIndexDefinitions) {
|
||||
stream(nullSafeArray(luceneIndexDefinitions, SchemaObjectDefinition.class)).forEach(this::createLuceneIndex);
|
||||
}
|
||||
|
||||
default void createLuceneIndexes(Iterable<SchemaObjectDefinition> luceneIndexDefinitions) {
|
||||
nullSafeIterable(luceneIndexDefinitions).forEach(this::createLuceneIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Region} OQL {@link Index} based on the given {@link IndexDefinition schema object definition}.
|
||||
*
|
||||
* @param indexDefinition {@link IndexDefinition} encapsulating the configuration meta-data
|
||||
* defining a {@link Region} OQL {@link Index}.
|
||||
* @see org.springframework.data.gemfire.config.schema.definitions.IndexDefinition
|
||||
* @see org.apache.geode.cache.query.Index
|
||||
* @see org.apache.geode.cache.Region
|
||||
*/
|
||||
void createIndex(IndexDefinition indexDefinition);
|
||||
|
||||
default void createIndexes(IndexDefinition... indexDefinitions) {
|
||||
stream(nullSafeArray(indexDefinitions, IndexDefinition.class)).forEach(this::createIndex);
|
||||
}
|
||||
|
||||
default void createIndexes(Iterable<IndexDefinition> indexDefinitions) {
|
||||
nullSafeIterable(indexDefinitions).forEach(this::createIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link DiskStore} based on the given {@link SchemaObjectDefinition schema object definition}.
|
||||
*
|
||||
* @param diskStoreDefinition {@link SchemaObjectDefinition} encapsulating the configuration meta-data
|
||||
* defining a {@link DiskStore}.
|
||||
* @see org.springframework.data.gemfire.config.schema.SchemaObjectDefinition
|
||||
* @see org.apache.geode.cache.DiskStore
|
||||
*/
|
||||
void createDiskStore(SchemaObjectDefinition diskStoreDefinition);
|
||||
|
||||
default void createDiskStores(SchemaObjectDefinition... diskStoreDefinitions) {
|
||||
stream(nullSafeArray(diskStoreDefinitions, SchemaObjectDefinition.class)).forEach(this::createDiskStore);
|
||||
}
|
||||
|
||||
default void createDiskStores(Iterable<SchemaObjectDefinition> diskStoreDefinitions) {
|
||||
nullSafeIterable(diskStoreDefinitions).forEach(this::createDiskStore);
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.config.admin.functions;
|
||||
|
||||
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeCollection;
|
||||
|
||||
import org.apache.geode.cache.Cache;
|
||||
import org.apache.geode.cache.CacheFactory;
|
||||
import org.apache.geode.cache.GemFireCache;
|
||||
import org.apache.geode.cache.query.QueryException;
|
||||
import org.apache.geode.cache.query.QueryService;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.data.gemfire.GemfireCacheUtils;
|
||||
import org.springframework.data.gemfire.config.schema.definitions.IndexDefinition;
|
||||
import org.springframework.data.gemfire.function.annotation.GemfireFunction;
|
||||
|
||||
/**
|
||||
* The CreateIndexFunction class...
|
||||
*
|
||||
* @author John Blum
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class CreateIndexFunction {
|
||||
|
||||
public static final String CREATE_INDEX_FUNCTION_ID = "CreateOqlIndexFunction";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
@GemfireFunction(id = CREATE_INDEX_FUNCTION_ID)
|
||||
public boolean createIndex(IndexDefinition indexDefinition) {
|
||||
|
||||
Cache gemfireCache = resolveCache();
|
||||
|
||||
if (isNonExistingIndex(gemfireCache, indexDefinition)) {
|
||||
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Creating Index with name [{}] having expression [{}] on Region [{}] with type [{}]",
|
||||
indexDefinition.getName(), indexDefinition.getExpression(), indexDefinition.getFromClause(),
|
||||
indexDefinition.getIndexType());
|
||||
}
|
||||
|
||||
QueryService queryService = gemfireCache.getQueryService();
|
||||
|
||||
try {
|
||||
switch (indexDefinition.getIndexType()) {
|
||||
case KEY:
|
||||
case PRIMARY_KEY:
|
||||
queryService.createKeyIndex(indexDefinition.getName(),
|
||||
indexDefinition.getExpression(), indexDefinition.getFromClause());
|
||||
return true;
|
||||
case HASH:
|
||||
queryService.createHashIndex(indexDefinition.getName(),
|
||||
indexDefinition.getExpression(), indexDefinition.getFromClause());
|
||||
return true;
|
||||
case FUNCTIONAL:
|
||||
queryService.createIndex(indexDefinition.getName(),
|
||||
indexDefinition.getExpression(), indexDefinition.getFromClause());
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (QueryException cause) {
|
||||
throw GemfireCacheUtils.convertGemfireAccessException(cause);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Index with name [{}] already exists", indexDefinition.getName());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected Cache resolveCache() {
|
||||
return CacheFactory.getAnyInstance();
|
||||
}
|
||||
|
||||
protected boolean isNonExistingIndex(GemFireCache gemfireCache, IndexDefinition indexDefinition) {
|
||||
return !nullSafeCollection(gemfireCache.getQueryService().getIndexes()).stream()
|
||||
.anyMatch(index -> index.getName().equals(indexDefinition.getName()));
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.config.admin.functions;
|
||||
|
||||
import org.apache.geode.cache.Cache;
|
||||
import org.apache.geode.cache.CacheFactory;
|
||||
import org.apache.geode.cache.Region;
|
||||
import org.apache.geode.cache.RegionFactory;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.data.gemfire.config.schema.definitions.RegionDefinition;
|
||||
import org.springframework.data.gemfire.function.annotation.GemfireFunction;
|
||||
|
||||
/**
|
||||
* The CreateRegionFunction class...
|
||||
*
|
||||
* @author John Blum
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class CreateRegionFunction {
|
||||
|
||||
public static final String CREATE_REGION_FUNCTION_ID = "CreateRegionFunction";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
@GemfireFunction(id = CREATE_REGION_FUNCTION_ID)
|
||||
public boolean createRegion(RegionDefinition regionDefinition) {
|
||||
|
||||
Cache gemfireCache = resolveCache();
|
||||
|
||||
if (isNonExistingRegion(gemfireCache, regionDefinition)) {
|
||||
|
||||
RegionFactory regionFactory = gemfireCache.createRegionFactory(regionDefinition.getRegionShortcut());
|
||||
|
||||
Region region = regionFactory.create(regionDefinition.getName());
|
||||
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Created Region [{}] of type [{}]", region.getName(), region.getAttributes().getDataPolicy());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.info("Region with name [{}] already exists", regionDefinition.getName());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected Cache resolveCache() {
|
||||
return CacheFactory.getAnyInstance();
|
||||
}
|
||||
|
||||
private boolean isNonExistingRegion(Cache gemfireCache, RegionDefinition regionDefinition) {
|
||||
return (gemfireCache.getRegion(regionDefinition.getName()) == null);
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.config.admin.functions;
|
||||
|
||||
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeCollection;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.geode.cache.Cache;
|
||||
import org.apache.geode.cache.CacheFactory;
|
||||
import org.apache.geode.cache.query.Index;
|
||||
|
||||
import org.springframework.data.gemfire.function.annotation.GemfireFunction;
|
||||
|
||||
/**
|
||||
* The ListIndexesFunction class...
|
||||
*
|
||||
* @author John Blum
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class ListIndexesFunction {
|
||||
|
||||
public static final String LIST_INDEXES_FUNCTION_ID = "ListQqlIndexesFunction";
|
||||
|
||||
@GemfireFunction(id = LIST_INDEXES_FUNCTION_ID)
|
||||
public Set<String> listIndexes() {
|
||||
|
||||
return Optional.ofNullable(resolveCache())
|
||||
.map(cache -> cache.getQueryService())
|
||||
.map(queryService ->
|
||||
nullSafeCollection(queryService.getIndexes()).stream().map(Index::getName).collect(Collectors.toSet()))
|
||||
.orElseGet(Collections::emptySet);
|
||||
}
|
||||
|
||||
protected Cache resolveCache() {
|
||||
return CacheFactory.getAnyInstance();
|
||||
}
|
||||
}
|
||||
@@ -1,167 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.config.admin.remote;
|
||||
|
||||
import static java.util.Arrays.stream;
|
||||
import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.geode.cache.Region;
|
||||
import org.apache.geode.cache.client.ClientCache;
|
||||
import org.apache.geode.cache.execute.Function;
|
||||
import org.apache.geode.cache.query.Index;
|
||||
import org.apache.geode.management.internal.cli.domain.RegionInformation;
|
||||
import org.apache.geode.management.internal.cli.functions.GetRegionsFunction;
|
||||
|
||||
import org.springframework.data.gemfire.client.function.ListRegionsOnServerFunction;
|
||||
import org.springframework.data.gemfire.config.admin.AbstractGemfireAdminOperations;
|
||||
import org.springframework.data.gemfire.config.admin.GemfireAdminOperations;
|
||||
import org.springframework.data.gemfire.config.admin.functions.CreateIndexFunction;
|
||||
import org.springframework.data.gemfire.config.admin.functions.CreateRegionFunction;
|
||||
import org.springframework.data.gemfire.config.admin.functions.ListIndexesFunction;
|
||||
import org.springframework.data.gemfire.config.schema.definitions.IndexDefinition;
|
||||
import org.springframework.data.gemfire.config.schema.definitions.RegionDefinition;
|
||||
import org.springframework.data.gemfire.function.execution.GemfireFunctionOperations;
|
||||
import org.springframework.data.gemfire.function.execution.GemfireOnServersFunctionTemplate;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* The {@link FunctionGemfireAdminTemplate} class is an implementation of the {@link GemfireAdminOperations} interface
|
||||
* supporting the Pivotal GemFire / Apache Geode administrative functions/operations via {@link Function} execution
|
||||
* in the cluster.
|
||||
*
|
||||
* Note: any schema changing functionality (e.g. {@link #createRegion(RegionDefinition)}) does not get recorded by
|
||||
* the GemFire/Geode Cluster Configuration Service using this strategy.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.apache.geode.cache.client.ClientCache
|
||||
* @see org.apache.geode.cache.execute.Function
|
||||
* @see org.springframework.data.gemfire.client.function.ListRegionsOnServerFunction
|
||||
* @see org.springframework.data.gemfire.config.admin.AbstractGemfireAdminOperations
|
||||
* @see org.springframework.data.gemfire.function.execution.GemfireOnServersFunctionTemplate
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class FunctionGemfireAdminTemplate extends AbstractGemfireAdminOperations {
|
||||
|
||||
private final ClientCache clientCache;
|
||||
|
||||
/**
|
||||
* Constructs a new instance of the {@link FunctionGemfireAdminTemplate} initialized with
|
||||
* a {@link ClientCache} instance.
|
||||
*
|
||||
* @param clientCache reference to a {@link ClientCache} instance.
|
||||
* @throws IllegalArgumentException if {@link ClientCache} is {@literal null}.
|
||||
* @see org.apache.geode.cache.client.ClientCache
|
||||
*/
|
||||
public FunctionGemfireAdminTemplate(ClientCache clientCache) {
|
||||
|
||||
Assert.notNull(clientCache, "ClientCache is required");
|
||||
|
||||
this.clientCache = clientCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the configured {@link ClientCache} instance.
|
||||
*
|
||||
* @return a reference to the configured {@link ClientCache} instance.
|
||||
* @see org.apache.geode.cache.client.ClientCache
|
||||
*/
|
||||
protected ClientCache getClientCache() {
|
||||
return this.clientCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all available {@link Region Regions} configured for all servers in the remote Pivotal GemFire
|
||||
* / Apache Geode cluster.
|
||||
*
|
||||
* @return an {@link Iterable} of servers-side {@link Region} names for all {@link Region Regions} defined
|
||||
* across all servers in the remote GemFire/Geode cluster.
|
||||
* @see java.lang.Iterable
|
||||
*/
|
||||
@Override
|
||||
public Iterable<String> getAvailableServerRegions() {
|
||||
|
||||
try {
|
||||
return execute(new ListRegionsOnServerFunction());
|
||||
}
|
||||
catch (Exception cause) {
|
||||
try {
|
||||
return Optional.ofNullable(execute(new GetRegionsFunction()))
|
||||
.filter(this::containsRegionInformation)
|
||||
.map(regionInformationArray ->
|
||||
stream(nullSafeArray((Object[]) regionInformationArray, Object.class))
|
||||
.map(regionInformation -> ((RegionInformation) regionInformation).getName())
|
||||
.collect(Collectors.toSet())
|
||||
)
|
||||
.orElse(Collections.emptySet());
|
||||
}
|
||||
catch (Exception ignore) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an {@link Iterable} of all the server {@link Region} {@link Index Indexes}.
|
||||
*
|
||||
* @return an {@link Iterable} of all the server {@link Region} {@link Index Indexes}.
|
||||
* @see org.apache.geode.cache.query.Index#getName()
|
||||
* @see java.lang.Iterable
|
||||
*/
|
||||
@Override
|
||||
public Iterable<String> getAvailableServerRegionIndexes() {
|
||||
return execute(ListIndexesFunction.LIST_INDEXES_FUNCTION_ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createRegion(RegionDefinition regionDefinition) {
|
||||
execute(CreateRegionFunction.CREATE_REGION_FUNCTION_ID, regionDefinition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createIndex(IndexDefinition indexDefinition) {
|
||||
execute(CreateIndexFunction.CREATE_INDEX_FUNCTION_ID, indexDefinition);
|
||||
}
|
||||
|
||||
<T> T execute(Function gemfireFunction, Object... arguments) {
|
||||
return newGemfireFunctionOperations().executeAndExtract(gemfireFunction, arguments);
|
||||
}
|
||||
|
||||
<T> T execute(String gemfireFunctionId, Object... arguments) {
|
||||
return newGemfireFunctionOperations().executeAndExtract(gemfireFunctionId, arguments);
|
||||
}
|
||||
|
||||
protected GemfireFunctionOperations newGemfireFunctionOperations() {
|
||||
return newGemfireFunctionOperations(getClientCache());
|
||||
}
|
||||
|
||||
protected GemfireFunctionOperations newGemfireFunctionOperations(ClientCache clientCache) {
|
||||
return new GemfireOnServersFunctionTemplate(clientCache);
|
||||
}
|
||||
|
||||
boolean containsRegionInformation(Object results) {
|
||||
|
||||
return Optional.ofNullable(results)
|
||||
.filter(it -> it instanceof Object[])
|
||||
.filter(it -> ((Object[]) it).length > 0)
|
||||
.filter(it -> ((Object[]) it)[0] instanceof RegionInformation)
|
||||
.isPresent();
|
||||
}
|
||||
}
|
||||
@@ -1,437 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.gemfire.config.admin.remote;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.geode.cache.client.ClientCache;
|
||||
import org.apache.geode.cache.execute.Function;
|
||||
|
||||
import org.springframework.data.gemfire.config.admin.GemfireAdminOperations;
|
||||
import org.springframework.data.gemfire.config.schema.definitions.IndexDefinition;
|
||||
import org.springframework.data.gemfire.config.schema.definitions.RegionDefinition;
|
||||
import org.springframework.data.gemfire.config.support.RestTemplateConfigurer;
|
||||
import org.springframework.data.gemfire.util.ArrayUtils;
|
||||
import org.springframework.data.gemfire.util.CollectionUtils;
|
||||
import org.springframework.data.gemfire.util.NetworkUtils;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.client.RestOperations;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* {@link RestHttpGemfireAdminTemplate} is class implementing the {@link GemfireAdminOperations} interface,
|
||||
* extending the {@link FunctionGemfireAdminTemplate} to support administrative (management) operations
|
||||
* on a Pivotal GemFire or Apache Geode cluster using the Management REST API interface over HTTP.
|
||||
*
|
||||
* The fallback is using {@link Function} execution if the a particular administrative (management) operation
|
||||
* is not supported or has not been implemented against the Management REST API interface over HTTP.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see java.net.HttpURLConnection
|
||||
* @see java.net.URI
|
||||
* @see org.apache.geode.cache.client.ClientCache
|
||||
* @see org.apache.geode.cache.execute.Function
|
||||
* @see org.springframework.data.gemfire.config.admin.GemfireAdminOperations
|
||||
* @see org.springframework.data.gemfire.config.admin.remote.FunctionGemfireAdminTemplate
|
||||
* @see org.springframework.http.HttpHeaders
|
||||
* @see org.springframework.http.HttpMethod
|
||||
* @see org.springframework.http.HttpStatus
|
||||
* @see org.springframework.http.RequestEntity
|
||||
* @see org.springframework.http.ResponseEntity
|
||||
* @see org.springframework.http.client.ClientHttpRequestFactory
|
||||
* @see org.springframework.http.client.ClientHttpRequestInterceptor
|
||||
* @see org.springframework.http.client.SimpleClientHttpRequestFactory
|
||||
* @see org.springframework.web.client.RestOperations
|
||||
* @see org.springframework.web.client.RestTemplate
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class RestHttpGemfireAdminTemplate extends FunctionGemfireAdminTemplate {
|
||||
|
||||
protected static final boolean DEFAULT_CREATE_REGION_SKIP_IF_EXISTS = true;
|
||||
protected static final boolean DEFAULT_HTTP_FOLLOW_REDIRECTS = true;
|
||||
|
||||
// Default port to -1 to let HTTP clients determine the port from the protocol/scheme.
|
||||
// By default, Apache Geode / Pivotal GemFire's (embedded) HTTP service listens on port 7070.
|
||||
protected static final int DEFAULT_PORT = -1;
|
||||
|
||||
protected static final String DEFAULT_HOST = "localhost";
|
||||
protected static final String DEFAULT_SCHEME = "https";
|
||||
protected static final String HTTP_SCHEME = "http";
|
||||
protected static final String HTTPS_SCHEME = "https";
|
||||
protected static final String MANAGEMENT_REST_API_URL_TEMPLATE = "%1$s://%2$s:%3$d/gemfire/v1";
|
||||
protected static final String MANAGEMENT_REST_API_NO_PORT_URL_TEMPLATE = "%1$s://%2$s/gemfire/v1";
|
||||
|
||||
protected static final List<String> VALID_SCHEMES = Arrays.asList(HTTP_SCHEME, HTTPS_SCHEME);
|
||||
|
||||
private final RestOperations restTemplate;
|
||||
|
||||
private final String managementRestApiUrl;
|
||||
|
||||
/**
|
||||
* Constructs a new instance of {@link RestHttpGemfireAdminTemplate} initialized with the given {@link ClientCache}
|
||||
* and configured with the default HTTP schema, host and port when accessing the Apache Geode or Pivotal GemFire
|
||||
* Management REST API interface.
|
||||
*
|
||||
* @param clientCache reference to the {@link ClientCache}.
|
||||
* @throws IllegalArgumentException if {@link ClientCache} is {@literal null}.
|
||||
* @see #RestHttpGemfireAdminTemplate(ClientCache, String, String, int, boolean, List)
|
||||
* @see org.apache.geode.cache.client.ClientCache
|
||||
*/
|
||||
public RestHttpGemfireAdminTemplate(ClientCache clientCache) {
|
||||
|
||||
this(clientCache, DEFAULT_SCHEME, DEFAULT_HOST, DEFAULT_PORT, DEFAULT_HTTP_FOLLOW_REDIRECTS,
|
||||
Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance of {@link RestHttpGemfireAdminTemplate} initialized with the given {@link ClientCache}
|
||||
* and configured with the specified HTTP scheme, host, port, redirects and
|
||||
* {@link ClientHttpRequestInterceptor ClientHttpRequestInterceptors}
|
||||
* when accessing the Apache Geode or Pivotal GemFire Management REST API interface.
|
||||
*
|
||||
* @param clientCache reference to the {@link ClientCache}
|
||||
* @param scheme {@link String} specifying the HTTP scheme to use (e.g. HTTP or HTTPS).
|
||||
* @param host {@link String} containing the hostname of the GemFire/Geode Manager.
|
||||
* @param port integer value specifying the port on which the GemFire/Geode Manager HTTP Service is listening
|
||||
* for HTTP clients.
|
||||
* @param followRedirects boolean indicating whether HTTP Redirects (with HTTP Status Code 3xx) should be followed.
|
||||
* @param clientHttpRequestInterceptors {@link List} of {@link ClientHttpRequestInterceptor} used to intercept
|
||||
* and decorate the HTTP request and HTTP response.
|
||||
* @throws IllegalArgumentException if the {@link ClientCache} reference is {@literal null}.
|
||||
* @see org.springframework.http.client.ClientHttpRequestInterceptor
|
||||
* @see org.apache.geode.cache.client.ClientCache
|
||||
*/
|
||||
public RestHttpGemfireAdminTemplate(ClientCache clientCache, String scheme, String host, int port,
|
||||
boolean followRedirects, List<ClientHttpRequestInterceptor> clientHttpRequestInterceptors) {
|
||||
|
||||
this(clientCache, scheme, host, port, followRedirects, clientHttpRequestInterceptors, Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance of {@link RestHttpGemfireAdminTemplate} initialized with the given {@link ClientCache}
|
||||
* and configured with the specified HTTP scheme, host, port, redirects and
|
||||
* {@link ClientHttpRequestInterceptor ClientHttpRequestInterceptors}
|
||||
* when accessing the Apache Geode or Pivotal GemFire Management REST API interface.
|
||||
*
|
||||
* @param clientCache reference to the {@link ClientCache}
|
||||
* @param scheme {@link String} specifying the HTTP scheme to use (e.g. HTTP or HTTPS).
|
||||
* @param host {@link String} containing the hostname of the GemFire/Geode Manager.
|
||||
* @param port integer value specifying the port on which the GemFire/Geode Manager HTTP Service is listening
|
||||
* for HTTP clients.
|
||||
* @param followRedirects boolean indicating whether HTTP Redirects (with HTTP Status Code 3xx) should be followed.
|
||||
* @param clientHttpRequestInterceptors {@link List} of {@link ClientHttpRequestInterceptor} used to intercept
|
||||
* and decorate the HTTP request and HTTP response.
|
||||
* @throws IllegalArgumentException if the {@link ClientCache} reference is {@literal null}.
|
||||
* @see org.apache.geode.cache.client.ClientCache
|
||||
* @see org.springframework.data.gemfire.config.support.RestTemplateConfigurer
|
||||
* @see org.springframework.http.client.ClientHttpRequestInterceptor
|
||||
* @see #newClientHttpRequestFactory(boolean)
|
||||
* @see #newRestOperations(ClientHttpRequestFactory, List, List)
|
||||
* @see #resolveManagementRestApiUrl(String, String, int)
|
||||
*/
|
||||
public RestHttpGemfireAdminTemplate(ClientCache clientCache, String scheme, String host, int port,
|
||||
boolean followRedirects, List<ClientHttpRequestInterceptor> clientHttpRequestInterceptors,
|
||||
List<RestTemplateConfigurer> restTemplateConfigurers) {
|
||||
|
||||
super(clientCache);
|
||||
|
||||
ClientHttpRequestFactory clientHttpRequestFactory = newClientHttpRequestFactory(followRedirects);
|
||||
|
||||
this.managementRestApiUrl = resolveManagementRestApiUrl(scheme, host, port);
|
||||
|
||||
this.restTemplate =
|
||||
newRestOperations(clientHttpRequestFactory, clientHttpRequestInterceptors, restTemplateConfigurers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance of {@link ClientHttpRequestFactory} to make HTTP client requests.
|
||||
*
|
||||
* @param followRedirects boolean value indicating whether HTTP redirects (with HTTP Status Code 3xx)
|
||||
* should be followed.
|
||||
* @return a new {@link ClientHttpRequestFactory}.
|
||||
* @see org.springframework.http.client.ClientHttpRequestFactory
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T extends ClientHttpRequestFactory> T newClientHttpRequestFactory(boolean followRedirects) {
|
||||
return (T) new FollowRedirectsSimpleClientHttpRequestFactory(followRedirects);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance of the Spring {@link RestTemplate} to perform REST API operations over HTTP.
|
||||
*
|
||||
* @param clientHttpRequestFactory {@link ClientHttpRequestFactory} used to construct HTTP request objects.
|
||||
* @param clientHttpRequestInterceptors {@link List} of {@link ClientHttpRequestInterceptor} used to intercept
|
||||
* and decorate the HTTP request and HTTP response.
|
||||
* @return a new instance of Spring's {@link RestTemplate}.
|
||||
* @see org.springframework.http.client.ClientHttpRequestInterceptor
|
||||
* @see org.springframework.http.client.SimpleClientHttpRequestFactory
|
||||
* @see org.springframework.web.client.RestOperations
|
||||
* @see org.springframework.web.client.RestTemplate
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T extends RestOperations> T newRestOperations(ClientHttpRequestFactory clientHttpRequestFactory,
|
||||
List<ClientHttpRequestInterceptor> clientHttpRequestInterceptors,
|
||||
List<RestTemplateConfigurer> restTemplateConfigurers) {
|
||||
|
||||
RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory);
|
||||
|
||||
Optional.ofNullable(clientHttpRequestInterceptors)
|
||||
.ifPresent(restTemplate.getInterceptors()::addAll);
|
||||
|
||||
CollectionUtils.nullSafeList(restTemplateConfigurers).stream()
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(configurer -> configurer.configure(restTemplate));
|
||||
|
||||
return (T) restTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the Pivotal GemFire or Apache Geode Management REST API URL given the host and port
|
||||
* of the GemFire/Geode Manager's embedded HTTP service.
|
||||
*
|
||||
* @param host {@link String} containing the hostname of the Manager running the embedded HTTP service
|
||||
* and Management REST API.
|
||||
* @param port integer specifying the port that the embedded Manager's HTTP service is listening on.
|
||||
* @return the resolved URL.
|
||||
*/
|
||||
String resolveManagementRestApiUrl(String scheme, String host, int port) {
|
||||
|
||||
return NetworkUtils.isValidNonEphemeralPort(port)
|
||||
? String.format(MANAGEMENT_REST_API_URL_TEMPLATE, scheme, host, port)
|
||||
: String.format(MANAGEMENT_REST_API_NO_PORT_URL_TEMPLATE, scheme, host);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the resolved GemFire/Geode Management REST API URL.
|
||||
*
|
||||
* @return a {@link String} containing the resolved GemFire/Geode Management REST API URL.
|
||||
*/
|
||||
protected String getManagementRestApiUrl() {
|
||||
return this.managementRestApiUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the {@link RestOperations} used to perform REST API calls.
|
||||
*
|
||||
* @return a reference to the {@link RestOperations} used to perform REST API calls.
|
||||
* @see org.springframework.web.client.RestOperations
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T extends RestOperations> T getRestOperations() {
|
||||
return (T) this.restTemplate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createIndex(IndexDefinition indexDefinition) {
|
||||
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
|
||||
httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
||||
|
||||
// HTTP Message Body
|
||||
MultiValueMap<String, Object> httpRequestParameters = new LinkedMultiValueMap<>();
|
||||
|
||||
httpRequestParameters.add("name", indexDefinition.getName());
|
||||
httpRequestParameters.add("expression", indexDefinition.getExpression());
|
||||
httpRequestParameters.add("region", indexDefinition.getFromClause());
|
||||
httpRequestParameters.add("type", indexDefinition.getIndexType().toString());
|
||||
|
||||
RequestEntity<MultiValueMap<String, Object>> requestEntity =
|
||||
new RequestEntity<>(httpRequestParameters, httpHeaders, HttpMethod.POST, resolveCreateIndexUri());
|
||||
|
||||
ResponseEntity<String> response = getRestOperations().exchange(requestEntity, String.class);
|
||||
|
||||
// TODO do something with the result; e.g. log when failure (or when not "OK")
|
||||
HttpStatus.OK.equals(response.getStatusCode());
|
||||
}
|
||||
|
||||
protected URI resolveCreateIndexUri() {
|
||||
return URI.create(getManagementRestApiUrl().concat("/indexes"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createRegion(RegionDefinition regionDefinition) {
|
||||
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
|
||||
httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
||||
|
||||
// HTTP Message Body
|
||||
MultiValueMap<String, Object> httpRequestParameters = new LinkedMultiValueMap<>();
|
||||
|
||||
httpRequestParameters.add("name", regionDefinition.getName());
|
||||
httpRequestParameters.add("type", regionDefinition.getRegionShortcut().toString());
|
||||
httpRequestParameters.add("skip-if-exists", String.valueOf(DEFAULT_CREATE_REGION_SKIP_IF_EXISTS));
|
||||
|
||||
RequestEntity<MultiValueMap<String, Object>> requestEntity =
|
||||
new RequestEntity<>(httpRequestParameters, httpHeaders, HttpMethod.POST, resolveCreateRegionUri());
|
||||
|
||||
ResponseEntity<String> response = getRestOperations().exchange(requestEntity, String.class);
|
||||
|
||||
// TODO do something with the result; e.g. log when failure (or when not "OK")
|
||||
HttpStatus.OK.equals(response.getStatusCode());
|
||||
}
|
||||
|
||||
protected URI resolveCreateRegionUri() {
|
||||
return URI.create(getManagementRestApiUrl().concat("/regions"));
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private boolean followRedirects = DEFAULT_HTTP_FOLLOW_REDIRECTS;
|
||||
|
||||
private int port = DEFAULT_PORT;
|
||||
|
||||
private ClientCache clientCache;
|
||||
|
||||
private final List<ClientHttpRequestInterceptor> clientHttpRequestInterceptors = new ArrayList<>();
|
||||
private final List<RestTemplateConfigurer> restTemplateConfigurers = new ArrayList<>();
|
||||
|
||||
private String hostname = DEFAULT_HOST;
|
||||
private String scheme = DEFAULT_SCHEME;
|
||||
|
||||
public Builder followRedirects(boolean followRedirects) {
|
||||
this.followRedirects = followRedirects;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder listenOn(int port) {
|
||||
|
||||
Assert.isTrue(NetworkUtils.isValidNonEphemeralPort(port),
|
||||
String.format(NetworkUtils.INVALID_NO_EPHEMERAL_PORT_MESSAGE, port));
|
||||
|
||||
this.port = port;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder on(String hostname) {
|
||||
this.hostname = StringUtils.hasText(hostname) ? hostname : DEFAULT_HOST;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder using(String scheme) {
|
||||
|
||||
scheme = String.valueOf(scheme).trim().toLowerCase();
|
||||
|
||||
Assert.isTrue(VALID_SCHEMES.contains(scheme),
|
||||
String.format("Scheme [%s] is not valid; must be 1 of %s", scheme, VALID_SCHEMES));
|
||||
|
||||
this.scheme = scheme;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder with(ClientCache clientCache) {
|
||||
this.clientCache = clientCache;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #withInterceptors(ClientHttpRequestInterceptor...)}.
|
||||
*/
|
||||
@Deprecated
|
||||
public Builder with(ClientHttpRequestInterceptor... clientHttpRequestInterceptors) {
|
||||
return withInterceptors(clientHttpRequestInterceptors);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #withInterceptors(List)}.
|
||||
*/
|
||||
@Deprecated
|
||||
public Builder with(List<ClientHttpRequestInterceptor> clientHttpRequestInterceptors) {
|
||||
return withInterceptors(clientHttpRequestInterceptors);
|
||||
}
|
||||
|
||||
public Builder withConfigurers(RestTemplateConfigurer... restTemplateConfigurers) {
|
||||
|
||||
List<RestTemplateConfigurer> restTemplateConfigurerList =
|
||||
Arrays.asList(ArrayUtils.nullSafeArray(restTemplateConfigurers, RestTemplateConfigurer.class));
|
||||
|
||||
return withConfigurers(restTemplateConfigurerList);
|
||||
}
|
||||
|
||||
public Builder withConfigurers(List<RestTemplateConfigurer> restTemplateConfigurers) {
|
||||
|
||||
this.restTemplateConfigurers.addAll(CollectionUtils.nullSafeList(restTemplateConfigurers));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withInterceptors(ClientHttpRequestInterceptor... clientHttpRequestInterceptors) {
|
||||
|
||||
List<ClientHttpRequestInterceptor> clientHttpRequestInterceptorList =
|
||||
Arrays.asList(ArrayUtils.nullSafeArray(clientHttpRequestInterceptors,
|
||||
ClientHttpRequestInterceptor.class));
|
||||
|
||||
return withInterceptors(clientHttpRequestInterceptorList);
|
||||
}
|
||||
|
||||
public Builder withInterceptors(List<ClientHttpRequestInterceptor> clientHttpRequestInterceptors) {
|
||||
|
||||
this.clientHttpRequestInterceptors.addAll(CollectionUtils.nullSafeList(clientHttpRequestInterceptors));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public RestHttpGemfireAdminTemplate build() {
|
||||
|
||||
return new RestHttpGemfireAdminTemplate(this.clientCache, this.scheme, this.hostname, this.port,
|
||||
this.followRedirects, this.clientHttpRequestInterceptors, this.restTemplateConfigurers);
|
||||
}
|
||||
}
|
||||
|
||||
public static class FollowRedirectsSimpleClientHttpRequestFactory extends SimpleClientHttpRequestFactory {
|
||||
|
||||
private final boolean followRedirects;
|
||||
|
||||
public FollowRedirectsSimpleClientHttpRequestFactory(boolean followRedirects) {
|
||||
this.followRedirects = followRedirects;
|
||||
}
|
||||
|
||||
public boolean isFollowRedirects() {
|
||||
return this.followRedirects;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
|
||||
|
||||
super.prepareConnection(connection, httpMethod);
|
||||
|
||||
connection.setInstanceFollowRedirects(isFollowRedirects());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,612 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package org.springframework.data.gemfire.config.annotation;
|
||||
|
||||
import static org.springframework.data.gemfire.CacheFactoryBean.JndiDataSource;
|
||||
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeList;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.apache.geode.cache.Cache;
|
||||
import org.apache.geode.cache.TransactionListener;
|
||||
import org.apache.geode.cache.TransactionWriter;
|
||||
import org.apache.geode.cache.client.ClientCache;
|
||||
import org.apache.geode.cache.server.CacheServer;
|
||||
import org.apache.geode.cache.util.GatewayConflictResolver;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
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.io.Resource;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.data.gemfire.CacheFactoryBean;
|
||||
import org.springframework.data.gemfire.config.annotation.support.AbstractAnnotationConfigSupport;
|
||||
import org.springframework.data.gemfire.config.support.CustomEditorBeanFactoryPostProcessor;
|
||||
import org.springframework.data.gemfire.config.support.DefinedIndexesApplicationListener;
|
||||
import org.springframework.data.gemfire.config.support.DiskStoreDirectoryBeanPostProcessor;
|
||||
import org.springframework.data.gemfire.util.PropertiesBuilder;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link AbstractCacheConfiguration} is an abstract base class for configuring either a Pivotal GemFire/Apache Geode
|
||||
* client or peer-based cache instance using Spring's Java-based, Annotation {@link Configuration} support.
|
||||
*
|
||||
* This class encapsulates configuration settings common to both Pivotal GemFire/Apache Geode
|
||||
* {@link org.apache.geode.cache.Cache peer caches}
|
||||
* and {@link org.apache.geode.cache.client.ClientCache client caches}.
|
||||
*
|
||||
* @author John Blum
|
||||
* @author Patrick Johnson
|
||||
* @see java.lang.annotation.Annotation
|
||||
* @see java.util.Properties
|
||||
* @see org.apache.geode.cache.Cache
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
* @see org.apache.geode.cache.client.ClientCache
|
||||
* @see org.apache.geode.cache.server.CacheServer
|
||||
* @see org.springframework.beans.factory.BeanFactory
|
||||
* @see org.springframework.beans.factory.config.BeanDefinition
|
||||
* @see org.springframework.beans.factory.support.BeanDefinitionBuilder
|
||||
* @see org.springframework.beans.factory.support.BeanDefinitionRegistry
|
||||
* @see org.springframework.context.annotation.Bean
|
||||
* @see org.springframework.context.annotation.Configuration
|
||||
* @see org.springframework.context.annotation.ImportAware
|
||||
* @see org.springframework.core.annotation.AnnotationAttributes
|
||||
* @see org.springframework.core.io.Resource
|
||||
* @see org.springframework.core.type.AnnotationMetadata
|
||||
* @see org.springframework.data.gemfire.CacheFactoryBean
|
||||
* @see org.springframework.data.gemfire.client.ClientCacheFactoryBean
|
||||
* @see org.springframework.data.gemfire.config.annotation.support.AbstractAnnotationConfigSupport
|
||||
* @see org.springframework.data.gemfire.config.support.CustomEditorBeanFactoryPostProcessor
|
||||
* @see org.springframework.data.gemfire.config.support.DefinedIndexesApplicationListener
|
||||
* @see org.springframework.data.gemfire.config.support.DiskStoreDirectoryBeanPostProcessor
|
||||
* @see org.springframework.data.gemfire.util.PropertiesBuilder
|
||||
* @since 1.9.0
|
||||
*/
|
||||
@Configuration
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class AbstractCacheConfiguration extends AbstractAnnotationConfigSupport implements ImportAware {
|
||||
|
||||
private static final AtomicBoolean CUSTOM_EDITORS_BEAN_FACTORY_POST_PROCESSOR_REGISTERED =
|
||||
new AtomicBoolean(false);
|
||||
|
||||
private static final AtomicBoolean DEFINED_INDEXES_APPLICATION_LISTENER_REGISTERED =
|
||||
new AtomicBoolean(false);
|
||||
|
||||
private static final AtomicBoolean DISK_STORE_DIRECTORY_BEAN_POST_PROCESSOR_REGISTERED =
|
||||
new AtomicBoolean(false);
|
||||
|
||||
protected static final boolean DEFAULT_CLOSE = true;
|
||||
protected static final boolean DEFAULT_COPY_ON_READ = false;
|
||||
protected static final boolean DEFAULT_USE_BEAN_FACTORY_LOCATOR = false;
|
||||
|
||||
protected static final int DEFAULT_MCAST_PORT = 0;
|
||||
|
||||
protected static final String DEFAULT_LOCATORS = "";
|
||||
protected static final String DEFAULT_LOG_LEVEL = "config";
|
||||
protected static final String DEFAULT_NAME = "SpringDataGemFireApplication";
|
||||
|
||||
private boolean close = DEFAULT_CLOSE;
|
||||
private boolean copyOnRead = DEFAULT_COPY_ON_READ;
|
||||
private boolean useBeanFactoryLocator = DEFAULT_USE_BEAN_FACTORY_LOCATOR;
|
||||
|
||||
private Integer mcastPort = DEFAULT_MCAST_PORT;
|
||||
|
||||
private Float criticalHeapPercentage;
|
||||
private Float criticalOffHeapPercentage;
|
||||
private Float evictionHeapPercentage;
|
||||
private Float evictionOffHeapPercentage;
|
||||
|
||||
private GatewayConflictResolver gatewayConflictResolver;
|
||||
|
||||
private List<JndiDataSource> jndiDataSources;
|
||||
private List<TransactionListener> transactionListeners;
|
||||
|
||||
private PropertiesBuilder customGemFireProperties = PropertiesBuilder.create();
|
||||
|
||||
private Resource cacheXml;
|
||||
|
||||
private String locators = DEFAULT_LOCATORS;
|
||||
private String logLevel = DEFAULT_LOG_LEVEL;
|
||||
private String name;
|
||||
private String startLocator;
|
||||
|
||||
private TransactionWriter transactionWriter;
|
||||
|
||||
/**
|
||||
* Returns a {@link Properties} object containing Pivotal GemFire/Apache Geode properties used to configure
|
||||
* the Pivotal GemFire/Apache Geode cache.
|
||||
*
|
||||
* The {@literal name} of the Pivotal GemFire/Apache Geode member/node in the cluster is set to a default,
|
||||
* pre-defined and descriptive value depending on the type of configuration meta-data applied.
|
||||
*
|
||||
* {@literal mcast-port} is set to {@literal 0} and {@literal locators} is set to an {@link String empty String},
|
||||
* which is necessary for {@link ClientCache cache client}-based applications. These values can be changed
|
||||
* and set accordingly for {@link Cache peer cache} and {@link CacheServer cache server} applications.
|
||||
*
|
||||
* Finally, the {@literal log-level} property defaults to {@literal config}.
|
||||
*
|
||||
* @return a {@link Properties} object containing Pivotal GemFire/Apache Geode properties used to configure
|
||||
* the Pivotal GemFire/Apache Geode cache instance.
|
||||
* @see <a href="https://gemfire.docs.pivotal.io/docs-gemfire/reference/topics/gemfire_properties.html">GemFire Properties</a>
|
||||
* @see java.util.Properties
|
||||
* @see #locators()
|
||||
* @see #logLevel()
|
||||
* @see #mcastPort()
|
||||
* @see #name()
|
||||
* @see #startLocator()
|
||||
*/
|
||||
@Bean
|
||||
protected Properties gemfireProperties() {
|
||||
|
||||
PropertiesBuilder gemfireProperties = PropertiesBuilder.create();
|
||||
|
||||
gemfireProperties.setProperty("name", name());
|
||||
gemfireProperties.setProperty("mcast-port", mcastPort());
|
||||
gemfireProperties.setProperty("log-level", logLevel());
|
||||
gemfireProperties.setProperty("locators", locators());
|
||||
gemfireProperties.setProperty("start-locator", startLocator());
|
||||
gemfireProperties.add(this.customGemFireProperties);
|
||||
|
||||
return gemfireProperties.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setImportMetadata(AnnotationMetadata importMetadata) {
|
||||
|
||||
configureInfrastructure(importMetadata);
|
||||
configureCache(importMetadata);
|
||||
configureOptional(importMetadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures Spring container infrastructure components and beans used by Spring Data GemFire
|
||||
* to enable Pivotal GemFire or Apache Geode to function properly inside a Spring context.
|
||||
*
|
||||
* @param importMetadata {@link AnnotationMetadata} containing annotation meta-data
|
||||
* for the Spring Data GemFire cache application class.
|
||||
* @see org.springframework.core.type.AnnotationMetadata
|
||||
*/
|
||||
protected void configureInfrastructure(AnnotationMetadata importMetadata) {
|
||||
|
||||
registerCustomEditorBeanFactoryPostProcessor(importMetadata);
|
||||
registerDefinedIndexesApplicationListener(importMetadata);
|
||||
registerDiskStoreDirectoryBeanPostProcessor(importMetadata);
|
||||
}
|
||||
|
||||
private void registerCustomEditorBeanFactoryPostProcessor(AnnotationMetadata importMetadata) {
|
||||
|
||||
if (CUSTOM_EDITORS_BEAN_FACTORY_POST_PROCESSOR_REGISTERED.compareAndSet(false, true)) {
|
||||
register(BeanDefinitionBuilder.rootBeanDefinition(CustomEditorBeanFactoryPostProcessor.class)
|
||||
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
|
||||
}
|
||||
}
|
||||
|
||||
private void registerDefinedIndexesApplicationListener(AnnotationMetadata importMetadata) {
|
||||
|
||||
if (DEFINED_INDEXES_APPLICATION_LISTENER_REGISTERED.compareAndSet(false, true)) {
|
||||
register(BeanDefinitionBuilder.rootBeanDefinition(DefinedIndexesApplicationListener.class)
|
||||
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
|
||||
}
|
||||
}
|
||||
|
||||
private void registerDiskStoreDirectoryBeanPostProcessor(AnnotationMetadata importMetadata) {
|
||||
|
||||
if (DISK_STORE_DIRECTORY_BEAN_POST_PROCESSOR_REGISTERED.compareAndSet(false, true)) {
|
||||
register(BeanDefinitionBuilder.rootBeanDefinition(DiskStoreDirectoryBeanPostProcessor.class)
|
||||
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures Pivotal GemFire/Apache Geode cache specific settings.
|
||||
*
|
||||
* @param importMetadata {@link AnnotationMetadata} containing the cache meta-data used to configure the cache.
|
||||
* @see org.springframework.core.type.AnnotationMetadata
|
||||
*/
|
||||
protected void configureCache(AnnotationMetadata importMetadata) {
|
||||
|
||||
if (isClientPeerOrServerCacheApplication(importMetadata)) {
|
||||
|
||||
AnnotationAttributes cacheMetadataAttributes = getAnnotationAttributes(importMetadata);
|
||||
|
||||
setCopyOnRead(resolveProperty(cacheProperty("copy-on-read"),
|
||||
Boolean.TRUE.equals(cacheMetadataAttributes.get("copyOnRead"))));
|
||||
|
||||
Optional.ofNullable(resolveProperty(cacheProperty("critical-heap-percentage"), (Float) null))
|
||||
.ifPresent(this::setCriticalHeapPercentage);
|
||||
|
||||
Optional.ofNullable((Float) cacheMetadataAttributes.get("criticalHeapPercentage"))
|
||||
.filter(it -> getCriticalHeapPercentage() == null)
|
||||
.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);
|
||||
|
||||
Optional.ofNullable((Float) cacheMetadataAttributes.get("evictionHeapPercentage"))
|
||||
.filter(it -> getEvictionHeapPercentage() == null)
|
||||
.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")));
|
||||
|
||||
setName(resolveProperty(propertyName("name"),
|
||||
resolveProperty(cacheProperty("name"),
|
||||
(String) cacheMetadataAttributes.get("name"))));
|
||||
|
||||
setUseBeanFactoryLocator(resolveProperty(propertyName("use-bean-factory-locator"),
|
||||
Boolean.TRUE.equals(cacheMetadataAttributes.get("useBeanFactoryLocator"))));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback method allowing developers to configure other cache or application specific configuration settings.
|
||||
*
|
||||
* @param importMetadata {@link AnnotationMetadata} containing meta-data used to configure the cache or application.
|
||||
* @see org.springframework.core.type.AnnotationMetadata
|
||||
*/
|
||||
protected void configureOptional(AnnotationMetadata importMetadata) { }
|
||||
|
||||
/**
|
||||
* Constructs a new, initialized instance of {@link CacheFactoryBean} based on the Spring application's
|
||||
* cache type preference (i.e. client or peer), which is expressed via the appropriate annotation.
|
||||
*
|
||||
* Use the {@link ClientCacheApplication} Annotation to construct a {@link ClientCache cache client} application.
|
||||
*
|
||||
* Use the {@link PeerCacheApplication} Annotation to construct a {@link Cache peer cache} application.
|
||||
*
|
||||
* @param <T> {@link Class} specific sub-type of the {@link CacheFactoryBean}.
|
||||
* @return a new instance of the appropriate {@link CacheFactoryBean} given the Spring application's
|
||||
* cache type preference (i.e client or peer), (e.g. {@link ClientCacheApplication}
|
||||
* or {@link PeerCacheApplication}).
|
||||
* @see org.springframework.data.gemfire.client.ClientCacheFactoryBean
|
||||
* @see org.springframework.data.gemfire.CacheFactoryBean
|
||||
* @see #configureCacheFactoryBean(CacheFactoryBean)
|
||||
* @see #newCacheFactoryBean()
|
||||
*/
|
||||
protected <T extends CacheFactoryBean> T constructCacheFactoryBean() {
|
||||
return configureCacheFactoryBean(this.newCacheFactoryBean());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new, uninitialized instance of {@link CacheFactoryBean} based on the Spring application's
|
||||
* cache type preference (i.e. client or peer), which is expressed via the appropriate annotation.
|
||||
*
|
||||
* Use the {@link ClientCacheApplication} Annotation to construct a {@link ClientCache cache client} application.
|
||||
*
|
||||
* Use the {@link PeerCacheApplication} Annotation to construct a {@link Cache peer cache} application.
|
||||
*
|
||||
* @param <T> {@link Class} specific sub-type of the {@link CacheFactoryBean}.
|
||||
* @return a new instance of the appropriate {@link CacheFactoryBean} given the Spring application's
|
||||
* cache type preference (i.e client or peer), (e.g. {@link ClientCacheApplication}
|
||||
* or {@link PeerCacheApplication}).
|
||||
* @see org.springframework.data.gemfire.client.ClientCacheFactoryBean
|
||||
* @see org.springframework.data.gemfire.CacheFactoryBean
|
||||
*/
|
||||
protected abstract <T extends CacheFactoryBean> T newCacheFactoryBean();
|
||||
|
||||
/**
|
||||
* Configures the {@link CacheFactoryBean} with common cache configuration settings.
|
||||
*
|
||||
* @param <T> {@link Class} specific sub-type of the {@link CacheFactoryBean}.
|
||||
* @param gemfireCache {@link CacheFactoryBean} to configure.
|
||||
* @return the given {@link CacheFactoryBean} with common cache configuration settings applied.
|
||||
* @see org.springframework.data.gemfire.client.ClientCacheFactoryBean
|
||||
* @see org.springframework.data.gemfire.CacheFactoryBean
|
||||
*/
|
||||
protected <T extends CacheFactoryBean> T configureCacheFactoryBean(T gemfireCache) {
|
||||
|
||||
gemfireCache.setBeanClassLoader(getBeanClassLoader());
|
||||
gemfireCache.setBeanFactory(getBeanFactory());
|
||||
gemfireCache.setCacheXml(getCacheXml());
|
||||
gemfireCache.setClose(isClose());
|
||||
gemfireCache.setCopyOnRead(getCopyOnRead());
|
||||
gemfireCache.setCriticalHeapPercentage(getCriticalHeapPercentage());
|
||||
gemfireCache.setCriticalOffHeapPercentage(getCriticalOffHeapPercentage());
|
||||
gemfireCache.setEvictionHeapPercentage(getEvictionHeapPercentage());
|
||||
gemfireCache.setEvictionOffHeapPercentage(getEvictionOffHeapPercentage());
|
||||
gemfireCache.setGatewayConflictResolver(getGatewayConflictResolver());
|
||||
gemfireCache.setJndiDataSources(getJndiDataSources());
|
||||
gemfireCache.setProperties(gemfireProperties());
|
||||
gemfireCache.setTransactionListeners(getTransactionListeners());
|
||||
gemfireCache.setTransactionWriter(getTransactionWriter());
|
||||
gemfireCache.setUseBeanFactoryLocator(useBeanFactoryLocator());
|
||||
|
||||
return gemfireCache;
|
||||
}
|
||||
|
||||
// TODO: REVIEW JAVADOC FROM HERE
|
||||
|
||||
/**
|
||||
* Determines whether this is a GemFire {@link org.apache.geode.cache.server.CacheServer} application,
|
||||
* which is indicated by the presence of the {@link CacheServerApplication} annotation on a Spring application
|
||||
* {@link org.springframework.context.annotation.Configuration @Configuration} class.
|
||||
*
|
||||
* @param importMetadata {@link AnnotationMetadata} containing application configuration meta-data
|
||||
* from the annotations used to configure the Spring application.
|
||||
* @return a boolean value indicating whether this is a GemFire cache server application.
|
||||
* @see org.springframework.data.gemfire.config.annotation.CacheServerApplication
|
||||
* @see #isTypedCacheApplication(Class, AnnotationMetadata)
|
||||
*/
|
||||
protected boolean isCacheServerApplication(AnnotationMetadata importMetadata) {
|
||||
return isTypedCacheApplication(CacheServerApplication.class, importMetadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether this is a GemFire {@link org.apache.geode.cache.client.ClientCache} application,
|
||||
* which is indicated by the presence of the {@link ClientCacheApplication} annotation on a Spring application
|
||||
* {@link org.springframework.context.annotation.Configuration @Configuration} class.
|
||||
*
|
||||
* @param importMetadata {@link AnnotationMetadata} containing application configuration meta-data
|
||||
* from the annotations used to configure the Spring application.
|
||||
* @return a boolean value indicating whether this is a GemFire cache client application.
|
||||
* @see org.springframework.data.gemfire.config.annotation.ClientCacheApplication
|
||||
* @see #isTypedCacheApplication(Class, AnnotationMetadata)
|
||||
*/
|
||||
protected boolean isClientCacheApplication(AnnotationMetadata importMetadata) {
|
||||
return isTypedCacheApplication(ClientCacheApplication.class, importMetadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether this is a GemFire peer {@link org.apache.geode.cache.Cache} application,
|
||||
* which is indicated by the presence of the {@link PeerCacheApplication} annotation on a Spring application
|
||||
* {@link org.springframework.context.annotation.Configuration @Configuration} class.
|
||||
*
|
||||
* @param importMetadata {@link AnnotationMetadata} containing application configuration meta-data
|
||||
* from the annotations used to configure the Spring application.
|
||||
* @return a boolean value indicating whether this is a GemFire peer cache application.
|
||||
* @see org.springframework.data.gemfire.config.annotation.PeerCacheApplication
|
||||
* @see #isTypedCacheApplication(Class, AnnotationMetadata)
|
||||
*/
|
||||
protected boolean isPeerCacheApplication(AnnotationMetadata importMetadata) {
|
||||
return isTypedCacheApplication(PeerCacheApplication.class, importMetadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether this Spring application is annotated with the given GemFire cache type annotation.
|
||||
*
|
||||
* @param annotationType {@link Annotation} cache type.
|
||||
* @param importMetadata {@link AnnotationMetadata} containing application configuration meta-data
|
||||
* from the annotations used to configure the Spring application.
|
||||
* @return a boolean value indicating if this Spring application is annotated with the given GemFire
|
||||
* cache type annotation.
|
||||
* @see org.springframework.core.type.AnnotationMetadata
|
||||
* @see java.lang.annotation.Annotation
|
||||
* @see #getAnnotationTypeName()
|
||||
* @see #getAnnotationType()
|
||||
*/
|
||||
protected boolean isTypedCacheApplication(Class<? extends Annotation> annotationType,
|
||||
AnnotationMetadata importMetadata) {
|
||||
|
||||
return annotationType.equals(getAnnotationType()) && importMetadata.hasAnnotation(getAnnotationTypeName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether this is a GemFire {@link org.apache.geode.cache.server.CacheServer} or
|
||||
* {@link org.apache.geode.cache.Cache peer cache} application, which is indicated by the presence
|
||||
* of either the {@link CacheServerApplication} annotation or the {@link PeerCacheApplication} annotation
|
||||
* on a Spring application {@link org.springframework.context.annotation.Configuration @Configuration} class.
|
||||
*
|
||||
* @param importMetadata {@link AnnotationMetadata} containing application configuration meta-data
|
||||
* from the annotations used to configure the Spring application.
|
||||
* @return a boolean value indicating whether this is a GemFire cache server or peer cache application.
|
||||
* @see org.springframework.core.type.AnnotationMetadata
|
||||
* @see org.springframework.data.gemfire.config.annotation.CacheServerApplication
|
||||
* @see org.springframework.data.gemfire.config.annotation.PeerCacheApplication
|
||||
* @see #isCacheServerApplication(AnnotationMetadata)
|
||||
* @see #isPeerCacheApplication(AnnotationMetadata)
|
||||
*/
|
||||
protected boolean isCacheServerOrPeerCacheApplication(AnnotationMetadata importMetadata) {
|
||||
return isCacheServerApplication(importMetadata) || isPeerCacheApplication(importMetadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether this Spring application is a {@link org.apache.geode.cache.server.CacheServer},
|
||||
* {@link org.apache.geode.cache.client.ClientCache} or a {@link org.apache.geode.cache.Cache} application.
|
||||
*
|
||||
* @param importMetadata {@link AnnotationMetadata} containing application configuration meta-data
|
||||
* from the class type-level annotations used to configure the Spring application.
|
||||
* @return a boolean value indicating whether this is a GemFire cache server, client cache or peer cache
|
||||
* Spring application.
|
||||
* @see #isCacheServerApplication(AnnotationMetadata)
|
||||
* @see #isClientCacheApplication(AnnotationMetadata)
|
||||
* @see #isPeerCacheApplication(AnnotationMetadata)
|
||||
*/
|
||||
protected boolean isClientPeerOrServerCacheApplication(AnnotationMetadata importMetadata) {
|
||||
|
||||
return isCacheServerApplication(importMetadata)
|
||||
|| isClientCacheApplication(importMetadata)
|
||||
|| isPeerCacheApplication(importMetadata);
|
||||
}
|
||||
|
||||
void setCacheXml(Resource cacheXml) {
|
||||
this.cacheXml = cacheXml;
|
||||
}
|
||||
|
||||
protected Resource getCacheXml() {
|
||||
return this.cacheXml;
|
||||
}
|
||||
|
||||
void setClose(boolean close) {
|
||||
this.close = close;
|
||||
}
|
||||
|
||||
protected boolean isClose() {
|
||||
return this.close;
|
||||
}
|
||||
|
||||
void setCopyOnRead(boolean copyOnRead) {
|
||||
this.copyOnRead = copyOnRead;
|
||||
}
|
||||
|
||||
protected boolean getCopyOnRead() {
|
||||
return this.copyOnRead;
|
||||
}
|
||||
|
||||
void setCriticalHeapPercentage(Float criticalHeapPercentage) {
|
||||
this.criticalHeapPercentage = criticalHeapPercentage;
|
||||
}
|
||||
|
||||
protected Float getCriticalHeapPercentage() {
|
||||
return this.criticalHeapPercentage;
|
||||
}
|
||||
|
||||
void setCriticalOffHeapPercentage(Float criticalOffHeapPercentage) {
|
||||
this.criticalOffHeapPercentage = criticalOffHeapPercentage;
|
||||
}
|
||||
|
||||
protected Float getCriticalOffHeapPercentage() {
|
||||
return this.criticalOffHeapPercentage;
|
||||
}
|
||||
|
||||
void setEvictionHeapPercentage(Float evictionHeapPercentage) {
|
||||
this.evictionHeapPercentage = evictionHeapPercentage;
|
||||
}
|
||||
|
||||
protected Float getEvictionHeapPercentage() {
|
||||
return this.evictionHeapPercentage;
|
||||
}
|
||||
|
||||
void setEvictionOffHeapPercentage(Float evictionOffHeapPercentage) {
|
||||
this.evictionOffHeapPercentage = evictionOffHeapPercentage;
|
||||
}
|
||||
|
||||
protected Float getEvictionOffHeapPercentage() {
|
||||
return this.evictionOffHeapPercentage;
|
||||
}
|
||||
|
||||
void setGatewayConflictResolver(GatewayConflictResolver gatewayConflictResolver) {
|
||||
this.gatewayConflictResolver = gatewayConflictResolver;
|
||||
}
|
||||
|
||||
protected GatewayConflictResolver getGatewayConflictResolver() {
|
||||
return this.gatewayConflictResolver;
|
||||
}
|
||||
|
||||
void setJndiDataSources(List<JndiDataSource> jndiDataSources) {
|
||||
this.jndiDataSources = jndiDataSources;
|
||||
}
|
||||
|
||||
protected List<CacheFactoryBean.JndiDataSource> getJndiDataSources() {
|
||||
return nullSafeList(this.jndiDataSources);
|
||||
}
|
||||
|
||||
void setLocators(String locators) {
|
||||
this.locators = locators;
|
||||
this.mcastPort = DEFAULT_MCAST_PORT;
|
||||
}
|
||||
|
||||
protected String locators() {
|
||||
return this.locators;
|
||||
}
|
||||
|
||||
void setLogLevel(String logLevel) {
|
||||
this.logLevel = logLevel;
|
||||
}
|
||||
|
||||
protected String logLevel() {
|
||||
return Optional.ofNullable(this.logLevel).orElse(DEFAULT_LOG_LEVEL);
|
||||
}
|
||||
|
||||
void setMcastPort(Integer mcastPort) {
|
||||
this.mcastPort = mcastPort;
|
||||
this.locators = DEFAULT_LOCATORS;
|
||||
}
|
||||
|
||||
protected Integer mcastPort() {
|
||||
return Optional.ofNullable(mcastPort).orElse(DEFAULT_MCAST_PORT);
|
||||
}
|
||||
|
||||
void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
protected String name() {
|
||||
return Optional.ofNullable(this.name).filter(StringUtils::hasText).orElseGet(this::toString);
|
||||
}
|
||||
|
||||
void setStartLocator(String startLocator) {
|
||||
this.startLocator = startLocator;
|
||||
}
|
||||
|
||||
protected String startLocator() {
|
||||
return this.startLocator;
|
||||
}
|
||||
|
||||
void setTransactionListeners(List<TransactionListener> transactionListeners) {
|
||||
this.transactionListeners = transactionListeners;
|
||||
}
|
||||
|
||||
protected List<TransactionListener> getTransactionListeners() {
|
||||
return nullSafeList(this.transactionListeners);
|
||||
}
|
||||
|
||||
void setTransactionWriter(TransactionWriter transactionWriter) {
|
||||
this.transactionWriter = transactionWriter;
|
||||
}
|
||||
|
||||
protected TransactionWriter getTransactionWriter() {
|
||||
return this.transactionWriter;
|
||||
}
|
||||
|
||||
void setUseBeanFactoryLocator(boolean useBeanFactoryLocator) {
|
||||
this.useBeanFactoryLocator = useBeanFactoryLocator;
|
||||
}
|
||||
|
||||
protected boolean useBeanFactoryLocator() {
|
||||
return this.useBeanFactoryLocator;
|
||||
}
|
||||
|
||||
public void add(Properties gemfireProperties) {
|
||||
customGemFireProperties.add(gemfireProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return DEFAULT_NAME;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user