DATAGEODE-192 - Add support for HTTPS and Follow Redirects when using @EnableClusterConfiguration.

This commit is contained in:
John Blum
2019-05-18 19:05:07 -07:00
parent 791e47ed91
commit 97101309a3
10 changed files with 1519 additions and 98 deletions

View File

@@ -13,25 +13,38 @@
* 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.Optional;
import java.util.stream.Collectors;
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.util.ArrayUtils;
import org.springframework.data.gemfire.util.CollectionUtils;
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;
@@ -44,69 +57,125 @@ import org.springframework.web.client.RestTemplate;
* 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 CREATE_REGION_SKIP_IF_EXISTS_DEFAULT = true;
protected static final boolean DEFAULT_CREATE_REGION_SKIP_IF_EXISTS = true;
protected static final boolean DEFAULT_HTTP_FOLLOW_REDIRECTS = true;
protected static final int DEFAULT_PORT = 7070;
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_URL_TEMPLATE = "http://%1$s:%2$d/gemfire/v1/";
protected static final List<String> VALID_SCHEMES = Arrays.asList(HTTP_SCHEME, HTTPS_SCHEME);
private final RestOperations restTemplate;
private final String managementRestApiUrl;
/**
* Constructs an instance of the {@link RestHttpGemfireAdminTemplate} initialized with
* the given {@link ClientCache} and configured with the default host and port when accessing
* the GemFire or Geode Management REST API interface.
* Constructs a new instance of {@link RestHttpGemfireAdminTemplate} initialized with the given {@link ClientCache}
* and configured with the default 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 the {@link ClientCache} reference is {@literal null}.
* @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_HOST, DEFAULT_PORT);
this(clientCache, DEFAULT_SCHEME, DEFAULT_HOST, DEFAULT_PORT, DEFAULT_HTTP_FOLLOW_REDIRECTS,
Collections.emptyList());
}
/**
* Constructs an instance of the {@link RestHttpGemfireAdminTemplate} initialized with
* the given {@link ClientCache} and configured with the specified host and port when accessing
* the GemFire or Geode Management REST API interface.
* 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
* @see #newClientHttpRequestFactory(boolean)
* @see #newRestOperations(ClientHttpRequestFactory, List)
* @see #resolveManagementRestApiUrl(String, String, int)
*/
public RestHttpGemfireAdminTemplate(ClientCache clientCache, String host, int port) {
public RestHttpGemfireAdminTemplate(ClientCache clientCache, String scheme, String host, int port,
boolean followRedirects, List<ClientHttpRequestInterceptor> clientHttpRequestInterceptors) {
super(clientCache);
this.restTemplate = newRestOperations();
this.managementRestApiUrl = resolveManagementRestApiUrl(host, port);
ClientHttpRequestFactory clientHttpRequestFactory = newClientHttpRequestFactory(followRedirects);
this.managementRestApiUrl = resolveManagementRestApiUrl(scheme, host, port);
this.restTemplate = newRestOperations(clientHttpRequestFactory, clientHttpRequestInterceptors);
}
/**
* 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
*/
RestOperations newRestOperations() {
return new RestTemplate(new SimpleClientHttpRequestFactory());
@SuppressWarnings("unchecked")
protected <T extends RestOperations> T newRestOperations(ClientHttpRequestFactory clientHttpRequestFactory,
List<ClientHttpRequestInterceptor> clientHttpRequestInterceptors) {
RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory);
Optional.ofNullable(clientHttpRequestInterceptors)
.ifPresent(restTemplate.getInterceptors()::addAll);
return (T) restTemplate;
}
/**
@@ -118,8 +187,8 @@ public class RestHttpGemfireAdminTemplate extends FunctionGemfireAdminTemplate {
* @param port integer specifying the port that the embedded Manager's HTTP service is listening on.
* @return the resolved URL.
*/
private String resolveManagementRestApiUrl(String host, int port) {
return String.format(MANAGEMENT_REST_API_URL_TEMPLATE, host, port);
String resolveManagementRestApiUrl(String scheme, String host, int port) {
return String.format(MANAGEMENT_REST_API_URL_TEMPLATE, scheme, host, port);
}
/**
@@ -137,8 +206,9 @@ public class RestHttpGemfireAdminTemplate extends FunctionGemfireAdminTemplate {
* @return a reference to the {@link RestOperations} used to perform REST API calls.
* @see org.springframework.web.client.RestOperations
*/
protected RestOperations getRestOperations() {
return this.restTemplate;
@SuppressWarnings("unchecked")
protected <T extends RestOperations> T getRestOperations() {
return (T) this.restTemplate;
}
@Override
@@ -149,24 +219,24 @@ public class RestHttpGemfireAdminTemplate extends FunctionGemfireAdminTemplate {
httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
// HTTP Message Body
MultiValueMap<String, Object> requestParameters = new LinkedMultiValueMap<>();
MultiValueMap<String, Object> httpRequestParameters = new LinkedMultiValueMap<>();
requestParameters.add("name", indexDefinition.getName());
requestParameters.add("expression", indexDefinition.getExpression());
requestParameters.add("region", indexDefinition.getFromClause());
requestParameters.add("type", indexDefinition.getIndexType().toString());
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<>(requestParameters, httpHeaders, HttpMethod.POST, resolveCreateIndexUri());
new RequestEntity<>(httpRequestParameters, httpHeaders, HttpMethod.POST, resolveCreateIndexUri());
ResponseEntity<String> response = getRestOperations().exchange(requestEntity, String.class);
// TODO do something with result; e.g. log when failure (or when not "OK")
// 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"));
return URI.create(getManagementRestApiUrl().concat("/indexes"));
}
@Override
@@ -177,22 +247,113 @@ public class RestHttpGemfireAdminTemplate extends FunctionGemfireAdminTemplate {
httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
// HTTP Message Body
MultiValueMap<String, Object> requestParameters = new LinkedMultiValueMap<>();
MultiValueMap<String, Object> httpRequestParameters = new LinkedMultiValueMap<>();
requestParameters.add("name", regionDefinition.getName());
requestParameters.add("type", regionDefinition.getRegionShortcut().toString());
requestParameters.add("skip-if-exists", String.valueOf(CREATE_REGION_SKIP_IF_EXISTS_DEFAULT));
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<>(requestParameters, httpHeaders, HttpMethod.POST, resolveCreateRegionUri());
new RequestEntity<>(httpRequestParameters, httpHeaders, HttpMethod.POST, resolveCreateRegionUri());
ResponseEntity<String> response = getRestOperations().exchange(requestEntity, String.class);
// TODO do something with result; e.g. log when failure (or when not "OK")
// 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"));
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 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(port > 0 && port < 65536,
String.format("Port [%d] must be greater than 0 and less than 65536", 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;
}
public Builder with(ClientHttpRequestInterceptor... clientHttpRequestInterceptors) {
clientHttpRequestInterceptors =
ArrayUtils.nullSafeArray(clientHttpRequestInterceptors, ClientHttpRequestInterceptor.class);
return with(Arrays.stream(clientHttpRequestInterceptors).collect(Collectors.toList()));
}
public Builder with(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);
}
}
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());
}
}
}

View File

@@ -13,26 +13,34 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire.config.annotation;
import static java.util.stream.StreamSupport.stream;
import static org.springframework.data.gemfire.util.CacheUtils.isClient;
import static org.springframework.data.gemfire.util.CacheUtils.isPeer;
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeMap;
import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionShortcut;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.query.Index;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.OrderComparator;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.gemfire.config.admin.GemfireAdminOperations;
import org.springframework.data.gemfire.config.admin.remote.FunctionGemfireAdminTemplate;
@@ -49,7 +57,9 @@ import org.springframework.data.gemfire.config.schema.support.IndexDefiner;
import org.springframework.data.gemfire.config.schema.support.RegionDefiner;
import org.springframework.data.gemfire.config.support.AbstractSmartLifecycle;
import org.springframework.data.gemfire.util.CacheUtils;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Spring {@link Configuration @Configuration} class defining Spring beans that will record the creation of
@@ -57,32 +67,54 @@ import org.springframework.util.Assert;
* as Spring beans in the Spring container.
*
* @author John Blum
* @see java.lang.annotation.Annotation
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.Region
* @see org.apache.geode.cache.client.ClientCache
* @see org.apache.geode.cache.query.Index
* @see org.springframework.beans.factory.ListableBeanFactory
* @see org.springframework.beans.factory.config.BeanPostProcessor
* @see org.springframework.context.annotation.Bean
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.context.annotation.ImportAware
* @see org.springframework.context.event.EventListener
* @see org.springframework.core.annotation.AnnotationAttributes
* @see org.springframework.core.env.Environment
* @see org.springframework.core.type.AnnotationMetadata
* @see org.springframework.data.gemfire.config.admin.GemfireAdminOperations
* @see org.springframework.data.gemfire.config.annotation.support.AbstractAnnotationConfigSupport
* @see org.springframework.http.client.ClientHttpRequestInterceptor
* @since 2.0.0
*/
@Configuration
@SuppressWarnings("unused")
public class ClusterConfigurationConfiguration extends AbstractAnnotationConfigSupport implements ImportAware {
protected static final boolean DEFAULT_HTTP_FOLLOW_REDIRECTS = false;
protected static final boolean DEFAULT_MANAGEMENT_USE_HTTP = false;
protected static final boolean DEFAULT_MANAGEMENT_REQUIRE_HTTPS = true;
protected static final int DEFAULT_MANAGEMENT_HTTP_PORT = HttpServiceConfiguration.DEFAULT_HTTP_SERVICE_PORT;
protected static final String DEFAULT_MANAGEMENT_HTTP_HOST = "localhost";
protected static final String HTTP_FOLLOW_REDIRECTS_PROPERTY =
"spring.data.gemfire.management.http.follow-redirects";
protected static final String HTTP_SCHEME = "http";
protected static final String HTTPS_SCHEME = "https";
private static final RegionShortcut DEFAULT_SERVER_REGION_SHORTCUT = RegionDefinition.DEFAULT_REGION_SHORTCUT;
private Boolean requireHttps = DEFAULT_MANAGEMENT_REQUIRE_HTTPS;
private Boolean useHttp = DEFAULT_MANAGEMENT_USE_HTTP;
private Integer managementHttpPort = DEFAULT_MANAGEMENT_HTTP_PORT;
@Autowired(required = false)
private GemfireAdminOperations gemfireAdminOperations;
@Autowired(required = false)
private List<ClientHttpRequestInterceptor> clientHttpRequestInterceptors;
private RegionShortcut serverRegionShortcut;
private String managementHttpHost = DEFAULT_MANAGEMENT_HTTP_HOST;
@@ -97,7 +129,7 @@ public class ClusterConfigurationConfiguration extends AbstractAnnotationConfigS
}
protected Optional<String> getManagementHttpHost() {
return Optional.ofNullable(this.managementHttpHost);
return Optional.ofNullable(this.managementHttpHost).filter(StringUtils::hasText);
}
protected String resolveManagementHttpHost() {
@@ -116,6 +148,18 @@ public class ClusterConfigurationConfiguration extends AbstractAnnotationConfigS
return getManagementHttpPort().orElse(DEFAULT_MANAGEMENT_HTTP_PORT);
}
protected void setManagementRequireHttps(Boolean requireHttps) {
this.requireHttps = requireHttps;
}
protected Optional<Boolean> getManagementRequireHttps() {
return Optional.ofNullable(this.requireHttps);
}
protected boolean resolveManagementRequireHttps() {
return getManagementRequireHttps().orElse(DEFAULT_MANAGEMENT_REQUIRE_HTTPS);
}
protected void setManagementUseHttp(Boolean useHttp) {
this.useHttp = useHttp;
}
@@ -153,6 +197,9 @@ public class ClusterConfigurationConfiguration extends AbstractAnnotationConfigS
setManagementHttpPort(resolveProperty(managementProperty("http.port"),
enableClusterConfigurationAttributes.<Integer>getNumber("port")));
setManagementRequireHttps(resolveProperty(managementProperty("require-https"),
enableClusterConfigurationAttributes.getBoolean("requireHttps")));
setManagementUseHttp(resolveProperty(managementProperty("use-http"),
enableClusterConfigurationAttributes.getBoolean("useHttp")));
@@ -162,14 +209,15 @@ public class ClusterConfigurationConfiguration extends AbstractAnnotationConfigS
}
@Bean
public ClusterSchemaObjectInitializer gemfireClusterSchemaObjectInitializer(GemFireCache gemfireCache) {
public ClusterSchemaObjectInitializer gemfireClusterSchemaObjectInitializer(Environment environment,
GemFireCache gemfireCache) {
return Optional.ofNullable(gemfireCache)
.filter(CacheUtils::isClient)
.map(clientCache -> {
SchemaObjectContext schemaObjectContext = SchemaObjectContext.from(gemfireCache)
.with(newGemfireAdminOperations((ClientCache) clientCache))
.with(resolveGemfireAdminOperations(environment, (ClientCache) clientCache))
.with(newSchemaObjectCollector())
.with(newSchemaObjectDefiner());
@@ -180,24 +228,92 @@ public class ClusterConfigurationConfiguration extends AbstractAnnotationConfigS
}
/**
* Constructs an instance of {@link GemfireAdminOperations} to perform administrative, schema functions
* Attempts to resolve a {@link List} of {@link ClientHttpRequestInterceptor} beans in the Spring
* {@link ApplicationContext}.
*
* @return a {@link List} of declared and registered {@link ClientHttpRequestInterceptor} beans.
* @see org.springframework.http.client.ClientHttpRequestInterceptor
* @see #getBeanFactory()
* @see java.util.List
*/
protected List<ClientHttpRequestInterceptor> resolveClientHttpRequestInterceptors() {
return Optional.ofNullable(this.clientHttpRequestInterceptors)
.orElseGet(() ->
Optional.of(getBeanFactory())
.filter(ListableBeanFactory.class::isInstance)
.map(ListableBeanFactory.class::cast)
.map(beanFactory -> {
Map<String, ClientHttpRequestInterceptor> beansOfType = beanFactory
.getBeansOfType(ClientHttpRequestInterceptor.class, true, false);
return nullSafeMap(beansOfType).values().stream().collect(Collectors.toList());
})
.orElseGet(Collections::emptyList));
}
/**
* Attempts to resolve the the {@link GemfireAdminOperations} object from the Spring {@link ApplicationContext}
* which is used to create Apache Geode or Pivotal GemFire schema objects.
*
* @param environment reference to the {@link Environment}.
* @param clientCache reference to the {@link ClientCache}.
* @return the resovled {@link GemfireAdminOperations} instance.
* @see org.springframework.core.env.Environment
* @see org.springframework.data.gemfire.config.admin.GemfireAdminOperations
* @see org.apache.geode.cache.client.ClientCache
* @see #newGemfireAdminOperations(Environment, ClientCache)
*/
protected GemfireAdminOperations resolveGemfireAdminOperations(Environment environment, ClientCache clientCache) {
return Optional.ofNullable(this.gemfireAdminOperations)
.orElseGet(() -> newGemfireAdminOperations(environment, clientCache));
}
/**
* Constructs a new instance of {@link GemfireAdminOperations} to perform administrative, schema functions
* on a GemFire cache cluster as well as a client cache from a cache client.
*
* @param environment reference to the {@link Environment}.
* @param clientCache {@link ClientCache} instance used by the {@link GemfireAdminOperations} interface
* to access the GemFire system.
* @return an implementation of the {@link GemfireAdminOperations} interface to perform administrative functions
* on a GemFire system.
* @see org.springframework.data.gemfire.config.admin.GemfireAdminOperations
* @see org.apache.geode.cache.client.ClientCache
* @see #resolveClientHttpRequestInterceptors()
* @see #resolveManagementHttpHost()
* @see #resolveManagementHttpPort()
* @see #resolveManagementRequireHttps()
* @see #resolveManagementUseHttp()
*/
private GemfireAdminOperations newGemfireAdminOperations(ClientCache clientCache) {
private GemfireAdminOperations newGemfireAdminOperations(Environment environment, ClientCache clientCache) {
if (resolveManagementUseHttp()) {
String host = resolveManagementHttpHost();
boolean setFollowRedirects =
environment.getProperty(HTTP_FOLLOW_REDIRECTS_PROPERTY, Boolean.class, DEFAULT_HTTP_FOLLOW_REDIRECTS);
boolean requireHttps = resolveManagementRequireHttps();
boolean followRedirects = !requireHttps || setFollowRedirects;
int port = resolveManagementHttpPort();
return new RestHttpGemfireAdminTemplate(clientCache, host, port);
String host = resolveManagementHttpHost();
String scheme = requireHttps ? HTTPS_SCHEME : HTTP_SCHEME;
return new RestHttpGemfireAdminTemplate.Builder()
.with(resolveClientHttpRequestInterceptors())
.with(clientCache)
.using(scheme)
.on(host)
.listenOn(port)
.followRedirects(followRedirects)
.build();
}
else {
return new FunctionGemfireAdminTemplate(clientCache);
@@ -205,7 +321,7 @@ public class ClusterConfigurationConfiguration extends AbstractAnnotationConfigS
}
/**
* Constructs an instance of {@link SchemaObjectCollector} to inspect the application's context
* Constructs a new instance of {@link SchemaObjectCollector} to inspect the application's context
* and find all the GemFire schema objects declared of a particular type or types.
*
* @return a new instance of {@link SchemaObjectCollector} to inspect a GemFire system schema
@@ -221,7 +337,7 @@ public class ClusterConfigurationConfiguration extends AbstractAnnotationConfigS
}
/**
* Constructs an instance of {@link SchemaObjectDefiner} used to reverse engineer a GemFire schema object instance
* Constructs a new instance of {@link SchemaObjectDefiner} used to reverse engineer a GemFire schema object instance
* to build a definition.
*
* @return a new instance of {@link SchemaObjectDefiner}.
@@ -240,7 +356,9 @@ public class ClusterConfigurationConfiguration extends AbstractAnnotationConfigS
private final SchemaObjectContext schemaObjectContext;
protected ClusterSchemaObjectInitializer(SchemaObjectContext schemaObjectContext) {
Assert.notNull(schemaObjectContext, "SchemaObjectContext is required");
this.schemaObjectContext = schemaObjectContext;
}
@@ -254,7 +372,7 @@ public class ClusterConfigurationConfiguration extends AbstractAnnotationConfigS
return Integer.MIN_VALUE;
}
protected SchemaObjectContext getSchemaObjectContext() {
public SchemaObjectContext getSchemaObjectContext() {
return this.schemaObjectContext;
}
@@ -268,14 +386,16 @@ public class ClusterConfigurationConfiguration extends AbstractAnnotationConfigS
Iterable<?> schemaObjects = schemaObjectContext.getSchemaObjectCollector()
.collectFrom(requireApplicationContext());
stream(schemaObjects.spliterator(), false)
//Iterable<?> cacheSchemaObjects = schemaObjectContext.getSchemaObjectCollector()
// .collectFrom(schemaObjectContext.<GemFireCache>getGemfireCache());
StreamSupport.stream(schemaObjects.spliterator(), false)
.map(schemaObjectContext.getSchemaObjectDefiner()::define)
.sorted(OrderComparator.INSTANCE)
.forEach(schemaObjectDefinition -> schemaObjectDefinition
.ifPresent(it -> it.create(schemaObjectContext.getGemfireAdminOperations())));
.forEach(schemaObjectDefinition -> schemaObjectDefinition.ifPresent(it ->
it.create(schemaObjectContext.getGemfireAdminOperations())));
setRunning(true);
}
/*
else if (schemaObjectContext.isPeerCache()) {
@@ -285,7 +405,6 @@ public class ClusterConfigurationConfiguration extends AbstractAnnotationConfigS
GemfireFunctionUtils.registerFunctionForPojoMethod(new CreateIndexFunction(),
CreateIndexFunction.CREATE_INDEX_FUNCTION_ID);
}
*/
}

View File

@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire.config.annotation;
import java.lang.annotation.Documented;
@@ -26,19 +25,22 @@ import java.lang.annotation.Target;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionShortcut;
import org.apache.geode.cache.client.ClientCache;
import org.springframework.context.annotation.Import;
/**
* The {@link EnableClusterConfiguration} annotation enables Apache Geode / Pivotal GemFire schema-like definitions
* defined in a Spring [Boot], Geode/GemFire cache client application using Spring config to be pushed to
* a Geode/GemFire cluster, similar to how schema commands (e.g. `create region`) in Gfsh are processed by
* an Geode/GemFire Manager.
* The {@link EnableClusterConfiguration} annotation enables Apache Geode / Pivotal GemFire schema object definitions
* defined in a Spring [Boot], Apache Geode / Pivotal GemFire {@link ClientCache} application using Spring config
* to be pushed to an Apache Geode / Pivotal GemFire cluster, similar to how schema commands (e.g. `create region`)
* in Gfsh are processed by an Apache Geode / Pivotal GemFire Manager.
*
* @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.apache.geode.cache.Region
* @see org.apache.geode.cache.client.ClientCache
* @see org.springframework.context.annotation.Import
* @see org.springframework.data.gemfire.config.annotation.ClusterConfigurationConfiguration
* @since 2.0.0
@@ -75,6 +77,18 @@ public @interface EnableClusterConfiguration {
*/
int port() default ClusterConfigurationConfiguration.DEFAULT_MANAGEMENT_HTTP_PORT;
/**
* Configures whether the HTTP connection between Spring and Apache Geode or Pivotal GemFire should be secure.
* That is, whether the HTTP connections uses TLS and results in a secure HTTPS connection rather a plain text
* HTTP connection.
*
* Alternatively, you can configure this setting using the {@literal spring.data.gemfire.management.require-https}
* property in {@literal application.properties}.
*
* Defaults to {@literal true}.
*/
boolean requireHttps() default ClusterConfigurationConfiguration.DEFAULT_MANAGEMENT_REQUIRE_HTTPS;
/**
* Configuration setting used to specify the data management policy used when creating {@link Region Regions}
* on the servers in the Geode/GemFire cluster.