SGF-846 - 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 49f3a67dee
commit 116c01a20f
10 changed files with 1524 additions and 102 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 Pivotal 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 Pivotal 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 host {@link String} containing the hostname of the Pivotal GemFire/Apache Geode Manager.
* @param port integer value specifying the port on which the Pivotal GemFire/Apache Geode Manager HTTP Service is listening
* @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,93 @@ public class ClusterConfigurationConfiguration extends AbstractAnnotationConfigS
}
/**
* Constructs an instance of {@link GemfireAdminOperations} to perform administrative, schema functions
* on a Pivotal GemFire cache cluster as well as a client cache from a cache client.
* 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.
>>>>>>> d4f8a960... DATAGEODE-192 - Add support for HTTPS and Follow Redirects when using @EnableClusterConfiguration.
*
* @param environment reference to the {@link Environment}.
* @param clientCache {@link ClientCache} instance used by the {@link GemfireAdminOperations} interface
* to access the Pivotal GemFire system.
* @return an implementation of the {@link GemfireAdminOperations} interface to perform administrative functions
* on a Pivotal 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,8 +322,8 @@ public class ClusterConfigurationConfiguration extends AbstractAnnotationConfigS
}
/**
* Constructs an instance of {@link SchemaObjectCollector} to inspect the application's context
* and find all the Pivotal GemFire schema objects declared of a particular type or types.
* 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 Pivotal GemFire system schema
* in search of specific Pivotal GemFire schema objects (e.g. {@link Region} or {@link Index}).
@@ -221,7 +338,7 @@ public class ClusterConfigurationConfiguration extends AbstractAnnotationConfigS
}
/**
* Constructs an instance of {@link SchemaObjectDefiner} used to reverse engineer a Pivotal 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 +357,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 +373,7 @@ public class ClusterConfigurationConfiguration extends AbstractAnnotationConfigS
return Integer.MIN_VALUE;
}
protected SchemaObjectContext getSchemaObjectContext() {
public SchemaObjectContext getSchemaObjectContext() {
return this.schemaObjectContext;
}
@@ -268,14 +387,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 +406,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/Pivotal GemFire cache client application using Spring config to be pushed to
* a Geode/Pivotal GemFire cluster, similar to how schema commands (e.g. `create region`) in Gfsh are processed by
* an Geode/Pivotal 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/Pivotal GemFire cluster.

View File

@@ -0,0 +1,173 @@
/*
* 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
*
* http://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 org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.springframework.data.gemfire.config.admin.remote.RestHttpGemfireAdminTemplate.FollowRedirectsSimpleClientHttpRequestFactory;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Optional;
import org.apache.geode.cache.client.ClientCache;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.InterceptingClientHttpRequestFactory;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.client.RestTemplate;
/**
* Unit Tests for {@link RestHttpGemfireAdminTemplate.Builder}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.mockito.junit.MockitoJUnitRunner
* @see org.apache.geode.cache.client.ClientCache
* @see org.springframework.data.gemfire.config.admin.remote.RestHttpGemfireAdminTemplate.Builder
* @see org.springframework.http.client.ClientHttpRequestFactory
* @see org.springframework.http.client.ClientHttpRequestInterceptor
* @see org.springframework.web.client.RestTemplate
* @since 2.2.0
*/
@RunWith(MockitoJUnitRunner.class)
public class RestHttpGemfireAdminTemplateBuilderUnitTests {
@Mock
private ClientCache mockClientCache;
@SuppressWarnings("unchecked")
private <T> T resolveFieldValue(Object target, String fieldName) throws NoSuchFieldException {
Field field = ReflectionUtils.findField(target.getClass(), fieldName, ClientHttpRequestFactory.class);
return Optional.ofNullable(field)
.map(it -> {
ReflectionUtils.makeAccessible(it);
return field;
})
.map(it -> (T) ReflectionUtils.getField(it, target))
.orElseThrow(() ->
new NoSuchFieldException(String.format("Field [%s] was not found on Object of type [%s]",
fieldName, target.getClass().getName())));
}
@Test
public void buildSuccessfullyBuildsNewRestHttpGemfireAdminTemplate() throws NoSuchFieldException {
ClientHttpRequestInterceptor mockInterceptorOne = mock(ClientHttpRequestInterceptor.class);
ClientHttpRequestInterceptor mockInterceptorTwo = mock(ClientHttpRequestInterceptor.class);
ClientHttpRequestInterceptor mockInterceptorThree = mock(ClientHttpRequestInterceptor.class);
ClientHttpRequestInterceptor mockInterceptorFour = mock(ClientHttpRequestInterceptor.class);
ClientHttpRequestInterceptor mockInterceptorFive = mock(ClientHttpRequestInterceptor.class);
RestHttpGemfireAdminTemplate template = new RestHttpGemfireAdminTemplate.Builder()
.with(this.mockClientCache)
.with(mockInterceptorOne, mockInterceptorTwo)
.with(mockInterceptorThree)
.with(Arrays.asList(mockInterceptorFour, mockInterceptorFive))
.using("Http")
.on("skullbox")
.listenOn(81)
.followRedirects(false)
.build();
assertThat(template).isNotNull();
assertThat(template.getClientCache()).isSameAs(this.mockClientCache);
assertThat(template.getManagementRestApiUrl()).isEqualTo("http://skullbox:81/gemfire/v1");
assertThat(template.<RestTemplate>getRestOperations().getInterceptors())
.containsExactly(mockInterceptorOne, mockInterceptorTwo, mockInterceptorThree,
mockInterceptorFour, mockInterceptorFive);
assertThat(template.<RestTemplate>getRestOperations().getRequestFactory())
.isInstanceOf(InterceptingClientHttpRequestFactory.class);
ClientHttpRequestFactory clientHttpRequestFactory =
resolveFieldValue(template.<RestTemplate>getRestOperations().getRequestFactory(), "requestFactory");
assertThat(clientHttpRequestFactory).isInstanceOf(FollowRedirectsSimpleClientHttpRequestFactory.class);
assertThat(((FollowRedirectsSimpleClientHttpRequestFactory) clientHttpRequestFactory).isFollowRedirects())
.isFalse();
}
private void testInvalidPortThrowsException(int port) {
try {
new RestHttpGemfireAdminTemplate.Builder().listenOn(port);
}
catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("Port [%d] must be greater than 0 and less than 65536", port);
assertThat(expected).hasNoCause();
throw expected;
}
}
@Test(expected = IllegalArgumentException.class)
public void listenOnOverflowPortThrowsException() {
testInvalidPortThrowsException(65536);
}
@Test(expected = IllegalArgumentException.class)
public void listenOnUnderflowPortThrowsException() {
testInvalidPortThrowsException(0);
}
private void testInvalidHostnameDefaultsToLocalhost(String hostname) {
RestHttpGemfireAdminTemplate template = new RestHttpGemfireAdminTemplate.Builder()
.with(this.mockClientCache)
.on(hostname)
.build();
assertThat(template).isNotNull();
assertThat(template.getManagementRestApiUrl()).isEqualTo("https://localhost:7070/gemfire/v1");
}
@Test
public void onEmptyHostnameDefaultsToLocalhost() {
testInvalidHostnameDefaultsToLocalhost("");
testInvalidHostnameDefaultsToLocalhost(" ");
}
@Test
public void onNoHostnameDefaultsToLocalhost() {
testInvalidHostnameDefaultsToLocalhost(null);
}
@Test(expected = IllegalArgumentException.class)
public void usingInvalidScheme() {
try {
new RestHttpGemfireAdminTemplate.Builder().using("ftp");
}
catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("Scheme [ftp] is not valid; must be 1 of %s",
RestHttpGemfireAdminTemplate.VALID_SCHEMES);
assertThat(expected).hasNoCause();
throw expected;
}
}
}

View File

@@ -0,0 +1,98 @@
/*
* 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
*
* http://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 org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.springframework.data.gemfire.config.admin.remote.RestHttpGemfireAdminTemplate.FollowRedirectsSimpleClientHttpRequestFactory;
import java.io.IOException;
import java.net.HttpURLConnection;
import org.junit.Test;
import org.mockito.InOrder;
import org.mockito.Mockito;
/**
* Unit Tests for {@link RestHttpGemfireAdminTemplate.FollowRedirectsSimpleClientHttpRequestFactory}.
*
* @author John Blum
* @see java.net.HttpURLConnection
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.springframework.data.gemfire.config.admin.remote.RestHttpGemfireAdminTemplate.FollowRedirectsSimpleClientHttpRequestFactory
* @since 2.2.0
*/
public class RestHttpGemfireAdminTemplateFollowRedirectsClientHttpRequestFactoryUnitTests {
@Test
public void doesNotFollowRedirectsClientHttpRequestFactory() throws IOException {
FollowRedirectsSimpleClientHttpRequestFactory clientHttpRequestFactory =
new FollowRedirectsSimpleClientHttpRequestFactory(false);
assertThat(clientHttpRequestFactory).isNotNull();
assertThat(clientHttpRequestFactory.isFollowRedirects()).isFalse();
HttpURLConnection mockHttpUrlConnection = mock(HttpURLConnection.class);
doCallRealMethod().when(mockHttpUrlConnection).setInstanceFollowRedirects(anyBoolean());
doCallRealMethod().when(mockHttpUrlConnection).getInstanceFollowRedirects();
clientHttpRequestFactory.prepareConnection(mockHttpUrlConnection, "GET");
assertThat(mockHttpUrlConnection.getInstanceFollowRedirects()).isFalse();
InOrder inOrder = Mockito.inOrder(mockHttpUrlConnection);
inOrder.verify(mockHttpUrlConnection, times(1))
.setInstanceFollowRedirects(eq(true));
inOrder.verify(mockHttpUrlConnection, times(1))
.setInstanceFollowRedirects(eq(false));
}
@Test
public void followsRedirectsClientHttpRequestFactory() throws IOException {
FollowRedirectsSimpleClientHttpRequestFactory clientHttpRequestFactory =
new FollowRedirectsSimpleClientHttpRequestFactory(true);
assertThat(clientHttpRequestFactory).isNotNull();
assertThat(clientHttpRequestFactory.isFollowRedirects()).isTrue();
HttpURLConnection mockHttpUrlConnection = mock(HttpURLConnection.class);
doCallRealMethod().when(mockHttpUrlConnection).setInstanceFollowRedirects(anyBoolean());
doCallRealMethod().when(mockHttpUrlConnection).getInstanceFollowRedirects();
clientHttpRequestFactory.prepareConnection(mockHttpUrlConnection, "POST");
assertThat(mockHttpUrlConnection.getInstanceFollowRedirects()).isTrue();
InOrder inOrder = Mockito.inOrder(mockHttpUrlConnection);
inOrder.verify(mockHttpUrlConnection, times(1))
.setInstanceFollowRedirects(eq(false));
inOrder.verify(mockHttpUrlConnection, times(1))
.setInstanceFollowRedirects(eq(true));
}
}

View File

@@ -13,18 +13,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire.config.admin.remote;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.data.gemfire.config.admin.remote.RestHttpGemfireAdminTemplate.FollowRedirectsSimpleClientHttpRequestFactory;
import java.lang.reflect.Field;
import java.net.URI;
import java.util.Arrays;
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;
@@ -43,17 +49,27 @@ 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.InterceptingClientHttpRequestFactory;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.client.RestOperations;
import org.springframework.web.client.RestTemplate;
/**
* Unit tests for {@link RestHttpGemfireAdminTemplate}.
*
* @author John Blum
* @see java.net.URI
* @see org.junit.Test
* @see org.mockito.Mock
* @see org.mockito.Mockito
* @see org.springframework.data.gemfire.config.admin.remote.RestHttpGemfireAdminTemplate
* @see org.springframework.http.client.ClientHttpRequestFactory
* @see org.springframework.http.client.ClientHttpRequestInterceptor
* @see org.springframework.web.client.RestOperations
* @see org.springframework.web.client.RestTemplate
* @since 2.0.0
*/
@RunWith(MockitoJUnitRunner.class)
@@ -77,16 +93,37 @@ public class RestHttpGemfireAdminTemplateUnitTests {
public void setup() {
this.template = new RestHttpGemfireAdminTemplate(this.mockClientCache) {
@Override protected RestOperations newRestOperations() {
return mockRestOperations;
@Override
@SuppressWarnings("unchecked")
protected <T extends RestOperations> T newRestOperations(ClientHttpRequestFactory clientHttpRequestFactory,
List<ClientHttpRequestInterceptor> clientHttpRequestInterceptors) {
return (T) mockRestOperations;
}
};
when(this.mockRegion.getName()).thenReturn("MockRegion");
when(this.mockIndex.getType()).thenReturn(IndexType.FUNCTIONAL.getGemfireIndexType());
when(this.mockIndex.getName()).thenReturn("MockIndex");
when(this.mockIndex.getIndexedExpression()).thenReturn("age");
when(this.mockIndex.getFromClause()).thenReturn("/Customers");
when(this.mockIndex.getType()).thenReturn(IndexType.FUNCTIONAL.getGemfireIndexType());
}
@SuppressWarnings("unchecked")
private <T> T resolveFieldValue(Object target, String fieldName) throws NoSuchFieldException {
Field field = ReflectionUtils.findField(target.getClass(), fieldName, ClientHttpRequestFactory.class);
return Optional.ofNullable(field)
.map(it -> {
ReflectionUtils.makeAccessible(it);
return field;
})
.map(it -> (T) ReflectionUtils.getField(it, target))
.orElseThrow(() ->
new NoSuchFieldException(String.format("Field [%s] was not found on Object of type [%s]",
fieldName, target.getClass().getName())));
}
@Test
@@ -98,21 +135,133 @@ public class RestHttpGemfireAdminTemplateUnitTests {
assertThat(template.getClientCache()).isSameAs(this.mockClientCache);
assertThat(template.getManagementRestApiUrl())
.isEqualTo(String.format(RestHttpGemfireAdminTemplate.MANAGEMENT_REST_API_URL_TEMPLATE,
RestHttpGemfireAdminTemplate.DEFAULT_HOST, RestHttpGemfireAdminTemplate.DEFAULT_PORT));
assertThat(template.getRestOperations()).isNotNull();
RestHttpGemfireAdminTemplate.DEFAULT_SCHEME, RestHttpGemfireAdminTemplate.DEFAULT_HOST,
RestHttpGemfireAdminTemplate.DEFAULT_PORT));
assertThat(template.<RestOperations>getRestOperations()).isInstanceOf(RestTemplate.class);
RestTemplate restTemplate = (RestTemplate) template.getRestOperations();
ClientHttpRequestFactory clientHttpRequestFactory = restTemplate.getRequestFactory();
assertThat(clientHttpRequestFactory).isInstanceOf(FollowRedirectsSimpleClientHttpRequestFactory.class);
assertThat(((FollowRedirectsSimpleClientHttpRequestFactory) clientHttpRequestFactory).isFollowRedirects())
.isTrue();
List<ClientHttpRequestInterceptor> clientHttpRequestInterceptors = restTemplate.getInterceptors();
assertThat(clientHttpRequestInterceptors).isNotNull();
assertThat(clientHttpRequestInterceptors).isEmpty();
}
@Test
public void constructCustomRestHttpGemfireAdminTemplate() {
@SuppressWarnings("all")
public void constructCustomRestHttpGemfireAdminTemplate() throws Exception {
ClientHttpRequestInterceptor mockInterceptor = mock(ClientHttpRequestInterceptor.class);
RestHttpGemfireAdminTemplate template =
new RestHttpGemfireAdminTemplate(this.mockClientCache, "skullbox", 8080);
new RestHttpGemfireAdminTemplate(this.mockClientCache, "sftp", "skullbox", 8080,
false, Collections.singletonList(mockInterceptor));
assertThat(template).isNotNull();
assertThat(template.getClientCache()).isSameAs(this.mockClientCache);
assertThat(template.getManagementRestApiUrl())
.isEqualTo(String.format(RestHttpGemfireAdminTemplate.MANAGEMENT_REST_API_URL_TEMPLATE, "skullbox", 8080));
assertThat(template.getRestOperations()).isNotNull();
.isEqualTo(String.format(RestHttpGemfireAdminTemplate.MANAGEMENT_REST_API_URL_TEMPLATE,
"sftp", "skullbox", 8080));
assertThat(template.<RestOperations>getRestOperations()).isInstanceOf(RestTemplate.class);
RestTemplate restTemplate = (RestTemplate) template.getRestOperations();
ClientHttpRequestFactory clientHttpRequestFactory = restTemplate.getRequestFactory();
assertThat(clientHttpRequestFactory).isInstanceOf(InterceptingClientHttpRequestFactory.class);
clientHttpRequestFactory = this.resolveFieldValue(clientHttpRequestFactory, "requestFactory");
assertThat(clientHttpRequestFactory).isInstanceOf(FollowRedirectsSimpleClientHttpRequestFactory.class);
assertThat(((FollowRedirectsSimpleClientHttpRequestFactory) clientHttpRequestFactory).isFollowRedirects())
.isFalse();
List<ClientHttpRequestInterceptor> clientHttpRequestInterceptors = restTemplate.getInterceptors();
assertThat(clientHttpRequestInterceptors).isNotNull();
assertThat(clientHttpRequestInterceptors).contains(mockInterceptor);
}
@Test
public void newClientHttpRequestFactoryFollowsRedirectsIsTrue() {
ClientHttpRequestFactory clientHttpRequestFactory =
this.template.newClientHttpRequestFactory(true);
assertThat(clientHttpRequestFactory).isInstanceOf(FollowRedirectsSimpleClientHttpRequestFactory.class);
assertThat(((FollowRedirectsSimpleClientHttpRequestFactory) clientHttpRequestFactory).isFollowRedirects())
.isTrue();
}
@Test
public void newClientHttpRequestFactoryFollowsRedirectsIsFalse() {
ClientHttpRequestFactory clientHttpRequestFactory =
this.template.newClientHttpRequestFactory(false);
assertThat(clientHttpRequestFactory).isInstanceOf(FollowRedirectsSimpleClientHttpRequestFactory.class);
assertThat(((FollowRedirectsSimpleClientHttpRequestFactory) clientHttpRequestFactory).isFollowRedirects())
.isFalse();
}
@Test
public void newRestOperationsWithInterceptors() {
ClientHttpRequestFactory mockClientHttpRequestFactory = mock(ClientHttpRequestFactory.class);
ClientHttpRequestInterceptor mockInterceptorOne = mock(ClientHttpRequestInterceptor.class);
ClientHttpRequestInterceptor mockInterceptorTwo = mock(ClientHttpRequestInterceptor.class);
RestTemplate restTemplate = new RestHttpGemfireAdminTemplate(this.mockClientCache)
.newRestOperations(mockClientHttpRequestFactory, Arrays.asList(mockInterceptorOne, mockInterceptorTwo));
assertThat(restTemplate).isNotNull();
assertThat(restTemplate.getInterceptors()).containsExactly(mockInterceptorOne, mockInterceptorTwo);
assertThat(restTemplate.getRequestFactory()).isNotSameAs(mockClientHttpRequestFactory);
assertThat(restTemplate.getRequestFactory()).isInstanceOf(InterceptingClientHttpRequestFactory.class);
}
@Test
public void newRestOperationsWithNoInterceptors() {
ClientHttpRequestFactory mockClientHttpRequestFactory = mock(ClientHttpRequestFactory.class);
RestTemplate restTemplate = new RestHttpGemfireAdminTemplate(this.mockClientCache)
.newRestOperations(mockClientHttpRequestFactory, Collections.emptyList());
assertThat(restTemplate).isNotNull();
assertThat(restTemplate.getInterceptors()).isEmpty();
assertThat(restTemplate.getRequestFactory()).isSameAs(mockClientHttpRequestFactory);
}
@Test
public void resolvesManagementRestApiUrlCorrectly() {
assertThat(this.template.resolveManagementRestApiUrl("http", "boombox", 80))
.isEqualTo(String.format(RestHttpGemfireAdminTemplate.MANAGEMENT_REST_API_URL_TEMPLATE,
"http", "boombox", 80));
assertThat(this.template.resolveManagementRestApiUrl("https", "cardboardbox", 443))
.isEqualTo(String.format(RestHttpGemfireAdminTemplate.MANAGEMENT_REST_API_URL_TEMPLATE,
"https", "cardboardbox", 443));
assertThat(this.template.resolveManagementRestApiUrl("ftp", "jambox", 21))
.isEqualTo(String.format(RestHttpGemfireAdminTemplate.MANAGEMENT_REST_API_URL_TEMPLATE,
"ftp", "jambox", 21));
assertThat(this.template.resolveManagementRestApiUrl("sftp", "mailbox", 22))
.isEqualTo(String.format(RestHttpGemfireAdminTemplate.MANAGEMENT_REST_API_URL_TEMPLATE,
"sftp", "mailbox", 22));
assertThat(this.template.resolveManagementRestApiUrl("smtp", "skullbox", 25))
.isEqualTo(String.format(RestHttpGemfireAdminTemplate.MANAGEMENT_REST_API_URL_TEMPLATE,
"smtp", "skullbox", 25));
}
@Test
@@ -127,7 +276,7 @@ public class RestHttpGemfireAdminTemplateUnitTests {
assertThat(requestEntity).isNotNull();
assertThat(requestEntity.getMethod()).isEqualTo(HttpMethod.POST);
assertThat(requestEntity.getUrl()).isEqualTo(URI.create("http://localhost:7070/gemfire/v1/indexes"));
assertThat(requestEntity.getUrl()).isEqualTo(URI.create("https://localhost:7070/gemfire/v1/indexes"));
HttpHeaders headers = requestEntity.getHeaders();
@@ -140,6 +289,7 @@ public class RestHttpGemfireAdminTemplateUnitTests {
MultiValueMap<String, Object> requestBody = (MultiValueMap<String, Object>) body;
assertThat(requestBody).isNotNull();
assertThat(requestBody.getFirst("name")).isEqualTo(indexDefinition.getName());
assertThat(requestBody.getFirst("expression")).isEqualTo(indexDefinition.getExpression());
assertThat(requestBody.getFirst("region")).isEqualTo(indexDefinition.getFromClause());
@@ -166,7 +316,7 @@ public class RestHttpGemfireAdminTemplateUnitTests {
assertThat(requestEntity).isNotNull();
assertThat(requestEntity.getMethod()).isEqualTo(HttpMethod.POST);
assertThat(requestEntity.getUrl()).isEqualTo(URI.create("http://localhost:7070/gemfire/v1/regions"));
assertThat(requestEntity.getUrl()).isEqualTo(URI.create("https://localhost:7070/gemfire/v1/regions"));
HttpHeaders headers = requestEntity.getHeaders();
@@ -179,10 +329,11 @@ public class RestHttpGemfireAdminTemplateUnitTests {
MultiValueMap<String, Object> requestBody = (MultiValueMap<String, Object>) body;
assertThat(requestBody).isNotNull();
assertThat(requestBody.getFirst("name")).isEqualTo(regionDefinition.getName());
assertThat(requestBody.getFirst("type")).isEqualTo(regionDefinition.getRegionShortcut().toString());
assertThat(requestBody.getFirst("skip-if-exists"))
.isEqualTo(String.valueOf(RestHttpGemfireAdminTemplate.CREATE_REGION_SKIP_IF_EXISTS_DEFAULT));
.isEqualTo(String.valueOf(RestHttpGemfireAdminTemplate.DEFAULT_CREATE_REGION_SKIP_IF_EXISTS));
return new ResponseEntity(HttpStatus.OK);
});

View File

@@ -0,0 +1,188 @@
/*
* 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
*
* http://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.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.springframework.data.gemfire.config.annotation.ClusterConfigurationConfiguration.ClusterSchemaObjectInitializer;
import static org.springframework.data.gemfire.config.annotation.ClusterConfigurationConfiguration.SchemaObjectContext;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Optional;
import org.apache.geode.cache.client.ClientCache;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;
import org.springframework.data.gemfire.config.admin.remote.RestHttpGemfireAdminTemplate;
import org.springframework.data.gemfire.test.mock.annotation.EnableGemFireMockObjects;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.InterceptingClientHttpRequestFactory;
import org.springframework.lang.Nullable;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.client.RestTemplate;
/**
* Integration Tests for {@link EnableClusterConfiguration} and {@link ClusterConfigurationConfiguration} asserting that
* all user-defined {@link ClientHttpRequestInterceptor} beans get applied.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.apache.geode.cache.client.ClientCache
* @see org.springframework.data.gemfire.config.annotation.ClusterConfigurationConfiguration
* @see org.springframework.data.gemfire.config.annotation.EnableClusterConfiguration
* @see org.springframework.data.gemfire.test.mock.annotation.EnableGemFireMockObjects
* @see org.springframework.http.client.ClientHttpRequestInterceptor
* @see org.springframework.http.client.InterceptingClientHttpRequestFactory
* @see org.springframework.test.context.ContextConfiguration
* @see org.springframework.test.context.junit4.SpringRunner
* @since 2.2.0
*/
@RunWith(SpringRunner.class)
@ContextConfiguration
@SuppressWarnings("unused")
public class ClusterConfigurationWithClientHttpRequestInterceptorsIntegrationTests {
@Autowired
private ClientCache clientCache;
@Autowired
@Qualifier("mockClientHttpRequestInterceptorOne")
private ClientHttpRequestInterceptor mockClientHttpRequestInterceptorOne;
@Autowired
@Qualifier("mockClientHttpRequestInterceptorTwo")
private ClientHttpRequestInterceptor mockClientHttpRequestInterceptorTwo;
@Autowired
private ClusterConfigurationConfiguration configuration;
@Autowired
private ClusterSchemaObjectInitializer initializer;
@Autowired
private List<ClientHttpRequestInterceptor> clientHttpRequestInterceptors;
@SuppressWarnings("unchecked")
private <T> T resolveFieldValue(Object target, String fieldName) throws NoSuchFieldException {
Field field = ReflectionUtils.findField(target.getClass(), fieldName);
return Optional.ofNullable(field)
.map(it -> {
ReflectionUtils.makeAccessible(it);
return field;
})
.map(it -> (T) ReflectionUtils.getField(it, target))
.orElseThrow(() ->
new NoSuchFieldException(String.format("Field with name [%s] was not found on Object of type [%s]",
fieldName, target.getClass().getName())));
}
@Before
public void setup() {
assertThat(this.clientCache).isNotNull();
assertThat(this.configuration).isNotNull();
assertThat(this.initializer).isNotNull();
assertThat(this.mockClientHttpRequestInterceptorOne).isNotNull();
assertThat(this.mockClientHttpRequestInterceptorTwo).isNotNull();
assertThat(this.clientHttpRequestInterceptors).isNotNull();
assertThat(this.clientHttpRequestInterceptors).hasSize(2);
assertThat(this.clientHttpRequestInterceptors)
.containsExactly(this.mockClientHttpRequestInterceptorTwo, this.mockClientHttpRequestInterceptorOne);
}
@Test
public void configurationWasAutowiredWithUserDefinedClientHttpRequestInterceptors() {
assertThat(this.configuration.resolveClientHttpRequestInterceptors())
.isEqualTo(this.clientHttpRequestInterceptors);
}
@Test
public void clientHttpRequestInterceptorsRegistered() throws Exception {
SchemaObjectContext schemaObjectContext = this.initializer.getSchemaObjectContext();
assertThat(schemaObjectContext).isNotNull();
assertThat(schemaObjectContext.<ClientCache>getGemfireCache()).isSameAs(this.clientCache);
assertThat(schemaObjectContext.getGemfireAdminOperations()).isInstanceOf(RestHttpGemfireAdminTemplate.class);
RestHttpGemfireAdminTemplate template =
(RestHttpGemfireAdminTemplate) schemaObjectContext.getGemfireAdminOperations();
RestTemplate restTemplate = resolveFieldValue(template, "restTemplate");
assertThat(restTemplate).isNotNull();
assertThat(restTemplate.getInterceptors())
.containsExactly(this.mockClientHttpRequestInterceptorTwo, this.mockClientHttpRequestInterceptorOne);
assertThat(restTemplate.getRequestFactory()).isInstanceOf(InterceptingClientHttpRequestFactory.class);
}
@ClientCacheApplication
@EnableGemFireMockObjects
@EnableClusterConfiguration(useHttp = true)
static class TestConfiguration {
@Bean
BeanPostProcessor clusterSchemaObjectInitializerBeanPostProcessor() {
return new BeanPostProcessor() {
@Nullable @Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof ClusterSchemaObjectInitializer) {
ClusterSchemaObjectInitializer initializer = spy((ClusterSchemaObjectInitializer) bean);
doReturn(false).when(initializer).isAutoStartup();
bean = initializer;
}
return bean;
}
};
}
@Bean
@Order(2)
ClientHttpRequestInterceptor mockClientHttpRequestInterceptorOne() {
return mock(ClientHttpRequestInterceptor.class);
}
@Bean
@Order(1)
ClientHttpRequestInterceptor mockClientHttpRequestInterceptorTwo() {
return mock(ClientHttpRequestInterceptor.class);
}
}
}

View File

@@ -0,0 +1,122 @@
/*
* 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
*
* http://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.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.springframework.data.gemfire.config.annotation.ClusterConfigurationConfiguration.ClusterSchemaObjectInitializer;
import static org.springframework.data.gemfire.config.annotation.ClusterConfigurationConfiguration.SchemaObjectContext;
import org.apache.geode.cache.client.ClientCache;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.data.gemfire.config.admin.GemfireAdminOperations;
import org.springframework.data.gemfire.test.mock.annotation.EnableGemFireMockObjects;
import org.springframework.lang.Nullable;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
/**
* Integration Tests for {@link EnableClusterConfiguration} and {@link ClusterConfigurationConfiguration} asserting that
* SDG support custom registered, user-defined {@link GemfireAdminOperations}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.apache.geode.cache.client.ClientCache
* @see org.springframework.data.gemfire.config.admin.GemfireAdminOperations
* @see org.springframework.data.gemfire.test.mock.annotation.EnableGemFireMockObjects
* @see org.springframework.test.context.ContextConfiguration
* @see org.springframework.test.context.junit4.SpringRunner
* @since 2.2.0
*/
@RunWith(SpringRunner.class)
@ContextConfiguration
@SuppressWarnings("unused")
public class ClusterConfigurationWithCustomGemfireAdminOperationsIntegrationTests {
@Autowired
private ClientCache clientCache;
@Autowired
private ClusterConfigurationConfiguration configuration;
@Autowired
private ClusterSchemaObjectInitializer initializer;
@Autowired
private GemfireAdminOperations gemfireAdminOperations;
@Before
public void setup() {
assertThat(this.clientCache).isNotNull();
assertThat(this.configuration).isNotNull();
assertThat(this.gemfireAdminOperations).isNotNull();
assertThat(this.initializer).isNotNull();
assertThat(this.configuration.resolveGemfireAdminOperations(null, this.clientCache))
.isSameAs(this.gemfireAdminOperations);
}
@Test
public void customGemfireAdminOperationsRegistered() {
SchemaObjectContext schemaObjectContext = this.initializer.getSchemaObjectContext();
assertThat(schemaObjectContext).isNotNull();
assertThat(schemaObjectContext.getGemfireAdminOperations()).isSameAs(this.gemfireAdminOperations);
}
@ClientCacheApplication
@EnableGemFireMockObjects
@EnableClusterConfiguration
static class TestConfiguration {
@Bean
BeanPostProcessor clusterSchemaObjectInitializerBeanPostProcessor() {
return new BeanPostProcessor() {
@Nullable @Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof ClusterSchemaObjectInitializer) {
ClusterSchemaObjectInitializer initializer = spy((ClusterSchemaObjectInitializer) bean);
doReturn(false).when(initializer).isAutoStartup();
bean = initializer;
}
return bean;
}
};
}
@Bean
GemfireAdminOperations mockGemfireAdminOperations() {
return mock(GemfireAdminOperations.class);
}
}
}

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 static org.assertj.core.api.Assertions.assertThat;
@@ -50,11 +49,14 @@ import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
/**
* Integration tests for the {@link EnableClusterConfiguration} annotation
* Integration Tests for the {@link EnableClusterConfiguration} annotation
* and {@link ClusterConfigurationConfiguration} class.
*
* @author John Blum
* @see org.junit.Test
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.client.ClientCache
* @see org.springframework.context.annotation.AnnotationConfigApplicationContext
* @see org.springframework.context.annotation.Bean
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.context.annotation.Import
@@ -74,6 +76,8 @@ import org.springframework.test.context.junit4.SpringRunner;
@SuppressWarnings("unused")
public class EnableClusterConfigurationIntegrationTests extends ClientServerIntegrationTestsSupport {
private static final String GEMFIRE_LOG_LEVEL = "error";
private static ProcessWrapper gemfireServer;
@Autowired
@@ -96,24 +100,31 @@ public class EnableClusterConfigurationIntegrationTests extends ClientServerInte
@AfterClass
public static void stopGemFireServer() {
System.clearProperty(GEMFIRE_CACHE_SERVER_PORT_PROPERTY);
stop(gemfireServer);
System.clearProperty(GEMFIRE_CACHE_SERVER_PORT_PROPERTY);
}
@Before
public void setup() {
this.adminOperations = new RestHttpGemfireAdminTemplate(this.gemfireCache,
"localhost", Integer.getInteger(GEMFIRE_CACHE_SERVER_PORT_PROPERTY, 40404));
this.adminOperations = new RestHttpGemfireAdminTemplate.Builder()
.with(this.gemfireCache)
.on("localhost")
.listenOn(Integer.getInteger(GEMFIRE_CACHE_SERVER_PORT_PROPERTY, 40404))
.build();
}
@Test
public void serverIndexesAreCorrect() {
assertThat(this.adminOperations.getAvailableServerRegionIndexes())
.containsAll(Arrays.asList("IndexOne", "IndexTwo"));
}
@Test
public void serverRegionsAreCorrect() {
assertThat(this.adminOperations.getAvailableServerRegions())
.containsAll(Arrays.asList("RegionOne", "RegionTwo", "RegionThree", "RegionFour"));
}
@@ -121,18 +132,17 @@ public class EnableClusterConfigurationIntegrationTests extends ClientServerInte
@Configuration
@EnableClusterConfiguration
@Import(ClientTestConfiguration.class)
static class TestConfiguration {
}
static class TestConfiguration { }
@ClientCacheApplication(logLevel = TEST_GEMFIRE_LOG_LEVEL, subscriptionEnabled = true)
@ClientCacheApplication(logLevel = GEMFIRE_LOG_LEVEL, subscriptionEnabled = true)
static class ClientTestConfiguration {
@Bean
ClientCacheConfigurer clientCachePoolPortConfigurer(
@Value("${" + GEMFIRE_CACHE_SERVER_PORT_PROPERTY + ":40404}") int port) {
return (bean, clientCacheFactoryBean) -> clientCacheFactoryBean.setServers(
Collections.singletonList(new ConnectionEndpoint("localhost", port)));
return (bean, clientCacheFactoryBean) -> clientCacheFactoryBean
.setServers(Collections.singletonList(new ConnectionEndpoint("localhost", port)));
}
@Bean("IndexOne")
@@ -186,7 +196,7 @@ public class EnableClusterConfigurationIntegrationTests extends ClientServerInte
}
}
@CacheServerApplication(name = "EnableClusterConfigurationIntegrationTests", logLevel = TEST_GEMFIRE_LOG_LEVEL)
@CacheServerApplication(name = "EnableClusterConfigurationIntegrationTests", logLevel = GEMFIRE_LOG_LEVEL)
static class ServerTestConfiguration {
public static void main(String[] args) {
@@ -219,19 +229,7 @@ public class EnableClusterConfigurationIntegrationTests extends ClientServerInte
}
@Bean("RegionTwo")
PartitionedRegionFactoryBean<Object, Object> regionTwo(GemFireCache gemfireCache) {
PartitionedRegionFactoryBean<Object, Object> regionFactoryBean = new PartitionedRegionFactoryBean<>();
regionFactoryBean.setCache(gemfireCache);
regionFactoryBean.setClose(false);
regionFactoryBean.setPersistent(false);
return regionFactoryBean;
}
@Bean("RegionFour")
ReplicatedRegionFactoryBean<Object, Object> regionFour(GemFireCache gemfireCache) {
ReplicatedRegionFactoryBean<Object, Object> regionTwo(GemFireCache gemfireCache) {
ReplicatedRegionFactoryBean<Object, Object> regionFactoryBean = new ReplicatedRegionFactoryBean<>();
@@ -241,5 +239,17 @@ public class EnableClusterConfigurationIntegrationTests extends ClientServerInte
return regionFactoryBean;
}
@Bean("RegionFour")
PartitionedRegionFactoryBean<Object, Object> regionFour(GemFireCache gemfireCache) {
PartitionedRegionFactoryBean<Object, Object> regionFactoryBean = new PartitionedRegionFactoryBean<>();
regionFactoryBean.setCache(gemfireCache);
regionFactoryBean.setClose(false);
regionFactoryBean.setPersistent(false);
return regionFactoryBean;
}
}
}

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 static org.assertj.core.api.Assertions.assertThat;
@@ -22,19 +21,45 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.springframework.data.gemfire.config.admin.remote.RestHttpGemfireAdminTemplate.FollowRedirectsSimpleClientHttpRequestFactory;
import static org.springframework.data.gemfire.config.annotation.ClusterConfigurationConfiguration.ClusterSchemaObjectInitializer;
import static org.springframework.data.gemfire.config.annotation.ClusterConfigurationConfiguration.SchemaObjectContext;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.RegionShortcut;
import org.apache.geode.cache.client.ClientCache;
import org.junit.After;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.core.env.Environment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.gemfire.config.admin.GemfireAdminOperations;
import org.springframework.data.gemfire.config.admin.remote.FunctionGemfireAdminTemplate;
import org.springframework.data.gemfire.config.admin.remote.RestHttpGemfireAdminTemplate;
import org.springframework.data.gemfire.config.schema.support.ComposableSchemaObjectCollector;
import org.springframework.data.gemfire.config.schema.support.ComposableSchemaObjectDefiner;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.InterceptingClientHttpRequestFactory;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.client.RestTemplate;
/**
* Unit tests for {@link EnableClusterConfiguration} annotation and the {@link ClusterConfigurationConfiguration} class.
@@ -51,6 +76,44 @@ import org.springframework.core.type.AnnotationMetadata;
*/
public class EnableClusterConfigurationUnitTests {
@After
public void tearDown() {
System.clearProperty(ClusterConfigurationConfiguration.HTTP_FOLLOW_REDIRECTS_PROPERTY);
}
private <T> ClusterConfigurationConfiguration autowire(ClusterConfigurationConfiguration target,
String fieldName, T dependency) throws NoSuchFieldException {
return Optional.ofNullable(ReflectionUtils.findField(target.getClass(), fieldName))
.map(field -> {
ReflectionUtils.makeAccessible(field);
return field;
})
.map(field -> {
ReflectionUtils.setField(field, target, dependency);
return target;
})
.orElseThrow(() ->
new NoSuchFieldException(String.format("Field [%s] was not found on Object of type [%s]",
fieldName, target.getClass().getName())));
}
@SuppressWarnings("unchecked")
private <T> T resolveFieldValue(Object target, String fieldName) throws NoSuchFieldException {
Field field = ReflectionUtils.findField(target.getClass(), fieldName);
return Optional.ofNullable(field)
.map(it -> {
ReflectionUtils.makeAccessible(it);
return field;
})
.map(it -> (T) ReflectionUtils.getField(it, target))
.orElseThrow(() ->
new NoSuchFieldException(String.format("Field with name [%s] was not found on Object of type [%s]",
fieldName, target.getClass().getName())));
}
@Test
public void setImportMetadataFromAnnotationAttributes() {
@@ -60,6 +123,7 @@ public class EnableClusterConfigurationUnitTests {
annotationAttributes.put("host", "skullbox");
annotationAttributes.put("port", 12345);
annotationAttributes.put("requireHttps", false);
annotationAttributes.put("serverRegionShortcut", RegionShortcut.PARTITION_PERSISTENT);
annotationAttributes.put("useHttp", true);
@@ -74,6 +138,7 @@ public class EnableClusterConfigurationUnitTests {
assertThat(configuration.getManagementHttpHost().orElse(null)).isEqualTo("skullbox");
assertThat(configuration.getManagementHttpPort().orElse(0)).isEqualTo(12345);
assertThat(configuration.getManagementRequireHttps().orElse(true)).isFalse();
assertThat(configuration.getManagementUseHttp().orElse(false)).isTrue();
assertThat(configuration.getServerRegionShortcut().orElse(null)).isEqualTo(RegionShortcut.PARTITION_PERSISTENT);
@@ -93,6 +158,7 @@ public class EnableClusterConfigurationUnitTests {
annotationAttributes.put("host", "skullbox");
annotationAttributes.put("port", 12345);
annotationAttributes.put("requireHttps", true);
annotationAttributes.put("serverRegionShortcut", RegionShortcut.PARTITION_PERSISTENT);
annotationAttributes.put("useHttp", false);
@@ -106,6 +172,7 @@ public class EnableClusterConfigurationUnitTests {
when(mockEnvironment.containsProperty(eq("spring.data.gemfire.cluster.region.type"))).thenReturn(true);
when(mockEnvironment.containsProperty(eq("spring.data.gemfire.management.http.host"))).thenReturn(true);
when(mockEnvironment.containsProperty(eq("spring.data.gemfire.management.http.port"))).thenReturn(true);
when(mockEnvironment.containsProperty(eq("spring.data.gemfire.management.require-https"))).thenReturn(true);
when(mockEnvironment.containsProperty(eq("spring.data.gemfire.management.use-http"))).thenReturn(true);
when(mockEnvironment.getProperty(eq("spring.data.gemfire.cluster.region.type"), eq(RegionShortcut.class), any(RegionShortcut.class)))
@@ -117,6 +184,9 @@ public class EnableClusterConfigurationUnitTests {
when(mockEnvironment.getProperty(eq("spring.data.gemfire.management.http.port"), eq(Integer.class), any(Integer.class)))
.thenReturn(11235);
when(mockEnvironment.getProperty(eq("spring.data.gemfire.management.require-https"), eq(Boolean.class), any(Boolean.class)))
.thenReturn(false);
when(mockEnvironment.getProperty(eq("spring.data.gemfire.management.use-http"), eq(Boolean.class), any(Boolean.class)))
.thenReturn(true);
@@ -130,6 +200,7 @@ public class EnableClusterConfigurationUnitTests {
assertThat(configuration.getManagementHttpHost().orElse(null)).isEqualTo("cardboardBox");
assertThat(configuration.getManagementHttpPort().orElse(0)).isEqualTo(11235);
assertThat(configuration.getManagementRequireHttps().orElse(true)).isFalse();
assertThat(configuration.getManagementUseHttp().orElse(false)).isTrue();
assertThat(configuration.getServerRegionShortcut().orElse(null)).isEqualTo(RegionShortcut.LOCAL);
@@ -148,6 +219,9 @@ public class EnableClusterConfigurationUnitTests {
verify(mockEnvironment, times(1))
.containsProperty(eq("spring.data.gemfire.management.http.port"));
verify(mockEnvironment, times(1))
.containsProperty(eq("spring.data.gemfire.management.require-https"));
verify(mockEnvironment, times(1))
.containsProperty(eq("spring.data.gemfire.management.use-http"));
@@ -161,6 +235,9 @@ public class EnableClusterConfigurationUnitTests {
verify(mockEnvironment, times(1))
.getProperty(eq("spring.data.gemfire.management.http.port"), eq(Integer.class), anyInt());
verify(mockEnvironment, times(1))
.getProperty(eq("spring.data.gemfire.management.require-https"), eq(Boolean.class), eq(true));
verify(mockEnvironment, times(1))
.getProperty(eq("spring.data.gemfire.management.use-http"), eq(Boolean.class), eq(false));
}
@@ -174,6 +251,7 @@ public class EnableClusterConfigurationUnitTests {
annotationAttributes.put("host", "postOfficeBox");
annotationAttributes.put("port", 10101);
annotationAttributes.put("requireHttps", false);
annotationAttributes.put("serverRegionShortcut", RegionShortcut.REPLICATE);
annotationAttributes.put("useHttp", true);
@@ -187,10 +265,11 @@ public class EnableClusterConfigurationUnitTests {
when(mockEnvironment.containsProperty(eq("spring.data.gemfire.cluster.region.type"))).thenReturn(false);
when(mockEnvironment.containsProperty(eq("spring.data.gemfire.management.http.host"))).thenReturn(true);
when(mockEnvironment.containsProperty(eq("spring.data.gemfire.management.http.port"))).thenReturn(true);
when(mockEnvironment.containsProperty(eq("spring.data.gemfire.management.require-https"))).thenReturn(false);
when(mockEnvironment.containsProperty(eq("spring.data.gemfire.management.use-http"))).thenReturn(false);
when(mockEnvironment.getProperty(eq("spring.data.gemfire.management.http.host"), eq(String.class), any(String.class)))
.thenReturn("shoeBox");
.thenReturn("shoebox");
when(mockEnvironment.getProperty(eq("spring.data.gemfire.management.http.port"), eq(Integer.class), any(Integer.class)))
.thenReturn(12480);
@@ -203,8 +282,9 @@ public class EnableClusterConfigurationUnitTests {
configuration.setEnvironment(mockEnvironment);
configuration.setImportMetadata(mockImportMetadata);
assertThat(configuration.getManagementHttpHost().orElse(null)).isEqualTo("shoeBox");
assertThat(configuration.getManagementHttpHost().orElse(null)).isEqualTo("shoebox");
assertThat(configuration.getManagementHttpPort().orElse(0)).isEqualTo(12480);
assertThat(configuration.getManagementRequireHttps().orElse(true)).isFalse();
assertThat(configuration.getManagementUseHttp().orElse(false)).isTrue();
assertThat(configuration.getServerRegionShortcut().orElse(null)).isEqualTo(RegionShortcut.REPLICATE);
@@ -223,6 +303,9 @@ public class EnableClusterConfigurationUnitTests {
verify(mockEnvironment, times(1))
.containsProperty(eq("spring.data.gemfire.management.http.port"));
verify(mockEnvironment, times(1))
.containsProperty(eq("spring.data.gemfire.management.require-https"));
verify(mockEnvironment, times(1))
.containsProperty(eq("spring.data.gemfire.management.use-http"));
@@ -236,7 +319,309 @@ public class EnableClusterConfigurationUnitTests {
verify(mockEnvironment, times(1))
.getProperty(eq("spring.data.gemfire.management.http.port"), eq(Integer.class), anyInt());
verify(mockEnvironment, never())
.getProperty(eq("spring.data.gemfire.management.require-https"), eq(Boolean.class), anyBoolean());
verify(mockEnvironment, never())
.getProperty(eq("spring.data.gemfire.management.use-http"), eq(Boolean.class), anyBoolean());
}
@Test
public void gemfireClusterSchemaObjectInitializerBeanIsCorrect() {
BeanFactory mockBeanFactory = mock(BeanFactory.class);
ClientCache mockClientCache = mock(ClientCache.class);
Environment mockEnvironment = mock(Environment.class);
GemfireAdminOperations mockGemfireAdminOperations = mock(GemfireAdminOperations.class);
ClusterConfigurationConfiguration configuration = spy(new ClusterConfigurationConfiguration());
doReturn(mockGemfireAdminOperations).when(configuration)
.resolveGemfireAdminOperations(eq(mockEnvironment), eq(mockClientCache));
configuration.setBeanFactory(mockBeanFactory);
ClusterSchemaObjectInitializer initializer =
configuration.gemfireClusterSchemaObjectInitializer(mockEnvironment, mockClientCache);
assertThat(initializer).isNotNull();
assertThat(initializer.isAutoStartup()).isTrue();
SchemaObjectContext schemaObjectContext = initializer.getSchemaObjectContext();
assertThat(schemaObjectContext).isNotNull();
assertThat(schemaObjectContext.getGemfireAdminOperations()).isEqualTo(mockGemfireAdminOperations);
assertThat(schemaObjectContext.getSchemaObjectCollector()).isInstanceOf(ComposableSchemaObjectCollector.class);
assertThat(schemaObjectContext.getSchemaObjectDefiner()).isInstanceOf(ComposableSchemaObjectDefiner.class);
verify(configuration, never()).resolveClientHttpRequestInterceptors();
verify(configuration, times(1))
.resolveGemfireAdminOperations(eq(mockEnvironment), eq(mockClientCache));
}
@Test
public void gemfireClusterSchemaObjectInitializerBeanIsNullWhenGemFireCacheIsNull() {
ClusterConfigurationConfiguration configuration = new ClusterConfigurationConfiguration();
Environment mockEnvironment = mock(Environment.class);
assertThat(configuration.gemfireClusterSchemaObjectInitializer(mockEnvironment, null)).isNull();
}
@Test
public void gemfireClusterSchemaObjectInitializerBeanIsNullWhenGemFireCacheIsNotAClientCache() {
Cache mockPeerCache = mock(Cache.class);
ClusterConfigurationConfiguration configuration = new ClusterConfigurationConfiguration();
Environment mockEnvironment = mock(Environment.class);
assertThat(configuration.gemfireClusterSchemaObjectInitializer(mockEnvironment, mockPeerCache)).isNull();
}
@Test
public void resolvesAutowiredClientHttpRequestInterceptors() throws Exception {
BeanFactory mockBeanFactory = mock(BeanFactory.class);
ClientHttpRequestInterceptor mockInterceptorOne = mock(ClientHttpRequestInterceptor.class);
ClientHttpRequestInterceptor mockInterceptorTwo = mock(ClientHttpRequestInterceptor.class);
List<ClientHttpRequestInterceptor> clientHttpRequestInterceptors =
Arrays.asList(mockInterceptorOne, mockInterceptorTwo);
ClusterConfigurationConfiguration configuration = new ClusterConfigurationConfiguration();
configuration = autowire(configuration, "clientHttpRequestInterceptors", clientHttpRequestInterceptors);
configuration.setBeanFactory(mockBeanFactory);
assertThat(configuration.resolveClientHttpRequestInterceptors()).isEqualTo(clientHttpRequestInterceptors);
verifyZeroInteractions(mockBeanFactory);
}
@Test
public void resolvesClientHttpRequestInterceptorsFromBeanFactory() {
ListableBeanFactory mockBeanFactory = mock(ListableBeanFactory.class);
ClientHttpRequestInterceptor mockInterceptorOne = mock(ClientHttpRequestInterceptor.class);
ClientHttpRequestInterceptor mockInterceptorTwo = mock(ClientHttpRequestInterceptor.class);
Map<String, ClientHttpRequestInterceptor> expectedClientHttpRequestInterceptors = new HashMap<>();
expectedClientHttpRequestInterceptors.put("MockInterceptorOne", mockInterceptorOne);
expectedClientHttpRequestInterceptors.put("MockInterceptorTwo", mockInterceptorTwo);
when(mockBeanFactory.getBeansOfType(eq(ClientHttpRequestInterceptor.class), anyBoolean(), anyBoolean()))
.thenReturn(expectedClientHttpRequestInterceptors);
ClusterConfigurationConfiguration configuration = new ClusterConfigurationConfiguration();
configuration.setBeanFactory(mockBeanFactory);
List<ClientHttpRequestInterceptor> actualClientHttpRequestInterceptors =
configuration.resolveClientHttpRequestInterceptors();
assertThat(actualClientHttpRequestInterceptors).isNotNull();
assertThat(actualClientHttpRequestInterceptors).hasSize(expectedClientHttpRequestInterceptors.size());
assertThat(actualClientHttpRequestInterceptors)
.containsExactlyInAnyOrder(expectedClientHttpRequestInterceptors.values()
.toArray(new ClientHttpRequestInterceptor[0]));
verify(mockBeanFactory, times(1))
.getBeansOfType(eq(ClientHttpRequestInterceptor.class), eq(true), eq(false));
}
@Test
public void resolveClientHttpRequestInterceptorsReturnsNullWhenBeanFactoryIsNotAListableBeanFactory() {
BeanFactory mockBeanFactory = mock(BeanFactory.class);
ClusterConfigurationConfiguration configuration = new ClusterConfigurationConfiguration();
configuration.setBeanFactory(mockBeanFactory);
List<ClientHttpRequestInterceptor> clientHttpRequestInterceptors =
configuration.resolveClientHttpRequestInterceptors();
assertThat(clientHttpRequestInterceptors).isNotNull();
assertThat(clientHttpRequestInterceptors).isEmpty();
verifyZeroInteractions(mockBeanFactory);
}
@Test
public void resolvesAutowiredGemfireAdminOperations() throws Exception {
GemfireAdminOperations mockGemfireAdminOperations = mock(GemfireAdminOperations.class);
ClusterConfigurationConfiguration configuration = new ClusterConfigurationConfiguration();
configuration = autowire(configuration, "gemfireAdminOperations", mockGemfireAdminOperations);
assertThat(configuration.resolveGemfireAdminOperations(null, null))
.isSameAs(mockGemfireAdminOperations);
}
@Test
public void resolvesNewFunctionGemfireAdminOperations() {
ClientCache mockClientCache = mock(ClientCache.class);
ClusterConfigurationConfiguration configuration = new ClusterConfigurationConfiguration();
GemfireAdminOperations operations =
configuration.resolveGemfireAdminOperations(null, mockClientCache);
assertThat(operations).isInstanceOf(FunctionGemfireAdminTemplate.class);
verifyZeroInteractions(mockClientCache);
}
@Test
public void resolvesNewRestHttpGemfireAdminOperationsUsingDefaults() throws Exception {
ClientCache mockClientCache = mock(ClientCache.class);
Environment mockEnvironment = mock(Environment.class);
when(mockEnvironment.getProperty(anyString(), eq(Boolean.class), anyBoolean())).thenReturn(false);
ClusterConfigurationConfiguration configuration = spy(new ClusterConfigurationConfiguration());
doReturn(Collections.emptyList()).when(configuration).resolveClientHttpRequestInterceptors();
configuration.setManagementUseHttp(true);
assertThat(configuration.resolveManagementRequireHttps()).isTrue();
assertThat(configuration.resolveManagementUseHttp()).isTrue();
GemfireAdminOperations operations =
configuration.resolveGemfireAdminOperations(mockEnvironment, mockClientCache);
assertThat(operations).isInstanceOf(RestHttpGemfireAdminTemplate.class);
RestHttpGemfireAdminTemplate template = (RestHttpGemfireAdminTemplate) operations;
RestTemplate restTemplate = resolveFieldValue(template, "restTemplate");
assertThat(restTemplate).isNotNull();
assertThat(restTemplate.getInterceptors()).isEmpty();
assertThat(restTemplate.getRequestFactory()).isInstanceOf(FollowRedirectsSimpleClientHttpRequestFactory.class);
FollowRedirectsSimpleClientHttpRequestFactory clientHttpRequestFactory =
(FollowRedirectsSimpleClientHttpRequestFactory) restTemplate.getRequestFactory();
assertThat(clientHttpRequestFactory.isFollowRedirects()).isFalse();
verifyZeroInteractions(mockClientCache);
verify(configuration, times(1)).resolveClientHttpRequestInterceptors();
verify(mockEnvironment, times(1))
.getProperty(eq(ClusterConfigurationConfiguration.HTTP_FOLLOW_REDIRECTS_PROPERTY), eq(Boolean.class),
eq(ClusterConfigurationConfiguration.DEFAULT_HTTP_FOLLOW_REDIRECTS));
}
@Test
public void resolvesNewRestHttpGemfireAdminOperationsSetsFollowRedirectsWithProperty() throws Exception {
System.setProperty(ClusterConfigurationConfiguration.HTTP_FOLLOW_REDIRECTS_PROPERTY, Boolean.TRUE.toString());
ClientCache mockClientCache = mock(ClientCache.class);
Environment environment = spy(new StandardEnvironment());
ClusterConfigurationConfiguration configuration = spy(new ClusterConfigurationConfiguration());
doReturn(Collections.emptyList()).when(configuration).resolveClientHttpRequestInterceptors();
configuration.setManagementUseHttp(true);
assertThat(configuration.resolveManagementRequireHttps()).isTrue();
assertThat(configuration.resolveManagementUseHttp()).isTrue();
GemfireAdminOperations operations =
configuration.resolveGemfireAdminOperations(environment, mockClientCache);
assertThat(operations).isInstanceOf(RestHttpGemfireAdminTemplate.class);
RestHttpGemfireAdminTemplate template = (RestHttpGemfireAdminTemplate) operations;
RestTemplate restTemplate = resolveFieldValue(template, "restTemplate");
assertThat(restTemplate).isNotNull();
assertThat(restTemplate.getInterceptors()).isEmpty();
assertThat(restTemplate.getRequestFactory()).isInstanceOf(FollowRedirectsSimpleClientHttpRequestFactory.class);
FollowRedirectsSimpleClientHttpRequestFactory clientHttpRequestFactory =
(FollowRedirectsSimpleClientHttpRequestFactory) restTemplate.getRequestFactory();
assertThat(clientHttpRequestFactory.isFollowRedirects()).isTrue();
verifyZeroInteractions(mockClientCache);
verify(configuration, times(1)).resolveClientHttpRequestInterceptors();
verify(environment, times(1))
.getProperty(eq(ClusterConfigurationConfiguration.HTTP_FOLLOW_REDIRECTS_PROPERTY), eq(Boolean.class),
eq(ClusterConfigurationConfiguration.DEFAULT_HTTP_FOLLOW_REDIRECTS));
}
@Test
public void resolvesNewRestHttpGemfireAdminOperationsUsesClientHttpRequestInterceptorsSetsFollowRedirectsWhenUsingHttp()
throws Exception {
assertThat(Boolean.getBoolean(ClusterConfigurationConfiguration.HTTP_FOLLOW_REDIRECTS_PROPERTY)).isFalse();
ClientCache mockClientCache = mock(ClientCache.class);
ClientHttpRequestInterceptor mockInterceptorOne = mock(ClientHttpRequestInterceptor.class);
ClientHttpRequestInterceptor mockInterceptorTwo = mock(ClientHttpRequestInterceptor.class);
Environment environment = spy(new StandardEnvironment());
ClusterConfigurationConfiguration configuration = spy(new ClusterConfigurationConfiguration());
doReturn(Arrays.asList(mockInterceptorOne, mockInterceptorTwo))
.when(configuration).resolveClientHttpRequestInterceptors();
configuration.setManagementRequireHttps(false);
configuration.setManagementUseHttp(true);
assertThat(configuration.resolveManagementRequireHttps()).isFalse();
assertThat(configuration.resolveManagementUseHttp()).isTrue();
GemfireAdminOperations operations =
configuration.resolveGemfireAdminOperations(environment, mockClientCache);
assertThat(operations).isInstanceOf(RestHttpGemfireAdminTemplate.class);
RestHttpGemfireAdminTemplate template = (RestHttpGemfireAdminTemplate) operations;
RestTemplate restTemplate = resolveFieldValue(template, "restTemplate");
assertThat(restTemplate).isNotNull();
assertThat(restTemplate.getInterceptors()).containsExactly(mockInterceptorOne, mockInterceptorTwo);
assertThat(restTemplate.getRequestFactory()).isInstanceOf(InterceptingClientHttpRequestFactory.class);
FollowRedirectsSimpleClientHttpRequestFactory clientHttpRequestFactory =
resolveFieldValue(restTemplate.getRequestFactory(), "requestFactory");
assertThat(clientHttpRequestFactory.isFollowRedirects()).isTrue();
verifyZeroInteractions(mockClientCache);
verify(configuration, times(1)).resolveClientHttpRequestInterceptors();
verify(environment, times(1))
.getProperty(eq(ClusterConfigurationConfiguration.HTTP_FOLLOW_REDIRECTS_PROPERTY), eq(Boolean.class),
eq(ClusterConfigurationConfiguration.DEFAULT_HTTP_FOLLOW_REDIRECTS));
}
}