SGF-674 - Add EnableClusterConfiguration annotation to push cluster configuration meta-data from the client to the server.

This commit is contained in:
John Blum
2017-08-05 21:43:56 -07:00
parent cbc655cd3d
commit b2b826a1e5
54 changed files with 6129 additions and 107 deletions

View File

@@ -89,6 +89,11 @@
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<!-- Spring Data Commons -->
<dependency>
<groupId>org.springframework.data</groupId>

View File

@@ -21,23 +21,23 @@ import java.util.concurrent.ConcurrentMap;
import org.apache.geode.cache.CacheFactory;
import org.apache.geode.cache.Region;
import org.apache.geode.internal.GemFireVersion;
import org.springframework.data.gemfire.util.CacheUtils;
import org.springframework.data.gemfire.util.RegionUtils;
import org.springframework.util.ClassUtils;
/**
* GemfireUtils is an abstract utility class encapsulating common functionality to access features and capabilities
* of GemFire based on version and other configuration meta-data.
* {@link GemfireUtils} is an abstract utility class encapsulating common functionality to access features
* and capabilities of GemFire based on version and other configuration meta-data.
*
* @author John Blum
* @see org.springframework.data.gemfire.util.DistributedSystemUtils
* @see org.apache.geode.cache.CacheFactory
* @see org.apache.geode.cache.Region
* @see org.springframework.data.gemfire.util.RegionUtils
* @since 1.3.3
*/
@SuppressWarnings("unused")
public abstract class GemfireUtils extends CacheUtils {
public abstract class GemfireUtils extends RegionUtils {
public final static String APACHE_GEODE_NAME = "Aache Geode";
public final static String APACHE_GEODE_NAME = "Apache Geode";
public final static String GEMFIRE_NAME = apacheGeodeProductName();
public final static String GEMFIRE_VERSION = apacheGeodeVersion();
public final static String UNKNOWN = "unknown";

View File

@@ -78,6 +78,7 @@ public class GemfireDataSourcePostProcessor implements BeanFactoryPostProcessor
}
/* (non-Javadoc) */
// TODO remove this logic and delegate to o.s.d.g.config.remote.GemfireAdminOperations
Iterable<String> regionNames() {
try {
return execute(new ListRegionsOnServerFunction());

View File

@@ -0,0 +1,119 @@
/*
* Copyright 2017 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;
import org.apache.geode.cache.DiskStore;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.lucene.LuceneIndex;
import org.apache.geode.cache.query.Index;
import org.springframework.data.gemfire.config.schema.SchemaObjectDefinition;
import org.springframework.data.gemfire.config.schema.definitions.IndexDefinition;
import org.springframework.data.gemfire.config.schema.definitions.RegionDefinition;
/**
* {@link AbstractGemfireAdminOperations} is an abstract base class encapsulating common functionality
* supporting administrative (management) operations against a Pivotal GemFire or Apache Geode cluster.
*
* @author John Blum
* @see org.springframework.data.gemfire.config.admin.GemfireAdminOperations
* @see org.springframework.data.gemfire.config.schema.SchemaObjectDefinition
* @since 2.0.0
*/
public class AbstractGemfireAdminOperations implements GemfireAdminOperations {
protected static final String NOT_IMPLEMENTED = "Not Implemented";
/**
* Returns a {@link Iterable collection} of {@link Region} names defined on the GemFire Servers in the cluster.
*
* @return an {@link Iterable} of {@link Region} names defined on the GemFire Servers in the cluster.
* @see org.apache.geode.cache.Region#getName()
* @see java.lang.Iterable
*/
@Override
public Iterable<String> getAvailableServerRegions() {
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
}
/**
* Returns an {@link Iterable} of all the server {@link Region} {@link Index Indexes}.
*
* @return an {@link Iterable} of all the server {@link Region} {@link Index Indexes}.
* @see org.apache.geode.cache.query.Index#getName()
* @see java.lang.Iterable
*/
@Override
public Iterable<String> getAvailableServerRegionIndexes() {
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
}
/**
* Creates a cache {@link Region} based on the given {@link RegionDefinition schema object definition}.
*
* @param regionDefinition {@link RegionDefinition} encapsulating configuration meta-data defining
* a cache {@link Region}.
* @see org.springframework.data.gemfire.config.schema.definitions.RegionDefinition
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.Region
*/
@Override
public void createRegion(RegionDefinition regionDefinition) {
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
}
/**
* Creates a {@link Region} {@link LuceneIndex} based on the given
* {@link SchemaObjectDefinition schema object definition}.
*
* @param luceneIndexDefinition {@link SchemaObjectDefinition} encapsulating the configuration meta-data
* defining a {@link Region} {@link LuceneIndex}.
* @see org.springframework.data.gemfire.config.schema.SchemaObjectDefinition
* @see org.apache.geode.cache.lucene.LuceneIndex
* @see org.apache.geode.cache.Region
*/
@Override
public void createLuceneIndex(SchemaObjectDefinition luceneIndexDefinition) {
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
}
/**
* Creates a {@link Region} OQL {@link Index} based on the given {@link IndexDefinition schema object definition}.
*
* @param indexDefinition {@link IndexDefinition} encapsulating the configuration meta-data
* defining a {@link Region} OQL {@link Index}.
* @see org.springframework.data.gemfire.config.schema.definitions.IndexDefinition
* @see org.apache.geode.cache.query.Index
* @see org.apache.geode.cache.Region
*/
@Override
public void createIndex(IndexDefinition indexDefinition) {
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
}
/**
* Creates a {@link DiskStore} based on the given {@link SchemaObjectDefinition schema object definition}.
*
* @param diskStoreDefinition {@link SchemaObjectDefinition} encapsulating the configuration meta-data
* defining a {@link DiskStore}.
* @see org.springframework.data.gemfire.config.schema.SchemaObjectDefinition
* @see org.apache.geode.cache.DiskStore
*/
@Override
public void createDiskStore(SchemaObjectDefinition diskStoreDefinition) {
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
}
}

View File

@@ -0,0 +1,141 @@
/*
* Copyright 2017 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;
import static java.util.Arrays.stream;
import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeIterable;
import org.apache.geode.cache.DiskStore;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.lucene.LuceneIndex;
import org.apache.geode.cache.query.Index;
import org.springframework.data.gemfire.config.schema.SchemaObjectDefinition;
import org.springframework.data.gemfire.config.schema.definitions.IndexDefinition;
import org.springframework.data.gemfire.config.schema.definitions.RegionDefinition;
/**
* The {@link GemfireAdminOperations} interface defines a set of operations to define schema objects in a remote
* Apache Geode or Pivotal GemFire cluster.
*
* @author John Blum
* @see org.apache.geode.cache.DiskStore
* @see org.apache.geode.cache.Region
* @see org.apache.geode.cache.lucene.LuceneIndex
* @see org.apache.geode.cache.query.Index
* @see org.springframework.data.gemfire.config.schema.definitions.IndexDefinition
* @see org.springframework.data.gemfire.config.schema.definitions.RegionDefinition
* @see org.springframework.data.gemfire.config.schema.SchemaObjectDefinition
* @since 2.0.0
*/
@SuppressWarnings("unused")
public interface GemfireAdminOperations {
/**
* Returns a {@link Iterable collection} of {@link Region} names defined on the GemFire Servers in the cluster.
*
* @return an {@link Iterable} of {@link Region} names defined on the GemFire Servers in the cluster.
* @see org.apache.geode.cache.Region#getName()
* @see java.lang.Iterable
*/
Iterable<String> getAvailableServerRegions();
/**
* Returns an {@link Iterable} of all the server {@link Region} {@link Index Indexes}.
*
* @return an {@link Iterable} of all the server {@link Region} {@link Index Indexes}.
* @see org.apache.geode.cache.query.Index#getName()
* @see java.lang.Iterable
*/
Iterable<String> getAvailableServerRegionIndexes();
/**
* Creates a cache {@link Region} based on the given {@link RegionDefinition schema object definition}.
*
* @param regionDefinition {@link RegionDefinition} encapsulating configuration meta-data defining
* a cache {@link Region}.
* @see org.springframework.data.gemfire.config.schema.definitions.RegionDefinition
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.Region
*/
void createRegion(RegionDefinition regionDefinition);
default void createRegions(RegionDefinition... regionDefinitions) {
stream(nullSafeArray(regionDefinitions, RegionDefinition.class)).forEach(this::createRegion);
}
default void createRegions(Iterable<RegionDefinition> regionDefinitions) {
nullSafeIterable(regionDefinitions).forEach(this::createRegion);
}
/**
* Creates a {@link Region} {@link LuceneIndex} based on the given
* {@link SchemaObjectDefinition schema object definition}.
*
* @param luceneIndexDefinition {@link SchemaObjectDefinition} encapsulating the configuration meta-data
* defining a {@link Region} {@link LuceneIndex}.
* @see org.springframework.data.gemfire.config.schema.SchemaObjectDefinition
* @see org.apache.geode.cache.lucene.LuceneIndex
* @see org.apache.geode.cache.Region
*/
void createLuceneIndex(SchemaObjectDefinition luceneIndexDefinition);
default void createLuceneIndexes(SchemaObjectDefinition... luceneIndexDefinitions) {
stream(nullSafeArray(luceneIndexDefinitions, SchemaObjectDefinition.class)).forEach(this::createLuceneIndex);
}
default void createLuceneIndexes(Iterable<SchemaObjectDefinition> luceneIndexDefinitions) {
nullSafeIterable(luceneIndexDefinitions).forEach(this::createLuceneIndex);
}
/**
* Creates a {@link Region} OQL {@link Index} based on the given {@link IndexDefinition schema object definition}.
*
* @param indexDefinition {@link IndexDefinition} encapsulating the configuration meta-data
* defining a {@link Region} OQL {@link Index}.
* @see org.springframework.data.gemfire.config.schema.definitions.IndexDefinition
* @see org.apache.geode.cache.query.Index
* @see org.apache.geode.cache.Region
*/
void createIndex(IndexDefinition indexDefinition);
default void createIndexes(IndexDefinition... indexDefinitions) {
stream(nullSafeArray(indexDefinitions, IndexDefinition.class)).forEach(this::createIndex);
}
default void createIndexes(Iterable<IndexDefinition> indexDefinitions) {
nullSafeIterable(indexDefinitions).forEach(this::createIndex);
}
/**
* Creates a {@link DiskStore} based on the given {@link SchemaObjectDefinition schema object definition}.
*
* @param diskStoreDefinition {@link SchemaObjectDefinition} encapsulating the configuration meta-data
* defining a {@link DiskStore}.
* @see org.springframework.data.gemfire.config.schema.SchemaObjectDefinition
* @see org.apache.geode.cache.DiskStore
*/
void createDiskStore(SchemaObjectDefinition diskStoreDefinition);
default void createDiskStores(SchemaObjectDefinition... diskStoreDefinitions) {
stream(nullSafeArray(diskStoreDefinitions, SchemaObjectDefinition.class)).forEach(this::createDiskStore);
}
default void createDiskStores(Iterable<SchemaObjectDefinition> diskStoreDefinitions) {
nullSafeIterable(diskStoreDefinitions).forEach(this::createDiskStore);
}
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright 2017 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.functions;
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeCollection;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.CacheFactory;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.query.QueryException;
import org.apache.geode.cache.query.QueryService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.gemfire.GemfireCacheUtils;
import org.springframework.data.gemfire.config.schema.definitions.IndexDefinition;
import org.springframework.data.gemfire.function.annotation.GemfireFunction;
/**
* The CreateIndexFunction class...
*
* @author John Blum
* @since 1.0.0
*/
public class CreateIndexFunction {
public static final String CREATE_INDEX_FUNCTION_ID = "CreateOqlIndexFunction";
private final Logger logger = LoggerFactory.getLogger(getClass());
@GemfireFunction(id = CREATE_INDEX_FUNCTION_ID)
public boolean createIndex(IndexDefinition indexDefinition) {
Cache gemfireCache = resolveCache();
if (isNonExistingIndex(gemfireCache, indexDefinition)) {
if (logger.isInfoEnabled()) {
logger.info("Creating Index with name [{}] having expression [{}] on Region [{}] with type [{}]",
indexDefinition.getName(), indexDefinition.getExpression(), indexDefinition.getFromClause(),
indexDefinition.getIndexType());
}
QueryService queryService = gemfireCache.getQueryService();
try {
switch (indexDefinition.getIndexType()) {
case KEY:
case PRIMARY_KEY:
queryService.createKeyIndex(indexDefinition.getName(),
indexDefinition.getExpression(), indexDefinition.getFromClause());
return true;
case HASH:
queryService.createHashIndex(indexDefinition.getName(),
indexDefinition.getExpression(), indexDefinition.getFromClause());
return true;
case FUNCTIONAL:
queryService.createIndex(indexDefinition.getName(),
indexDefinition.getExpression(), indexDefinition.getFromClause());
return true;
default:
return false;
}
}
catch (QueryException cause) {
throw GemfireCacheUtils.convertGemfireAccessException(cause);
}
}
else {
if (logger.isInfoEnabled()) {
logger.info("Index with name [{}] already exists", indexDefinition.getName());
}
return false;
}
}
protected Cache resolveCache() {
return CacheFactory.getAnyInstance();
}
protected boolean isNonExistingIndex(GemFireCache gemfireCache, IndexDefinition indexDefinition) {
return !nullSafeCollection(gemfireCache.getQueryService().getIndexes()).stream()
.anyMatch(index -> index.getName().equals(indexDefinition.getName()));
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2017 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.functions;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.CacheFactory;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.gemfire.config.schema.definitions.RegionDefinition;
import org.springframework.data.gemfire.function.annotation.GemfireFunction;
/**
* The CreateRegionFunction class...
*
* @author John Blum
* @since 1.0.0
*/
public class CreateRegionFunction {
public static final String CREATE_REGION_FUNCTION_ID = "CreateRegionFunction";
private final Logger logger = LoggerFactory.getLogger(getClass());
@GemfireFunction(id = CREATE_REGION_FUNCTION_ID)
public boolean createRegion(RegionDefinition regionDefinition) {
Cache gemfireCache = resolveCache();
if (isNonExistingRegion(gemfireCache, regionDefinition)) {
RegionFactory regionFactory = gemfireCache.createRegionFactory(regionDefinition.getRegionShortcut());
Region region = regionFactory.create(regionDefinition.getName());
if (logger.isInfoEnabled()) {
logger.info("Created Region [{}] of type [{}]", region.getName(), region.getAttributes().getDataPolicy());
}
return true;
}
else {
if (logger.isDebugEnabled()) {
logger.info("Region with name [{}] already exists", regionDefinition.getName());
}
return false;
}
}
protected Cache resolveCache() {
return CacheFactory.getAnyInstance();
}
private boolean isNonExistingRegion(Cache gemfireCache, RegionDefinition regionDefinition) {
return (gemfireCache.getRegion(regionDefinition.getName()) == null);
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2017 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.functions;
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeCollection;
import java.util.Collections;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.CacheFactory;
import org.apache.geode.cache.query.Index;
import org.springframework.data.gemfire.function.annotation.GemfireFunction;
/**
* The ListIndexesFunction class...
*
* @author John Blum
* @since 1.0.0
*/
@SuppressWarnings("unused")
public class ListIndexesFunction {
public static final String LIST_INDEXES_FUNCTION_ID = "ListQqlIndexesFunction";
@GemfireFunction(id = LIST_INDEXES_FUNCTION_ID)
public Set<String> listIndexes() {
return Optional.ofNullable(resolveCache())
.map(cache -> cache.getQueryService())
.map(queryService ->
nullSafeCollection(queryService.getIndexes()).stream().map(Index::getName).collect(Collectors.toSet()))
.orElseGet(Collections::emptySet);
}
protected Cache resolveCache() {
return CacheFactory.getAnyInstance();
}
}

View File

@@ -0,0 +1,171 @@
/*
* Copyright 2017 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 java.util.Arrays.stream;
import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
import java.util.Collections;
import java.util.Optional;
import java.util.stream.Collectors;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.execute.Function;
import org.apache.geode.cache.query.Index;
import org.apache.geode.management.internal.cli.domain.RegionInformation;
import org.apache.geode.management.internal.cli.functions.GetRegionsFunction;
import org.springframework.data.gemfire.client.function.ListRegionsOnServerFunction;
import org.springframework.data.gemfire.config.admin.AbstractGemfireAdminOperations;
import org.springframework.data.gemfire.config.admin.GemfireAdminOperations;
import org.springframework.data.gemfire.config.admin.functions.CreateIndexFunction;
import org.springframework.data.gemfire.config.admin.functions.CreateRegionFunction;
import org.springframework.data.gemfire.config.admin.functions.ListIndexesFunction;
import org.springframework.data.gemfire.config.schema.definitions.IndexDefinition;
import org.springframework.data.gemfire.config.schema.definitions.RegionDefinition;
import org.springframework.data.gemfire.function.execution.GemfireFunctionOperations;
import org.springframework.data.gemfire.function.execution.GemfireOnServersFunctionTemplate;
import org.springframework.util.Assert;
/**
* The {@link FunctionGemfireAdminTemplate} class is an implementation of the {@link GemfireAdminOperations} interface
* supporting the Pivotal GemFire / Apache Geode administrative functions/operations via {@link Function} execution
* in the cluster.
*
* Note: any schema changing functionality (e.g. {@link #createRegion(RegionDefinition)}) does not get recorded by
* the GemFire/Geode Cluster Configuration Service using this strategy.
*
* @author John Blum
* @see org.apache.geode.cache.client.ClientCache
* @see org.apache.geode.cache.execute.Function
* @see org.springframework.data.gemfire.client.function.ListRegionsOnServerFunction
* @see org.springframework.data.gemfire.config.admin.AbstractGemfireAdminOperations
* @see org.springframework.data.gemfire.function.execution.GemfireOnServersFunctionTemplate
* @since 2.0.0
*/
public class FunctionGemfireAdminTemplate extends AbstractGemfireAdminOperations {
private final ClientCache clientCache;
/**
* Constructs a new instance of the {@link FunctionGemfireAdminTemplate} initialized with
* a {@link ClientCache} instance.
*
* @param clientCache reference to a {@link ClientCache} instance.
* @throws IllegalArgumentException if {@link ClientCache} is {@literal null}.
* @see org.apache.geode.cache.client.ClientCache
*/
public FunctionGemfireAdminTemplate(ClientCache clientCache) {
Assert.notNull(clientCache, "ClientCache is required");
this.clientCache = clientCache;
}
/**
* Returns a reference to the configured {@link ClientCache} instance.
*
* @return a reference to the configured {@link ClientCache} instance.
* @see org.apache.geode.cache.client.ClientCache
*/
protected ClientCache getClientCache() {
return this.clientCache;
}
/**
* Lists all available {@link Region Regions} configured for all servers in the remote Pivotal GemFire
* / Apache Geode cluster.
*
* @return an {@link Iterable} of servers-side {@link Region} names for all {@link Region Regions} defined
* across all servers in the remote GemFire/Geode cluster.
* @see java.lang.Iterable
*/
@Override
public Iterable<String> getAvailableServerRegions() {
try {
return execute(new ListRegionsOnServerFunction());
}
catch (Exception cause) {
try {
return Optional.ofNullable(execute(new GetRegionsFunction()))
.filter(this::containsRegionInformation)
.map(regionInformationArray ->
stream(nullSafeArray((Object[]) regionInformationArray, Object.class))
.map(regionInformation -> ((RegionInformation) regionInformation).getName())
.collect(Collectors.toSet())
)
.orElse(Collections.emptySet());
}
catch (Exception ignore) {
return Collections.emptySet();
}
}
}
/**
* Returns an {@link Iterable} of all the server {@link Region} {@link Index Indexes}.
*
* @return an {@link Iterable} of all the server {@link Region} {@link Index Indexes}.
* @see org.apache.geode.cache.query.Index#getName()
* @see java.lang.Iterable
*/
@Override
public Iterable<String> getAvailableServerRegionIndexes() {
return execute(ListIndexesFunction.LIST_INDEXES_FUNCTION_ID);
}
@Override
public void createRegion(RegionDefinition regionDefinition) {
execute(CreateRegionFunction.CREATE_REGION_FUNCTION_ID, regionDefinition);
}
@Override
public void createIndex(IndexDefinition indexDefinition) {
execute(CreateIndexFunction.CREATE_INDEX_FUNCTION_ID, indexDefinition);
}
/* (non-Javadoc) */
<T> T execute(Function gemfireFunction, Object... arguments) {
return newGemfireFunctionOperations().executeAndExtract(gemfireFunction, arguments);
}
/* (non-Javadoc) */
<T> T execute(String gemfireFunctionId, Object... arguments) {
return newGemfireFunctionOperations().executeAndExtract(gemfireFunctionId, arguments);
}
/* (non-Javadoc) */
protected GemfireFunctionOperations newGemfireFunctionOperations() {
return newGemfireFunctionOperations(getClientCache());
}
/* (non-Javadoc) */
protected GemfireFunctionOperations newGemfireFunctionOperations(ClientCache clientCache) {
return new GemfireOnServersFunctionTemplate(clientCache);
}
/* (non-Javadoc) */
boolean containsRegionInformation(Object results) {
return Optional.ofNullable(results)
.filter(it -> it instanceof Object[])
.filter(it -> ((Object[]) it).length > 0)
.filter(it -> ((Object[]) it)[0] instanceof RegionInformation)
.isPresent();
}
}

View File

@@ -0,0 +1,198 @@
/*
* Copyright 2017 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 java.net.URI;
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.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.SimpleClientHttpRequestFactory;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestOperations;
import org.springframework.web.client.RestTemplate;
/**
* {@link RestHttpGemfireAdminTemplate} is class implementing the {@link GemfireAdminOperations} interface,
* extending the {@link FunctionGemfireAdminTemplate} to support administrative (management) operations
* on a Pivotal GemFire or Apache Geode cluster using the Management REST API interface over HTTP.
*
* The fallback is using {@link Function} execution if the a particular administrative (management) operation
* is not supported or has not been implemented against the Management REST API interface over HTTP.
*
* @author John Blum
* @see 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
* @since 2.0.0
*/
public class RestHttpGemfireAdminTemplate extends FunctionGemfireAdminTemplate {
protected static final boolean CREATE_REGION_SKIP_IF_EXISTS_DEFAULT = true;
protected static final int DEFAULT_PORT = 7070;
protected static final String DEFAULT_HOST = "localhost";
protected static final String MANAGEMENT_REST_API_URL_TEMPLATE = "http://%1$s:%2$d/gemfire/v1/";
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.
*
* @param clientCache reference to the {@link ClientCache}
* @throws IllegalArgumentException if the {@link ClientCache} reference is {@literal null}.
* @see org.apache.geode.cache.client.ClientCache
*/
public RestHttpGemfireAdminTemplate(ClientCache clientCache) {
this(clientCache, DEFAULT_HOST, DEFAULT_PORT);
}
/**
* 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.
*
* @param clientCache reference to the {@link ClientCache}
* @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.
* @throws IllegalArgumentException if the {@link ClientCache} reference is {@literal null}.
* @see org.apache.geode.cache.client.ClientCache
*/
public RestHttpGemfireAdminTemplate(ClientCache clientCache, String host, int port) {
super(clientCache);
this.restTemplate = newRestOperations();
this.managementRestApiUrl = resolveManagementRestApiUrl(host, port);
}
/**
* Constructs a new instance of the Spring {@link RestTemplate} to perform REST API operations over HTTP.
*
* @return a new instance of Spring's {@link RestTemplate}.
* @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());
}
/**
* Resolves the Pivotal GemFire or Apache Geode Management REST API URL given the host and port
* of the GemFire/Geode Manager's embedded HTTP service.
*
* @param host {@link String} containing the hostname of the Manager running the embedded HTTP service
* and Management REST API.
* @param port integer specifying the port that the embedded Manager's HTTP service is listening on.
* @return the resolved URL.
*/
private String resolveManagementRestApiUrl(String host, int port) {
return String.format(MANAGEMENT_REST_API_URL_TEMPLATE, host, port);
}
/**
* Returns a reference to the resolved GemFire/Geode Management REST API URL.
*
* @return a {@link String} containing the resolved GemFire/Geode Management REST API URL.
*/
protected String getManagementRestApiUrl() {
return this.managementRestApiUrl;
}
/**
* Returns a reference to the {@link RestOperations} used to perform REST API calls.
*
* @return a reference to the {@link RestOperations} used to perform REST API calls.
* @see org.springframework.web.client.RestOperations
*/
protected RestOperations getRestOperations() {
return this.restTemplate;
}
@Override
public void createIndex(IndexDefinition indexDefinition) {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
// HTTP Message Body
MultiValueMap<String, Object> requestParameters = new LinkedMultiValueMap<>();
requestParameters.add("name", indexDefinition.getName());
requestParameters.add("expression", indexDefinition.getExpression());
requestParameters.add("region", indexDefinition.getFromClause());
requestParameters.add("type", indexDefinition.getIndexType().toString());
RequestEntity<MultiValueMap<String, Object>> requestEntity =
new RequestEntity<>(requestParameters, 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")
HttpStatus.OK.equals(response.getStatusCode());
}
protected URI resolveCreateIndexUri() {
return URI.create(getManagementRestApiUrl().concat("indexes"));
}
@Override
public void createRegion(RegionDefinition regionDefinition) {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
// HTTP Message Body
MultiValueMap<String, Object> requestParameters = 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));
RequestEntity<MultiValueMap<String, Object>> requestEntity =
new RequestEntity<>(requestParameters, 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")
HttpStatus.OK.equals(response.getStatusCode());
}
protected URI resolveCreateRegionUri() {
return URI.create(getManagementRestApiUrl().concat("regions"));
}
}

View File

@@ -0,0 +1,286 @@
/*
* Copyright 2017 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 java.util.stream.StreamSupport.stream;
import static org.springframework.data.gemfire.util.CacheUtils.isClient;
import java.lang.annotation.Annotation;
import java.util.Optional;
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.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
import org.springframework.context.event.ApplicationContextEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.core.OrderComparator;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.gemfire.GemfireUtils;
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.annotation.support.AbstractAnnotationConfigSupport;
import org.springframework.data.gemfire.config.schema.SchemaObjectCollector;
import org.springframework.data.gemfire.config.schema.SchemaObjectDefiner;
import org.springframework.data.gemfire.config.schema.definitions.RegionDefinition;
import org.springframework.data.gemfire.config.schema.support.ClientRegionCollector;
import org.springframework.data.gemfire.config.schema.support.ComposableSchemaObjectCollector;
import org.springframework.data.gemfire.config.schema.support.ComposableSchemaObjectDefiner;
import org.springframework.data.gemfire.config.schema.support.IndexCollector;
import org.springframework.data.gemfire.config.schema.support.IndexDefiner;
import org.springframework.data.gemfire.config.schema.support.RegionDefiner;
/**
* Spring {@link Configuration @Configuration} class defining Spring beans that will record the creation of
* Apache Geode / Pivotal GemFire {@link Region Regions} defined in Spring config (i.e. XML, Java or by Annotations)
* as Spring beans in the Spring container.
*
* @author John Blum
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.Region
* @see org.apache.geode.cache.query.Index
* @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
* @since 2.0.0
*/
@Configuration
@SuppressWarnings("unused")
public class ClusterConfigurationConfiguration extends AbstractAnnotationConfigSupport implements ImportAware {
protected static final boolean DEFAULT_MANAGEMENT_USE_HTTP = false;
protected static final int DEFAULT_MANAGEMENT_HTTP_PORT = HttpServiceConfiguration.DEFAULT_HTTP_SERVICE_PORT;
protected static final String DEFAULT_MANAGEMENT_HTTP_HOST = "localhost";
private static final RegionShortcut DEFAULT_SERVER_REGION_SHORTCUT = RegionDefinition.DEFAULT_REGION_SHORTCUT;
private Boolean useHttp = DEFAULT_MANAGEMENT_USE_HTTP;
private Integer managementHttpPort = DEFAULT_MANAGEMENT_HTTP_PORT;
private RegionShortcut serverRegionShortcut;
private String managementHttpHost = DEFAULT_MANAGEMENT_HTTP_HOST;
@Override
protected Class<? extends Annotation> getAnnotationType() {
return EnableClusterConfiguration.class;
}
protected void setManagementHttpHost(String hostname) {
this.managementHttpHost = hostname;
}
protected Optional<String> getManagementHttpHost() {
return Optional.ofNullable(this.managementHttpHost);
}
protected String resolveManagementHttpHost() {
return getManagementHttpHost().orElse(DEFAULT_MANAGEMENT_HTTP_HOST);
}
protected void setManagementHttpPort(Integer managementHttpPort) {
this.managementHttpPort = managementHttpPort;
}
protected Optional<Integer> getManagementHttpPort() {
return Optional.ofNullable(this.managementHttpPort);
}
protected int resolveManagementHttpPort() {
return getManagementHttpPort().orElse(DEFAULT_MANAGEMENT_HTTP_PORT);
}
protected void setManagementUseHttp(Boolean useHttp) {
this.useHttp = useHttp;
}
protected Optional<Boolean> getManagementUseHttp() {
return Optional.ofNullable(this.useHttp);
}
protected boolean resolveManagementUseHttp() {
return getManagementUseHttp().orElse(DEFAULT_MANAGEMENT_USE_HTTP);
}
protected void setServerRegionShortcut(RegionShortcut regionShortcut) {
this.serverRegionShortcut = regionShortcut;
}
protected Optional<RegionShortcut> getServerRegionShortcut() {
return Optional.ofNullable(this.serverRegionShortcut);
}
protected RegionShortcut resolveServerRegionShortcut() {
return getServerRegionShortcut().orElse(DEFAULT_SERVER_REGION_SHORTCUT);
}
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
if (isAnnotationPresent(importMetadata)) {
AnnotationAttributes enableClusterConfigurationAttributes = getAnnotationAttributes(importMetadata);
setManagementHttpHost(resolveProperty(managementProperty("http.hostname"),
DEFAULT_MANAGEMENT_HTTP_HOST));
setManagementHttpPort(resolveProperty(managementProperty("http.port"),
DEFAULT_MANAGEMENT_HTTP_PORT));
setManagementUseHttp(resolveProperty(managementProperty("use-http"),
DEFAULT_MANAGEMENT_USE_HTTP));
setServerRegionShortcut(resolveProperty(clusterProperty("region.type"),
RegionShortcut.class, DEFAULT_SERVER_REGION_SHORTCUT));
}
}
@EventListener
public void gemfireClusterSchemaCreationHandler(ContextRefreshedEvent event) {
GemFireCache gemfireCache = resolveGemFireCache(event);
if (isClient(gemfireCache)) {
GemfireAdminOperations gemfireAdminOperations = newGemfireAdminOperations((ClientCache) gemfireCache);
SchemaObjectDefiner schemaObjectDefiner = newSchemaObjectDefiner();
Iterable<?> schemaObjects = newSchemaObjectCollector().collectFrom(resolveApplicationContext(event));
stream(schemaObjects.spliterator(), false)
.map(schemaObjectDefiner::define)
.sorted(OrderComparator.INSTANCE)
.forEach(schemaObjectDefinition ->
schemaObjectDefinition.ifPresent(it -> it.create(gemfireAdminOperations)));
}
/*
else if (isPeer(gemfireCache)) {
GemfireFunctionUtils.registerFunctionForPojoMethod(new CreateRegionFunction(),
CreateRegionFunction.CREATE_REGION_FUNCTION_ID);
GemfireFunctionUtils.registerFunctionForPojoMethod(new CreateIndexFunction(),
CreateIndexFunction.CREATE_INDEX_FUNCTION_ID);
}
*/
}
/**
* Resolves a reference to the Spring {@link ApplicationContext} from the given {@link ApplicationContextEvent}.
*
* @param event {@link ApplicationContextEvent} from which to resolve the Spring {@link ApplicationContext}.
* @return the resolved Spring {@link ApplicationContext}.
* @see org.springframework.context.event.ApplicationContextEvent
* @see org.springframework.context.ApplicationContext
*/
private ApplicationContext resolveApplicationContext(ApplicationContextEvent event) {
return event.getApplicationContext();
}
/**
* Tries to resolve the {@link GemFireCache} from the Spring {@link ApplicationContext}.
* The {@link GemFireCache} will be resolvable from the Spring {@link ApplicationContext} if the cache
* was registered a managed bean in the Spring container.
*
* If the {@link GemFireCache} cannot be resolved from the {@link ApplicationContext}, this method will attempt
* to resolve the cache reference from GemFire's global context using the GemFire API.
*
* @param event Spring {@link ApplicationContextEvent} encapsulating the details of the Spring container event.
* @return the resolved {@link GemFireCache} if available.
* @see org.springframework.context.event.ApplicationContextEvent
* @see org.apache.geode.cache.GemFireCache
*/
private GemFireCache resolveGemFireCache(ApplicationContextEvent event) {
try {
return resolveApplicationContext(event).getBean(GemFireCache.class);
}
catch (BeansException ignore) {
return GemfireUtils.resolveGemFireCache();
}
}
/**
* Constructs an 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 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
*/
private GemfireAdminOperations newGemfireAdminOperations(ClientCache clientCache) {
if (resolveManagementUseHttp()) {
String host = resolveManagementHttpHost();
int port = resolveManagementHttpPort();
return new RestHttpGemfireAdminTemplate(clientCache, host, port);
}
else {
return new FunctionGemfireAdminTemplate(clientCache);
}
}
/**
* Constructs an 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
* in search of specific GemFire schema objects (e.g. {@link Region} or {@link Index}).
* @see org.springframework.data.gemfire.config.schema.SchemaObjectCollector
*/
private SchemaObjectCollector<?> newSchemaObjectCollector() {
return ComposableSchemaObjectCollector.compose(
new ClientRegionCollector(),
new IndexCollector()
);
}
/**
* Constructs an instance of {@link SchemaObjectDefiner} used to reverse engineer a GemFire schema object instance
* to build a definition.
*
* @return a new instance of {@link SchemaObjectDefiner}.
* @see org.springframework.data.gemfire.config.schema.SchemaObjectDefiner
*/
private SchemaObjectDefiner newSchemaObjectDefiner() {
return ComposableSchemaObjectDefiner.compose(
new RegionDefiner(resolveServerRegionShortcut()),
new IndexDefiner()
);
}
}

View File

@@ -0,0 +1,102 @@
/*
* Copyright 2017 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 java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionShortcut;
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.
*
* @author John Blum
* @see java.lang.annotation.Documented
* @see java.lang.annotation.Inherited
* @see java.lang.annotation.Retention
* @see java.lang.annotation.Target
* @see org.springframework.context.annotation.Import
* @see org.springframework.data.gemfire.config.annotation.ClusterConfigurationConfiguration
* @since 2.0.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Import(ClusterConfigurationConfiguration.class)
@SuppressWarnings("unused")
public @interface EnableClusterConfiguration {
/**
* Configures the bind address used by the Spring, GemFire/Geode cache client application to locate
* the Manager's HTTP Service and access the Management REST API. This configuration setting is only used
* when {@link #useHttp()} is set to {@literal true}.
*
* Alternatively, you can configure this setting using the {@literal spring.data.gemfire.management.http.host}
* property in {@literal application.properties}.
*
* Defaults to {@literal localhost}.
*/
String host() default ClusterConfigurationConfiguration.DEFAULT_MANAGEMENT_HTTP_HOST;
/**
* Configures the port used by the Spring, GemFire/Geode cache client application to locate
* the Manager's HTTP Service and access the Management REST API. This configuration setting is only used
* when {@link #useHttp()} is set to {@literal true}.
*
* Alternatively, you can configure this setting using the {@literal spring.data.gemfire.management.http.port}
* property in {@literal application.properties}.
*
* Defaults to {@literal 7070}.
*/
int port() default ClusterConfigurationConfiguration.DEFAULT_MANAGEMENT_HTTP_PORT;
/**
* Configuration setting used to specify the data management policy used when creating {@link Region Regions}
* on the servers in the Geode/GemFire cluster.
*
* The data management policy is expressed with a {@link RegionShortcut}, but corresponds to the various
* different {@link DataPolicy DataPolicies} available.
*
* Alternatively, you can configure this setting using the {@literal spring.data.gemfire.cluster.region.type}
* property in {@literal application.properties}.
*
* Defaults to {@link RegionShortcut#PARTITION}.
*/
RegionShortcut serverRegionShortcut() default RegionShortcut.PARTITION;
/**
* Configures whether connectivity between the Spring, GemFire/Geode application should be established using HTTP.
*
* Alternatively, you can configure this setting using the {@literal spring.data.gemfire.management.use-http}
* property in {@literal application.properties}.
*
* Defaults to {@literal false}.
*/
boolean useHttp() default ClusterConfigurationConfiguration.DEFAULT_MANAGEMENT_USE_HTTP;
}

View File

@@ -541,6 +541,11 @@ public abstract class AbstractAnnotationConfigSupport
return String.format("%1$s%2$s.%3$s", propertyName("cache.server."), name, propertyNameSuffix);
}
/* (non-Javadoc) */
protected String clusterProperty(String propertyNameSuffix) {
return String.format("%1$s%2$s", propertyName("cluster."), propertyNameSuffix);
}
/* (non-Javadoc) */
protected String diskStoreProperty(String propertyNameSuffix) {
return String.format("%1$s%2$s", propertyName("disk.store."), propertyNameSuffix);
@@ -561,6 +566,11 @@ public abstract class AbstractAnnotationConfigSupport
return String.format("%1$s%2$s", propertyName("logging."), propertyNameSuffix);
}
/* (non-Javadoc) */
protected String managementProperty(String propertyNameSuffix) {
return String.format("%1$s%2$s", propertyName("management."), propertyNameSuffix);
}
/* (non-Javadoc) */
protected String managerProperty(String propertyNameSuffix) {
return String.format("%1$s%2$s", propertyName("manager."), propertyNameSuffix);

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2017 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.schema;
import java.util.Collections;
import java.util.Set;
import org.apache.geode.cache.GemFireCache;
import org.springframework.context.ApplicationContext;
/**
* The {@link SchemaObjectCollector} interface defines a contract for implementing objects to search for
* and find all schema objects of a particular type in a given context.
*
* Implementations of this interface know how to inspect the given context and find all references
* to the schema object instances of a particular type.
*
* @author John Blum
* @see org.apache.geode.cache.GemFireCache
* @see org.springframework.context.ApplicationContext
* @since 2.0.0
*/
public interface SchemaObjectCollector<T> {
/**
* Collects all schema objects of type {@link T} declared in the given {@link ApplicationContext}.
*
* @param applicationContext Spring {@link ApplicationContext} from which to collect schema objects
* of type {@link T}.
* @return a {@link Set} of all schema objects of type {@link T} declared in the {@link ApplicationContext};
* returns an empty {@link Set} if no schema object of type {@link T} could be found.
* @see org.springframework.context.ApplicationContext
* @see java.lang.Iterable
*/
default Iterable<T> collectFrom(ApplicationContext applicationContext) {
return Collections.emptySet();
}
/**
* Collects all schema objects of type {@link T} defined in the {@link GemFireCache}.
*
* @param gemfireCache {@link GemFireCache} from which to collect schema objects of type {@link T}.
* @return a {@link Set} of all schema objects of type {@link T} defined in the {@link GemFireCache};
* returns an empty {@link Set} if no schema object of type {@link T} could be found.
* @see org.apache.geode.cache.GemFireCache
* @see java.lang.Iterable
*/
default Iterable<T> collectFrom(GemFireCache gemfireCache) {
return Collections.emptySet();
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright 2017 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.schema;
import java.util.Collections;
import java.util.Optional;
import java.util.Set;
/**
* The {@link SchemaObjectDefiner} interface defines a contract for implementing objects
* that can reverse engineer a schema object instance back into a definition of the schema object.
*
* @author John Blum
* @see org.springframework.data.gemfire.config.schema.SchemaObjectDefinition
* @see org.springframework.data.gemfire.config.schema.SchemaObjectType
* @since 2.0.0
*/
public interface SchemaObjectDefiner {
/**
* Returns a {@link Set} of {@link SchemaObjectType schema object types} definable by this definer.
*
* @return a {@link Set} of {@link SchemaObjectType schema object types} definable by this definer.
* @see org.springframework.data.gemfire.config.schema.SchemaObjectType
* @see java.util.Set
*/
default Set<SchemaObjectType> getSchemaObjectTypes() {
return Collections.emptySet();
}
/**
* Determines whether this definer is able to define the given {@link Object schema object} instance.
*
* @param schemaObject {@link Object} to evaluate.
* @return a boolean value indicating whether this definer is able to define
* the given {@link Object schema object} instance.
* @see java.lang.Object#getClass()
* @see #canDefine(Class)
*/
default boolean canDefine(Object schemaObject) {
return Optional.ofNullable(schemaObject).map(Object::getClass).filter(this::canDefine).isPresent();
}
/**
* Determines whether this definer is able to define schema objects of the given {@link Class type}.
*
* @param schemaObjectType {@link Class type} of the {@link Object schema object} instance to evaluate.
* @return a boolean value indicating whether this definer is able to define {@link Object schema objects}
* of the given {@link Class type}.
* @see org.springframework.data.gemfire.config.schema.SchemaObjectType#from(Class)
* @see #canDefine(SchemaObjectType)
*/
default boolean canDefine(Class<?> schemaObjectType) {
return canDefine(SchemaObjectType.from(schemaObjectType));
}
/**
* Determines whether this definer is able to define schema objects of the given
* {@link SchemaObjectType enumerated schema object type}.
*
* @param schemaObjectType {@link SchemaObjectType} to evaluate.
* @return a boolean value indicating whether this handler is able to handle schema objects
* of the given {@link SchemaObjectType enumerated schema object type}.
* @see org.springframework.data.gemfire.config.schema.SchemaObjectType
*/
default boolean canDefine(SchemaObjectType schemaObjectType) {
return getSchemaObjectTypes().contains(schemaObjectType);
}
/**
* Returns an {@link Optional} {@link SchemaObjectDefinition definition} for the given
* {@link Object schema object} instance.
*
* @param schemaObject {@link Object schema object} to define.
* @return an {@link Optional} {@link SchemaObjectDefinition definition} for the given
* {@link Object schema object} instance.
* @see org.springframework.data.gemfire.config.schema.SchemaObjectDefinition
*/
Optional<? extends SchemaObjectDefinition> define(Object schemaObject);
}

View File

@@ -0,0 +1,115 @@
/*
* Copyright 2017 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.schema;
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalArgumentException;
import java.io.Serializable;
import java.util.Optional;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.query.Index;
import org.springframework.core.Ordered;
import org.springframework.data.gemfire.config.admin.GemfireAdminOperations;
import org.springframework.util.StringUtils;
/**
* {@link SchemaObjectDefinition} is an Abstract Data Type (ADT) encapsulating the definition of a single Apache Geode
* or Pivotal GemFire schema object (e.g. {@link Region} or {@link Index}).
*
* @author John Blum
* @see java.io.Serializable
* @see org.springframework.core.Ordered
* @see org.springframework.data.gemfire.config.schema.SchemaObjectDefiner
* @see org.springframework.data.gemfire.config.schema.SchemaObjectType
* @since 2.0.0
*/
public abstract class SchemaObjectDefinition implements Serializable, Ordered {
private final String name;
/**
* Constructs a new instance of {@link SchemaObjectDefinition} initialized with the specified {@link String name}.
*
* @param name {@link String name} given to the GemFire/Geode schema object; must not be {@literal null}.
* @throws IllegalArgumentException if name is not specified.
*/
public SchemaObjectDefinition(String name) {
this.name = Optional.ofNullable(name).filter(StringUtils::hasText)
.orElseThrow(() -> newIllegalArgumentException("Name [%s] is required", name));
}
/**
* Returns the {@link String name} assigned to the schema object.
*
* @return the {@link String name} assigned to the schema object; name is never {@literal null}.
*/
public String getName() {
return this.name;
}
/**
* Returns the {@link SchemaObjectType type} of schema object defined by this {@link SchemaObjectDefinition}.
*
* @return the {@link SchemaObjectType type} of schema object defined by this {@link SchemaObjectDefinition}.
* @see org.springframework.data.gemfire.config.schema.SchemaObjectType
*/
public abstract SchemaObjectType getType();
/**
* Creates an actual schema object from this {@link SchemaObjectDefinition}.
*
* @param gemfireAdminOperations {@link GemfireAdminOperations} used to create an actual schema object from this
* {@link SchemaObjectDefinition}.
* @see org.springframework.data.gemfire.config.admin.GemfireAdminOperations
*/
public void create(GemfireAdminOperations gemfireAdminOperations) {
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof SchemaObjectDefinition)) {
return false;
}
SchemaObjectDefinition that = (SchemaObjectDefinition) obj;
return this.getName().equals(that.getName())
&& this.getType().equals(that.getType());
}
@Override
public int hashCode() {
int hashValue = 17;
hashValue = 37 * hashValue + this.getName().hashCode();
hashValue = 37 * hashValue + this.getType().hashCode();
return hashValue;
}
@Override
public String toString() {
return String.format("%1$s[%2$s]", getType(), getName());
}
}

View File

@@ -0,0 +1,126 @@
/*
* Copyright 2017 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.schema;
import static java.util.Arrays.stream;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.DiskStore;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.asyncqueue.AsyncEventQueue;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.client.Pool;
import org.apache.geode.cache.execute.Function;
import org.apache.geode.cache.lucene.LuceneIndex;
import org.apache.geode.cache.query.Index;
import org.apache.geode.cache.wan.GatewayReceiver;
import org.apache.geode.cache.wan.GatewaySender;
/**
* {@link SchemaObjectType} defines an enumeration of all the types of Apache Geode or Pivotal GemFire schema objects
* (e.g. {@link Region}) that may possibly be handled by Spring Data Geode / Spring Data GemFire
* and that can be created remotely, from a client application.
*
* @author John Blum
* @see org.apache.geode.cache.asyncqueue.AsyncEventQueue
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.DiskStore
* @see org.apache.geode.cache.Region
* @see org.apache.geode.cache.client.ClientCache
* @see org.apache.geode.cache.client.Pool
* @see org.apache.geode.cache.execute.Function
* @see org.apache.geode.cache.lucene.LuceneIndex
* @see org.apache.geode.cache.query.Index
* @see org.apache.geode.cache.wan.GatewayReceiver
* @see org.apache.geode.cache.wan.GatewaySender
* @see org.springframework.data.gemfire.config.schema.SchemaObjectDefinition
* @since 2.0.0
*/
public enum SchemaObjectType {
ASYNC_EVENT_QUEUE(AsyncEventQueue.class),
CACHE(Cache.class),
CLIENT_CACHE(ClientCache.class),
DISK_STORE(DiskStore.class),
FUNCTION(Function.class),
GATEWAY_RECEIVER(GatewayReceiver.class),
GATEWAY_SENDER(GatewaySender.class),
INDEX(Index.class),
LUCENE_INDEX(LuceneIndex.class),
POOL(Pool.class),
REGION(Region.class),
UNKNOWN(Void.class);
private final Class<?> objectType;
/**
* Constructs an instance of an {@link SchemaObjectType} enumerated value initialized with
* the actual GemFire {@link Class schema object instance type}.
*
* @param objectType actual {@link Class interface type} of the GemFire schema object instance.
* @see java.lang.Class
*/
SchemaObjectType(Class<?> objectType) {
this.objectType = objectType;
}
/**
* Null-safe factory method used to look up and resolve the corresponding {@link SchemaObjectType}
* given an instance of a GemFire schema object.
*
* For example, given an instance of {@link Region}, this factory method will return
* {@link SchemaObjectType#REGION}.
*
* @param obj actual instance of a GemFire schema object, e.g. reference to a {@link Region}.
* @return a corresponding {@link SchemaObjectType} for a given instance of a GemFire schema object.
* If the type cannot be determined, then {@link SchemaObjectType#UNKNOWN} is returned.
* @see #from(Class)
*/
public static SchemaObjectType from(Object obj) {
return stream(SchemaObjectType.values())
.filter(it -> it.getObjectType().isInstance(obj))
.findFirst().orElse(UNKNOWN);
}
/**
* Null-safe factory method used to look up and resolve the corresponding {@link SchemaObjectType}
* given the type of GemFire schema object.
*
* For example, given the {@link Region} {@link Class interface} or any {@link Class sub-type} of {@link Region},
* this factory method will return {@link SchemaObjectType#REGION}.
*
* @param type {@link Class type} of the GemFire schema object, e.g. the {@link Region} {@link Class interface}.
* @return a corresponding {@link SchemaObjectType} for a given {@link Class type }of a GemFire schema object.
* If the type cannot be determined, then {@link SchemaObjectType#UNKNOWN} is returned.
* @see #from(Object)
*/
public static SchemaObjectType from(Class<?> type) {
return stream(SchemaObjectType.values())
.filter(it -> type != null && it.getObjectType().isAssignableFrom(type))
.findFirst().orElse(UNKNOWN);
}
/**
* Returns the {@link Class class type} of the GemFire schema object represented by this enumerated value.
*
* @return the {@link Class class type} of the GemFire schema object represented by this enumerated value.
* @see java.lang.Class
*/
public Class<?> getObjectType() {
return this.objectType;
}
}

View File

@@ -0,0 +1,232 @@
/*
* Copyright 2017 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.schema.definitions;
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalArgumentException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Optional;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.query.Index;
import org.springframework.data.gemfire.IndexType;
import org.springframework.data.gemfire.config.admin.GemfireAdminOperations;
import org.springframework.data.gemfire.config.schema.SchemaObjectDefinition;
import org.springframework.data.gemfire.config.schema.SchemaObjectType;
import org.springframework.data.gemfire.domain.support.AbstractIndexSupport;
import org.springframework.util.StringUtils;
/**
* {@link IndexDefinition} is an Abstract Data Type (ADT) encapsulating the configuration meta-data used to define
* a new Apache Geode / Pivotal GemFire {@link Region} {@link Index}.
*
* @author John Blum
* @see org.apache.geode.cache.Region
* @see org.apache.geode.cache.query.Index
* @see org.springframework.data.gemfire.IndexType
* @see org.springframework.data.gemfire.config.admin.GemfireAdminOperations
* @see org.springframework.data.gemfire.config.schema.SchemaObjectDefinition
* @see org.springframework.data.gemfire.config.schema.SchemaObjectType
* @since 2.0.0
*/
public class IndexDefinition extends SchemaObjectDefinition {
protected static final int ORDER = RegionDefinition.ORDER + 1;
/**
* Factory method used to construct a new instance of {@link IndexDefinition} defined from the given {@link Index}.
*
* @param index {@link Index} on which the new {@link IndexDefinition} will be defined;
* must not be {@literal null}.
* @return a new instance of {@link IndexDefinition} defined from the given {@link Index}.
* @throws IllegalArgumentException if {@link Index} is {@literal null}.
* @see org.apache.geode.cache.query.Index
*/
public static IndexDefinition from(Index index) {
return new IndexDefinition(index);
}
private transient Index index;
private IndexType indexType;
private String expression;
private String fromClause;
private String name;
/**
* Constructs a new instance of {@link IndexDefinition} defined with the given {@link Index}.
*
* @param index {@link Index} on which this definition is defined; must not be {@literal null}.
* @throws IllegalArgumentException if {@link Index} is {@literal null}.
* @see org.apache.geode.cache.query.Index
*/
protected IndexDefinition(Index index) {
super(Optional.ofNullable(index).map(Index::getName)
.orElseThrow(() -> newIllegalArgumentException("Index is required")));
this.index = index;
}
/**
* Returns a reference to the {@link Index} on which this definition is defined.
*
* @return a reference to the {@link Index} on which this definition is defined.
* @see org.apache.geode.cache.query.Index
*/
protected Index getIndex() {
return this.index;
}
@Override
public String getName() {
return Optional.ofNullable(this.name).filter(StringUtils::hasText).orElseGet(super::getName);
}
public String getExpression() {
return Optional.ofNullable(this.expression).filter(StringUtils::hasText)
.orElseGet(this.index::getIndexedExpression);
}
public String getFromClause() {
return Optional.ofNullable(this.fromClause).filter(StringUtils::hasText)
.orElseGet(this.index::getFromClause);
}
public IndexType getIndexType() {
return Optional.ofNullable(this.indexType).orElseGet(() -> IndexType.valueOf(this.index.getType()));
}
/**
* Get the order value of this object.
*
* @return the order value of this object.
* @see org.springframework.core.Ordered
*/
@Override
public int getOrder() {
return ORDER;
}
@Override
public SchemaObjectType getType() {
return SchemaObjectType.INDEX;
}
@Override
public void create(GemfireAdminOperations gemfireAdminOperations) {
gemfireAdminOperations.createIndex(this);
}
@SuppressWarnings("deprecation")
public IndexDefinition as(org.apache.geode.cache.query.IndexType gemfireGeodeIndexType) {
return as(IndexType.valueOf(gemfireGeodeIndexType));
}
public IndexDefinition as(IndexType indexType) {
this.indexType = indexType;
return this;
}
public IndexDefinition having(String expression) {
this.expression = Optional.ofNullable(expression).filter(StringUtils::hasText)
.orElseThrow(() -> newIllegalArgumentException("Expression is required"));
return this;
}
public IndexDefinition on(Region<?, ?> region) {
return on(Optional.ofNullable(region).map(Region::getFullPath)
.orElseThrow(() -> newIllegalArgumentException("Region is required")));
}
public IndexDefinition on(String fromClause) {
this.fromClause = Optional.ofNullable(fromClause).filter(StringUtils::hasText)
.orElseThrow(() -> newIllegalArgumentException("From Clause is required"));
return this;
}
public IndexDefinition with(String name) {
this.name = name;
return this;
}
private void writeObject(ObjectOutputStream outputStream) throws IOException {
outputStream.writeUTF(getName());
outputStream.writeUTF(getExpression());
outputStream.writeUTF(getFromClause());
outputStream.writeObject(getIndexType());
}
private void readObject(ObjectInputStream inputStream) throws ClassNotFoundException, IOException {
String name = inputStream.readUTF();
String expression = inputStream.readUTF();
String fromClause = inputStream.readUTF();
IndexType indexType = (IndexType) inputStream.readObject();
this.index = IndexWrapper.from(name, expression, fromClause, indexType);
}
protected static final class IndexWrapper extends AbstractIndexSupport {
private IndexType indexType;
private final String name;
private final String expression;
private final String fromClause;
protected static Index from(String name, String expression, String fromClause, IndexType indexType) {
return new IndexWrapper(name, expression, fromClause, indexType);
}
protected IndexWrapper(String name, String expression, String fromClause, IndexType indexType) {
this.name = name;
this.expression = expression;
this.fromClause = fromClause;
this.indexType = indexType;
}
@Override
public String getFromClause() {
return this.fromClause;
}
@Override
public String getIndexedExpression() {
return this.expression;
}
@Override
public String getName() {
return this.name;
}
@Override
@SuppressWarnings("deprecation")
public org.apache.geode.cache.query.IndexType getType() {
return Optional.ofNullable(this.indexType).map(IndexType::getGemfireIndexType).orElse(null);
}
}
}

View File

@@ -0,0 +1,146 @@
/*
* Copyright 2017 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.schema.definitions;
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalArgumentException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Optional;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionShortcut;
import org.springframework.data.gemfire.config.admin.GemfireAdminOperations;
import org.springframework.data.gemfire.config.schema.SchemaObjectDefinition;
import org.springframework.data.gemfire.config.schema.SchemaObjectType;
import org.springframework.util.StringUtils;
/**
* {@link RegionDefinition} is an Abstract Data Type (ADT) encapsulating the configuration meta-data used to
* define a new Apache Geode / Pivotal GemFire cache {@link Region}.
*
* @author John Blum
* @see org.apache.geode.cache.Region
* @see org.apache.geode.cache.RegionShortcut
* @see org.springframework.data.gemfire.config.admin.GemfireAdminOperations
* @see org.springframework.data.gemfire.config.schema.SchemaObjectDefinition
* @see org.springframework.data.gemfire.config.schema.SchemaObjectType
* @since 2.0.0
*/
public class RegionDefinition extends SchemaObjectDefinition {
protected static final int ORDER = 1;
public static final RegionShortcut DEFAULT_REGION_SHORTCUT = RegionShortcut.PARTITION;
/**
* Factory method used to construct a new instance of {@link RegionDefinition} defined from
* the given {@link Region}.
*
* @param region {@link Region} from which the new definition will be defined.
* @return a new instance of {@link RegionDefinition} defined from the given {@link Region}.
* @throws IllegalArgumentException if {@link Region} is {@literal null}.
* @see org.apache.geode.cache.Region
* @see #RegionDefinition(Region)
*/
public static RegionDefinition from(Region<?, ?> region) {
return Optional.ofNullable(region).map(RegionDefinition::new)
.orElseThrow(() -> newIllegalArgumentException("Region is required"));
}
private final transient Region<?, ?> region;
private RegionShortcut regionShortcut;
private String name;
/**
* Constructs a new instance of {@link RegionDefinition} defined with the given {@link Region}.
*
* @param region {@link Region} on which this definition is defined; must not be {@literal null}.
* @throws IllegalArgumentException if {@link Region} is {@literal null}.
* @see org.apache.geode.cache.Region
*/
protected RegionDefinition(Region<?, ?> region) {
super(Optional.ofNullable(region).map(Region::getName)
.orElseThrow(() -> newIllegalArgumentException("Region is required")));
this.region = region;
}
/**
* Get the order value of this object.
*
* @return the order value of this object.
* @see org.springframework.core.Ordered
*/
@Override
public int getOrder() {
return ORDER;
}
/**
* Returns a reference to the {@link Region} from which this definition is defined.
*
* @return a reference to the {@link Region} from which this definition is defined.
* @see org.apache.geode.cache.Region
*/
protected Region<?, ?> getRegion() {
return this.region;
}
@Override
public String getName() {
return Optional.ofNullable(this.name).filter(StringUtils::hasText).orElseGet(super::getName);
}
public RegionShortcut getRegionShortcut() {
return Optional.ofNullable(this.regionShortcut).orElse(DEFAULT_REGION_SHORTCUT);
}
@Override
public SchemaObjectType getType() {
return SchemaObjectType.REGION;
}
@Override
public void create(GemfireAdminOperations gemfireAdminOperations) {
gemfireAdminOperations.createRegion(this);
}
public RegionDefinition having(RegionShortcut regionShortcut) {
this.regionShortcut = regionShortcut;
return this;
}
public RegionDefinition with(String name) {
this.name = name;
return this;
}
private void writeObject(ObjectOutputStream outputStream) throws IOException {
outputStream.writeUTF(getName());
outputStream.writeObject(getRegionShortcut());
}
private void readObject(ObjectInputStream inputStream) throws ClassNotFoundException, IOException {
this.name = inputStream.readUTF();
this.regionShortcut = (RegionShortcut) inputStream.readObject();
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2017 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.schema.support;
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeSet;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.Region;
import org.springframework.context.ApplicationContext;
import org.springframework.data.gemfire.util.RegionUtils;
/**
* The {@link ClientRegionCollector} class is an extension of the {@link RegionCollector} which applies additional
* filtering to find only client {@link Region Regions} in a given context.
*
* @author John Blum
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.Region
* @see org.springframework.context.ApplicationContext
* @see org.springframework.data.gemfire.config.schema.SchemaObjectCollector
* @since 2.0.0
*/
public class ClientRegionCollector extends RegionCollector {
@Override
public Set<Region> collectFrom(ApplicationContext applicationContext) {
return onlyClientRegions(super.collectFrom(applicationContext));
}
@Override
public Set<Region> collectFrom(GemFireCache gemfireCache) {
return onlyClientRegions(super.collectFrom(gemfireCache));
}
private Set<Region> onlyClientRegions(Set<Region> regions) {
return nullSafeSet(regions).stream().filter(RegionUtils::isClient).collect(Collectors.toSet());
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright 2017 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.schema.support;
import static java.util.stream.StreamSupport.stream;
import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeIterable;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.geode.cache.GemFireCache;
import org.springframework.context.ApplicationContext;
import org.springframework.data.gemfire.config.schema.SchemaObjectCollector;
import org.springframework.lang.Nullable;
/**
* The {@link ComposableSchemaObjectCollector} class is a {@link SchemaObjectCollector} implementation composed of
* multiple {@link SchemaObjectCollector} objects wrapped in a facade and treated like a single
* {@link SchemaObjectCollector} using the Composite Software Design Pattern.
*
* @author John Blum
* @see org.springframework.data.gemfire.config.schema.SchemaObjectCollector
* @since 2.0.0
*/
public final class ComposableSchemaObjectCollector
implements SchemaObjectCollector<Object>, Iterable<SchemaObjectCollector<?>> {
private final List<SchemaObjectCollector<?>> schemaObjectCollectors;
@Nullable
@SuppressWarnings("unchecked")
public static SchemaObjectCollector<?> compose(SchemaObjectCollector<?>... schemaObjectCollectors) {
return compose(Arrays.asList(nullSafeArray(schemaObjectCollectors, SchemaObjectCollector.class)));
}
@Nullable
public static SchemaObjectCollector<?> compose(Iterable<SchemaObjectCollector<?>> schemaObjectCollectors) {
List<SchemaObjectCollector<?>> schemaObjectCollectorList =
stream(nullSafeIterable(schemaObjectCollectors).spliterator(), false)
.filter(Objects::nonNull).collect(Collectors.toList());
return (schemaObjectCollectorList.isEmpty() ? null
: (schemaObjectCollectorList.size() == 1 ? schemaObjectCollectorList.iterator().next()
: new ComposableSchemaObjectCollector(schemaObjectCollectorList)));
}
private ComposableSchemaObjectCollector(List<SchemaObjectCollector<?>> schemaObjectCollectors) {
this.schemaObjectCollectors = Collections.unmodifiableList(schemaObjectCollectors);
}
@Override
public Iterable<Object> collectFrom(ApplicationContext applicationContext) {
return collectFrom(collector -> collector.collectFrom(applicationContext));
}
@Override
public Iterable<Object> collectFrom(GemFireCache gemfireCache) {
return collectFrom(collector -> collector.collectFrom(gemfireCache));
}
private Iterable<Object> collectFrom(Function<SchemaObjectCollector<?>, Iterable<?>> schemaObjectSource) {
return this.schemaObjectCollectors.stream()
.flatMap(collector -> stream(schemaObjectSource.apply(collector).spliterator(), false))
.collect(Collectors.toList());
}
@Override
public Iterator<SchemaObjectCollector<?>> iterator() {
return Collections.unmodifiableList(this.schemaObjectCollectors).iterator();
}
}

View File

@@ -0,0 +1,99 @@
/*
* Copyright 2017 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.schema.support;
import static java.util.stream.StreamSupport.stream;
import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeIterable;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.data.gemfire.config.schema.SchemaObjectDefiner;
import org.springframework.data.gemfire.config.schema.SchemaObjectDefinition;
import org.springframework.data.gemfire.config.schema.SchemaObjectType;
import org.springframework.lang.Nullable;
/**
* {@link ComposableSchemaObjectDefiner} is an implementation of {@link SchemaObjectDefiner}
* as well as a composition of {@link SchemaObjectDefiner SchemaObjectInstanceHandlers} composed
* using the Composite Software Design Pattern.
*
* @author John Blum
* @see java.lang.Iterable
* @see org.springframework.data.gemfire.config.schema.SchemaObjectDefiner
* @since 2.0.0
*/
public final class ComposableSchemaObjectDefiner
implements SchemaObjectDefiner, Iterable<SchemaObjectDefiner> {
@Nullable
public static SchemaObjectDefiner compose(SchemaObjectDefiner... schemaObjectDefiners) {
return compose(Arrays.asList(nullSafeArray(schemaObjectDefiners, SchemaObjectDefiner.class)));
}
@Nullable
public static SchemaObjectDefiner compose(Iterable<SchemaObjectDefiner> schemaObjectDefiners) {
Set<SchemaObjectDefiner> schemaObjectDefinerSet =
stream(nullSafeIterable(schemaObjectDefiners).spliterator(), false)
.filter(Objects::nonNull).collect(Collectors.toSet());
return (schemaObjectDefinerSet.isEmpty() ? null
: (schemaObjectDefinerSet.size() == 1 ? schemaObjectDefinerSet.iterator().next()
: new ComposableSchemaObjectDefiner(schemaObjectDefinerSet)));
}
private final Set<SchemaObjectDefiner> schemaObjectDefiners;
/**
* Constructs a new instance of {@link ComposableSchemaObjectDefiner} initialized and compose of
* the given {@link Set} of {@link SchemaObjectDefiner SchemaObjectInstanceHandlers}.
*
* @param schemaObjectDefiners {@link Set} of {@link SchemaObjectDefiner SchemaObjectInstanceHandlers}
* from which this {@link ComposableSchemaObjectDefiner} is composed.
* @see SchemaObjectDefiner
*/
private ComposableSchemaObjectDefiner(Set<SchemaObjectDefiner> schemaObjectDefiners) {
this.schemaObjectDefiners = Collections.unmodifiableSet(schemaObjectDefiners);
}
@Override
public Set<SchemaObjectType> getSchemaObjectTypes() {
return this.schemaObjectDefiners.stream()
.flatMap(it -> it.getSchemaObjectTypes().stream())
.collect(Collectors.toSet());
}
@Override
public Optional<? extends SchemaObjectDefinition> define(Object schemaObject) {
return this.schemaObjectDefiners.stream()
.filter(it -> it.canDefine(schemaObject)).findAny()
.map(it -> it.define(schemaObject).orElse(null));
}
@Override
public Iterator<SchemaObjectDefiner> iterator() {
return Collections.unmodifiableSet(this.schemaObjectDefiners).iterator();
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2017 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.schema.support;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.query.Index;
import org.springframework.context.ApplicationContext;
import org.springframework.data.gemfire.config.schema.SchemaObjectCollector;
/**
* The {@link IndexCollector} class is an implementation of the {@link SchemaObjectCollector} that is capable of
* inspecting a context and finding all {@link Index} schema object instances that have been declared in that context.
*
* @author John Blum
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.query.Index
* @see org.springframework.context.ApplicationContext
* @see org.springframework.data.gemfire.config.schema.SchemaObjectCollector
* @since 2.0.0
*/
public class IndexCollector implements SchemaObjectCollector<Index> {
@Override
public Set<Index> collectFrom(ApplicationContext applicationContext) {
return applicationContext.getBeansOfType(Index.class).values().stream().collect(Collectors.toSet());
}
@Override
public Set<Index> collectFrom(GemFireCache gemfireCache) {
return gemfireCache.getQueryService().getIndexes().stream().collect(Collectors.toSet());
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2017 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.schema.support;
import static org.springframework.data.gemfire.util.CollectionUtils.asSet;
import java.util.Optional;
import java.util.Set;
import org.apache.geode.cache.query.Index;
import org.springframework.data.gemfire.config.schema.SchemaObjectDefiner;
import org.springframework.data.gemfire.config.schema.SchemaObjectType;
import org.springframework.data.gemfire.config.schema.definitions.IndexDefinition;
/**
* The {@link {{@link IndexDefiner }} class is responsible for defining an {@link Index} given a reference to
* an {@link Index} instance.
*
* @author John Blum
* @see org.apache.geode.cache.query.Index
* @see org.springframework.data.gemfire.config.schema.SchemaObjectDefiner
* @see org.springframework.data.gemfire.config.schema.SchemaObjectType
* @see org.springframework.data.gemfire.config.schema.definitions.IndexDefinition
* @since 2.0.0
*/
public class IndexDefiner implements SchemaObjectDefiner {
@Override
public Set<SchemaObjectType> getSchemaObjectTypes() {
return asSet(SchemaObjectType.INDEX);
}
@Override
public Optional<IndexDefinition> define(Object schemaObject) {
return Optional.ofNullable(schemaObject).filter(this::canDefine).map(it -> IndexDefinition.from((Index) it));
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2017 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.schema.support;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.Region;
import org.springframework.context.ApplicationContext;
import org.springframework.data.gemfire.config.schema.SchemaObjectCollector;
/**
* The {@link RegionCollector} class is an implementation of the {@link SchemaObjectCollector} that is capable of
* inspecting a context and finding all {@link Region} schema object instances that have been declared in that context.
*
* @author John Blum
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.Region
* @see org.springframework.context.ApplicationContext
* @see org.springframework.data.gemfire.config.schema.SchemaObjectCollector
* @since 2.0.0
*/
public class RegionCollector implements SchemaObjectCollector<Region> {
@Override
public Set<Region> collectFrom(ApplicationContext applicationContext) {
return applicationContext.getBeansOfType(Region.class).values().stream().collect(Collectors.toSet());
}
@Override
public Set<Region> collectFrom(GemFireCache gemfireCache) {
return gemfireCache.rootRegions().stream().collect(Collectors.toSet());
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2017 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.schema.support;
import static org.springframework.data.gemfire.util.CollectionUtils.asSet;
import java.util.Optional;
import java.util.Set;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionShortcut;
import org.springframework.data.gemfire.config.schema.SchemaObjectDefiner;
import org.springframework.data.gemfire.config.schema.SchemaObjectType;
import org.springframework.data.gemfire.config.schema.definitions.RegionDefinition;
import org.springframework.data.gemfire.util.RegionUtils;
/**
* The {@link {RegionDefiner} class is responsible for defining a {@link Region}
* given a reference to a {@link Region} instance.
*
* @author John Blum
* @see org.apache.geode.cache.Region
* @see org.apache.geode.cache.RegionAttributes
* @see org.apache.geode.cache.RegionShortcut
* @see org.springframework.data.gemfire.config.schema.SchemaObjectDefiner
* @see org.springframework.data.gemfire.config.schema.SchemaObjectType
* @see org.springframework.data.gemfire.config.schema.definitions.RegionDefinition
* @since 2.0.0
*/
public class RegionDefiner implements SchemaObjectDefiner {
private final RegionShortcut regionShortcut;
public RegionDefiner() {
this(RegionDefinition.DEFAULT_REGION_SHORTCUT);
}
public RegionDefiner(RegionShortcut regionShortcut) {
this.regionShortcut = regionShortcut;
}
protected RegionShortcut getRegionShortcut() {
return Optional.ofNullable(this.regionShortcut).orElse(RegionDefinition.DEFAULT_REGION_SHORTCUT);
}
@Override
public Set<SchemaObjectType> getSchemaObjectTypes() {
return asSet(SchemaObjectType.REGION);
}
@Override
public Optional<RegionDefinition> define(Object schemaObject) {
return Optional.ofNullable(schemaObject)
.filter(this::canDefine)
.map(it -> (Region) it)
.filter(RegionUtils::isClient)
.map(it -> RegionDefinition.from(it).having(getRegionShortcut()));
}
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright 2017 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.domain.support;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.query.Index;
import org.apache.geode.cache.query.IndexStatistics;
import org.apache.geode.cache.query.IndexType;
/**
* {@link AbstractIndexSupport} is an abstract base class supporting the implementation
* of the Pivotal GemFire / Apache Geode {@link Index} interface.
*
* @author John Blum
* @see org.apache.geode.cache.query.Index
* @since 2.0.0
*/
public abstract class AbstractIndexSupport implements Index {
private static final String NOT_IMPLEMENTED = "Not Implemented";
@Override
public String getCanonicalizedFromClause() {
return getFromClause();
}
@Override
public String getCanonicalizedIndexedExpression() {
return getIndexedExpression();
}
@Override
public String getCanonicalizedProjectionAttributes() {
return getProjectionAttributes();
}
@Override
public String getFromClause() {
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
}
@Override
public String getIndexedExpression() {
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
}
@Override
public String getName() {
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
}
@Override
public String getProjectionAttributes() {
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
}
@Override
public Region<?, ?> getRegion() {
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
}
@Override
public IndexStatistics getStatistics() {
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
}
@Override
@SuppressWarnings("deprecation")
public IndexType getType() {
throw new UnsupportedOperationException(NOT_IMPLEMENTED);
}
}

View File

@@ -13,80 +13,269 @@
package org.springframework.data.gemfire.function;
import static java.util.Arrays.stream;
import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalArgumentException;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.geode.cache.execute.Function;
import org.apache.geode.cache.execute.FunctionService;
import org.springframework.beans.BeanUtils;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.gemfire.function.annotation.GemfireFunction;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
* Utility class for registering a POJO as a GemFire/Geode {@link Function}.
*
* @author David Turanski
* @author John Blum
* @see org.apache.geode.cache.execute.Function
* @see org.apache.geode.cache.execute.FunctionService
* @see org.springframework.data.gemfire.function.annotation.GemfireFunction
* @since 1.2.0
*/
public abstract class GemfireFunctionUtils {
private static Log log = LogFactory.getLog(GemfireFunctionUtils.class);
/**
* Wrap a target object and method in a GemFire Function and register the function to the {@link FunctionService}
* Determines whether the given {@link Method} is a POJO, {@link GemfireFunction} annotated {@link Method}.
*
* @param target the target object
* @param method the method bound to the function
* @param attributes function attributes
* @param overwrite if true, will replace the existing function
* @param method {@link Method} to evaluate.
* @return a boolean value indicating whether the {@link Method} on a POJO represents a SDG {@link GemfireFunction}.
* @see org.springframework.data.gemfire.function.annotation.GemfireFunction
* @see java.lang.reflect.Method
*/
public static void registerFunctionForPojoMethod(Object target, Method method, Map<String, Object> attributes,
boolean overwrite) {
public static boolean isGemfireFunction(Method method) {
return (method != null && method.isAnnotationPresent(GemfireFunction.class));
}
String id = attributes.containsKey("id") ? (String) attributes.get("id") : "";
private static boolean isMatchingGemfireFunction(Method method, String functionId) {
return getGemfireFunctionId(method).filter(methodFunctionId ->
ObjectUtils.nullSafeEquals(methodFunctionId, functionId)).isPresent();
}
private static AnnotationAttributes getAnnotationAttributes(AnnotatedElement element,
Class<? extends Annotation> annotationType) {
return AnnotationAttributes.fromMap(
AnnotationUtils.getAnnotationAttributes(element, element.getAnnotation(annotationType)));
}
/**
* Null-safe operation used to determine the GemFire/Geode {@link Function#getId()} Function ID}
* of a given {@link GemfireFunction} annotated POJO {@link Method}.
*
* If the {@link Method} is not {@literal null} and annotated with {@link GemfireFunction} then this method
* tries to determine the {@link Function#getId()} from the {@link GemfireFunction#id()} attribute.
*
* If the {@link GemfireFunction#id()} attribute was not set, then the {@link Method#getName()}
* is returned as the {@link Function#getId() Function ID}.
*
* @param method {@link GemfireFunction} annotated POJO {@link Method} containing the implementation
* for a GemFire/Geode {@link Function}.
* @return the GemFire/Geode Function ID of the given {@link Method}, or an empty {@link Optional}
* if the {@link Method} is not a GemFire/Geode {@link Function}.
* @see org.springframework.data.gemfire.function.annotation.GemfireFunction
* @see java.lang.reflect.Method
* @see #isGemfireFunction(Method)
*/
private static Optional<String> getGemfireFunctionId(Method method) {
return Optional.ofNullable(method)
.filter(GemfireFunctionUtils::isGemfireFunction)
.map(it ->
Optional.of(it.getAnnotation(GemfireFunction.class))
.map(annotation -> getAnnotationAttributes(it, annotation.getClass()))
.filter(annotationAttributes -> annotationAttributes.containsKey("id"))
.map(annotationAttributes -> annotationAttributes.getString("id"))
.filter(StringUtils::hasText)
.orElseGet(() -> it.getName())
);
}
private static Object constructInstance(Class<?> type, Object... constructorArguments) {
return org.springframework.data.util.ReflectionUtils.findConstructor(type, constructorArguments)
.map(constructor -> BeanUtils.instantiateClass(constructor, constructorArguments))
.orElseThrow(() -> newIllegalArgumentException(
"No suitable constructor was found for type [%s] having parameters [%s]", type.getName(),
stream(nullSafeArray(constructorArguments, Object.class)).map(ObjectUtils::nullSafeClassName)
.collect(Collectors.toList())));
}
private static String nullSafeName(Method method) {
return Optional.ofNullable(method)
.map(it -> String.format("%s.%s", method.getDeclaringClass().getName(), method.getName()))
.orElse(null);
}
/**
* Bind a {@link Method method} with the given {@link Function#getId() Function ID} on an object
* of the given {@link Class type} as a {@link Function} and register it with the {@link FunctionService}.
*
* @param type {@link Class target type} to evaluate; must not be {@literal null}.
* @param functionId {@link String} containing the {@link Function#getId()} identifying the {@link Method}
* on the {@link Class target type} to bind as a {@link Function}.
* @throws IllegalArgumentException if {@link Class type} is {@literal null}.
* @see #registerFunctionForPojoMethod(Object, Method, AnnotationAttributes, boolean)
* @see #isMatchingGemfireFunction(Method, String)
*/
@SuppressWarnings("unused")
public static void registerFunctionForPojoMethod(Class<?> type, String functionId) {
Assert.notNull(type, () -> String.format("Class type of POJO containing %s(s) is required",
GemfireFunction.class.getName()));
ReflectionUtils.doWithMethods(type,
method -> registerFunctionForPojoMethod(constructInstance(type), method,
getAnnotationAttributes(method, GemfireFunction.class), true),
method -> isMatchingGemfireFunction(method, functionId));
}
/**
* Bind a {@link Method method} with the given {@link Function#getId() Function ID} on the given {@link Object target}
* as a {@link Function} and register it with the {@link FunctionService}.
*
* @param target {@link Object target object} to evaluate; must not be {@literal null}.
* @param functionId {@link String} containing the {@link Function#getId()} identifying the {@link Method}
* on the {@link Object target object} to bind as a {@link Function}.
* @throws IllegalArgumentException if {@link Object target} is {@literal null}.
* @see #registerFunctionForPojoMethod(Object, Method, AnnotationAttributes, boolean)
* @see #isMatchingGemfireFunction(Method, String)
*/
public static void registerFunctionForPojoMethod(Object target, String functionId) {
Assert.notNull(target, "Target object is required");
ReflectionUtils.doWithMethods(target.getClass(),
method -> registerFunctionForPojoMethod(target, method,
getAnnotationAttributes(method, GemfireFunction.class), true),
method -> isMatchingGemfireFunction(method, functionId));
}
/**
* Binds the given {@link Method method} on the given {@link Object target} as a {@link Function}
* and registers it with the {@link FunctionService}.
*
* @param target {@link Object target object} to evaluate; must not be {@literal null}.
* @param method {@link Method} on {@link Object target} bound as a {@link Function}.
* @param overwrite if {@literal true}, will replace any existing {@link Function}
* having the same {@link Function#getId() ID}.
* @throws IllegalArgumentException if {@link Object target} is {@literal null} or the given {@link Method}
* is not a {@link GemfireFunction}.
* @see #registerFunctionForPojoMethod(Object, Method, AnnotationAttributes, boolean)
* @see #isGemfireFunction(Method)
*/
public static void registerFunctionForPojoMethod(Object target, Method method, boolean overwrite) {
Assert.notNull(target, "Target object is required");
Assert.isTrue(isGemfireFunction(method), () -> String.format("Method [%s] must be a %s",
nullSafeName(method), GemfireFunction.class.getName()));
registerFunctionForPojoMethod(target, method, getAnnotationAttributes(method, GemfireFunction.class), overwrite);
}
/**
* Wrap the {@link Object target object} and {@link Method method} in a GemFire/Geode {@link Function}
* and register the {@link Function} with the {@link FunctionService}.
*
* @param target {@link Object target object}.
* @param method {@link Method} bound to a {@link Function}.
* @param gemfireFunctionAttributes {@link GemfireFunction} annotation {@link Map attributes}.
* @param overwrite if {@literal true}, will replace any existing {@link Function} having the same ID.
* @deprecated use {@link #registerFunctionForPojoMethod(Object, Method, AnnotationAttributes, boolean)} instead.
*/
@Deprecated
public static void registerFunctionForPojoMethod(Object target, Method method,
Map<String, Object> gemfireFunctionAttributes, boolean overwrite) {
registerFunctionForPojoMethod(target, method, AnnotationAttributes.fromMap(gemfireFunctionAttributes), overwrite);
}
/**
* Wrap the {@link Object target object} and {@link Method method} in a GemFire/Geode {@link Function}
* and register the {@link Function} with the {@link FunctionService}.
*
* @param target {@link Object target object}.
* @param method {@link Method} bound to a {@link Function}.
* @param gemfireFunctionAttributes {@link GemfireFunction} {@link AnnotationAttributes annotation attributes}.
* @param overwrite if {@literal true}, will replace any existing {@link Function} having the same ID.
*/
public static void registerFunctionForPojoMethod(Object target, Method method,
AnnotationAttributes gemfireFunctionAttributes, boolean overwrite) {
String id = gemfireFunctionAttributes.containsKey("id")
? gemfireFunctionAttributes.getString("id") : "";
PojoFunctionWrapper function = new PojoFunctionWrapper(target, method, id);
if (attributes.containsKey("HA")) {
function.setHA((Boolean) attributes.get("HA"));
}
if (gemfireFunctionAttributes.containsKey("batchSize")) {
if (attributes.containsKey("optimizeForWrite")) {
function.setOptimizeForWrite((Boolean) attributes.get("optimizeForWrite"));
}
int batchSize = gemfireFunctionAttributes.getNumber("batchSize");
Assert.isTrue(batchSize >= 0,
String.format("batchSize [%1$d] specified on [%2$s.%3$s] must be a non-negative value",
batchSize, target.getClass().getName(), method.getName()));
if (attributes.containsKey("batchSize")) {
int batchSize = (Integer) attributes.get("batchSize");
Assert.isTrue(batchSize >= 0, String.format("batchSize must be a non-negative value %1$s.%2$s",
target.getClass().getName(), method.getName()));
function.setBatchSize(batchSize);
}
if (attributes.containsKey("hasResult")) {
// only set if true TODO figure out why???
if (Boolean.TRUE.equals(attributes.get("hasResult"))) {
if (gemfireFunctionAttributes.containsKey("HA")) {
function.setHA(gemfireFunctionAttributes.getBoolean("HA"));
}
if (gemfireFunctionAttributes.containsKey("hasResult")) {
if (Boolean.TRUE.equals(gemfireFunctionAttributes.getBoolean("hasResult"))) {
function.setHasResult(true);
}
}
if (gemfireFunctionAttributes.containsKey("optimizeForWrite")) {
function.setOptimizeForWrite(gemfireFunctionAttributes.getBoolean("optimizeForWrite"));
}
if (FunctionService.isRegistered(function.getId())) {
if (overwrite) {
if (log.isDebugEnabled()) {
log.debug("unregistering function definition " + function.getId());
log.debug(String.format("Unregistering Function [%s]", function.getId()));
}
FunctionService.unregisterFunction(function.getId());
}
}
if (!FunctionService.isRegistered(function.getId())) {
FunctionService.registerFunction(function);
if (log.isDebugEnabled()) {
log.debug("registered function " + function.getId());
log.debug(String.format("Registered Function [%s]", function.getId()));
}
}
else {
if (log.isDebugEnabled()) {
log.debug("function " + function.getId() + "is already registered");
log.debug(String.format("Function [%s] is already registered", function.getId()));
}
}
}
@@ -107,16 +296,19 @@ public abstract class GemfireFunctionUtils {
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
if (parameterAnnotations.length > 0) {
Class<?>[] parameterTypes = method.getParameterTypes();
List<Class<?>> requiredTypesList = Arrays.asList(requiredTypes);
for (int index = 0; index < parameterAnnotations.length; index++) {
Annotation[] annotations = parameterAnnotations[index];
if (annotations.length > 0) {
for (Annotation annotation : annotations) {
if (annotation.annotationType().equals(targetAnnotationType)) {
Assert.state(position < 0, String.format(
"Method %s signature cannot contain more than one parameter annotated with type %s",
method.getName(), targetAnnotationType.getName()));
@@ -144,5 +336,4 @@ public abstract class GemfireFunctionUtils {
return position;
}
}

View File

@@ -16,6 +16,7 @@ import java.lang.reflect.Method;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.execute.Function;
import org.apache.geode.cache.execute.FunctionContext;
import org.apache.geode.cache.execute.ResultSender;
@@ -24,16 +25,22 @@ import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
* Invokes a POJO's given method as a Gemfire remote function.
* If the POJO has a constructor that takes a Map, and the function context is Region, the
* region will be injected. The delegate class name, the method name, and the method arguments
* are part of a remote function invocation, therefore all arguments must be serializable
* or an alternate serialization method must be used.
* The delegate class must be the class path of the remote cache(s)
* @author David Turanski
* Invokes a given {@link Object POJO} {@link Method} as a (remote) GemFire/Geode {@link Function}.
*
* If the {@link Object POJO} has a constructor that takes a {@link java.util.Map}, and the {@link Function} context
* is a {@link Region}, the {@link Region} will be injected.
*
* The delegate {@link Class#getName() class name}, the {@link Method#getName() method name},
* and {@link Method} arguments are part of the {@link Function} invocation, therefore all arguments
* must be {@link java.io.Serializable} or an alternate serialization strategy must be used.
*
* The delegate {@link Class} must be on the class path of the remote cache(s).
*
* @author David Turanski
* @author John Blum
* @see org.apache.geode.cache.execute.Function
* @since 1.2.0
*/
@SuppressWarnings("serial")
public class PojoFunctionWrapper implements Function {
@@ -59,7 +66,7 @@ public class PojoFunctionWrapper implements Function {
this.method = method;
this.id = (StringUtils.hasText(id) ? id : method.getName());
this.HA = false;
this.hasResult = !(method.getReturnType().equals(void.class));
this.hasResult = !method.getReturnType().equals(void.class);
this.optimizeForWrite = false;
}
@@ -100,7 +107,9 @@ public class PojoFunctionWrapper implements Function {
}
@Override
@SuppressWarnings("unchecked")
public void execute(final FunctionContext functionContext) {
Object[] args = this.functionArgumentResolver.resolveFunctionArguments(functionContext);
Object result = invokeTargetMethod(args);
@@ -111,19 +120,22 @@ public class PojoFunctionWrapper implements Function {
}
protected final Object invokeTargetMethod(Object[] args) {
if (logger.isDebugEnabled()) {
logger.debug(String.format("about to invoke method %s on class %s as function %s", method.getName(),
target.getClass().getName(), this.id));
logger.debug(String.format("About to invoke method [%s] on class [%s] as Function [%s]",
this.method.getName(), this.target.getClass().getName(), getId()));
for (Object arg : args) {
logger.debug("arg:" + arg.getClass().getName() + " " + arg.toString());
logger.debug(String.format("Argument of type [%s] is [%s]", arg.getClass().getName(), arg.toString()));
}
}
return ReflectionUtils.invokeMethod(method, target, (Object[]) args);
return ReflectionUtils.invokeMethod(this.method, this.target, (Object[]) args);
}
private void sendResults(ResultSender<Object> resultSender, Object result) {
if (result == null) {
resultSender.lastResult(null);
}
@@ -139,5 +151,4 @@ public class PojoFunctionWrapper implements Function {
}
}
}
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2002-2013 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.
@@ -22,17 +22,20 @@ import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;
/**
*
* Enables Gemfire annotated function implementations. Causes the container to discover any beans that are annotated
* with {code} @GemfireFunction {code}, wrap them in a
* {@link org.springframework.data.gemfire.function.PojoFunctionWrapper}, and register them with the cache.
*
* Enables GemFire annotated Function implementations.
*
* Causes the container to discover any beans that are annotated with {code}@GemfireFunction{code},
* wrap them in a {@link org.springframework.data.gemfire.function.PojoFunctionWrapper},
* and register them with the cache.
*
* @author David Turanski
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(GemfireFunctionPostBeanProcessorRegistrar.class)
@Documented
@Import(GemfireFunctionBeanPostProcessorRegistrar.class)
@SuppressWarnings("unused")
public @interface EnableGemfireFunctions {
}

View File

@@ -1,23 +1,26 @@
/*
* Copyright 2002-2013 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.function.config;
import static java.util.Arrays.stream;
import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.gemfire.function.GemfireFunctionUtils;
import org.springframework.data.gemfire.function.annotation.GemfireFunction;
@@ -25,48 +28,44 @@ import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
/**
* A {@link BeanPostProcessor} to discover components wired as function implementations. That is
* beans that contain methods annotated with {code} @GemfireFunction {code}
*
* @author David Turanski
* Spring {@link BeanPostProcessor} that discovers bean components wired as Function implementations,
* i.e. beans containing methods annotated with {@link GemfireFunction}.
*
* @author David Turanski
* @author John Blum
* @see org.springframework.beans.factory.config.BeanPostProcessor
* @see org.springframework.data.gemfire.function.annotation.GemfireFunction
*/
public class GemfireFunctionBeanPostProcessor implements BeanPostProcessor {
private static final String GEMFIRE_FUNCTION_ANNOTATION_NAME = GemfireFunction.class.getName();
/* (non-Javadoc)
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization(java.lang.Object, java.lang.String)
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization(java.lang.Object, java.lang.String)
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
registerAnyDeclaredGemfireFunctionMethods(bean);
registerAnyDeclaredGemfireFunctionAnnotatedMethods(bean);
return bean;
}
private void registerAnyDeclaredGemfireFunctionMethods (Object bean) {
Method[] methods = ReflectionUtils.getAllDeclaredMethods(bean.getClass());
for (Method method: methods) {
GemfireFunction annotation = AnnotationUtils.getAnnotation(method, GemfireFunction.class);
if (annotation != null) {
Assert.isTrue(Modifier.isPublic(method.getModifiers()),"The method " + method.getName()+ " annotated with" + GEMFIRE_FUNCTION_ANNOTATION_NAME+ " must be public");
Map<String,Object> attributes = AnnotationUtils.getAnnotationAttributes(annotation,false,true);
GemfireFunctionUtils.registerFunctionForPojoMethod(bean, method, attributes, false);
private void registerAnyDeclaredGemfireFunctionAnnotatedMethods(Object bean) {
stream(nullSafeArray(ReflectionUtils.getAllDeclaredMethods(bean.getClass()), Method.class)).forEach(method -> {
GemfireFunction gemfireFunctionAnnotation = AnnotationUtils.getAnnotation(method, GemfireFunction.class);
if (gemfireFunctionAnnotation != null) {
Assert.isTrue(Modifier.isPublic(method.getModifiers()),
String.format("The bean [%s] method [%s] annotated with [%s] must be public",
bean.getClass().getName(), method.getName(), GemfireFunction.class.getName()));
AnnotationAttributes annotationAttributes = AnnotationAttributes.fromMap(
AnnotationUtils.getAnnotationAttributes(gemfireFunctionAnnotation,false,true));
GemfireFunctionUtils.registerFunctionForPojoMethod(bean, method, annotationAttributes, false);
}
}
});
}
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2002-2013 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.
@@ -19,12 +19,11 @@ import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
/**
* and {@link ImportBeanDefinitionRegistrar} to register the {@link GemfireFunctionBeanPostProcessor}
*
* @author David Turanski
* Spring {@link ImportBeanDefinitionRegistrar} to register the {@link GemfireFunctionBeanPostProcessor}
*
* @author David Turanski
*/
public class GemfireFunctionPostBeanProcessorRegistrar implements ImportBeanDefinitionRegistrar {
public class GemfireFunctionBeanPostProcessorRegistrar implements ImportBeanDefinitionRegistrar {
/* (non-Javadoc)
* @see org.springframework.context.annotation.ImportBeanDefinitionRegistrar#registerBeanDefinitions(org.springframework.core.type.AnnotationMetadata, org.springframework.beans.factory.support.BeanDefinitionRegistry)
@@ -34,5 +33,4 @@ public class GemfireFunctionPostBeanProcessorRegistrar implements ImportBeanDefi
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(GemfireFunctionBeanPostProcessor.class);
BeanDefinitionReaderUtils.registerWithGeneratedName(builder.getBeanDefinition(), registry);
}
}

View File

@@ -10,8 +10,8 @@
* 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.function.execution;
package org.springframework.data.gemfire.function.execution;
import org.apache.geode.cache.execute.Function;
@@ -31,7 +31,7 @@ public interface GemfireFunctionOperations {
* @param args an array of Object arguments to the Function call.
* @return the contents of the ResultsCollector.
*/
public abstract <T> Iterable<T> execute(Function function, Object... args);
<T> Iterable<T> execute(Function function, Object... args);
/**
* Execute a GemFire Function registered with the given ID.
@@ -41,7 +41,7 @@ public interface GemfireFunctionOperations {
* @param args an array of Object arguments to the Function call.
* @return the results
*/
public abstract <T> Iterable<T> execute(String functionId, Object... args);
<T> Iterable<T> execute(String functionId, Object... args);
/**
* Execute an unregistered GemFire Function with the expected singleton result.
@@ -52,7 +52,7 @@ public interface GemfireFunctionOperations {
* @return the first item in the ResultsCollector.
* @see org.apache.geode.cache.execute.Function
*/
public abstract <T> T executeAndExtract(Function function, Object... args);
<T> T executeAndExtract(Function function, Object... args);
/**
* Execute a GemFire Function registered with an ID and with an expected singleton result
@@ -62,7 +62,7 @@ public interface GemfireFunctionOperations {
* @param args an array of Object arguments to the Function call.
* @return the first item in the results collector
*/
public abstract <T> T executeAndExtract(String functionId, Object... args);
<T> T executeAndExtract(String functionId, Object... args);
/**
* Execute a GemFire Function registered with the given ID having no return value.
@@ -70,7 +70,7 @@ public interface GemfireFunctionOperations {
* @param functionId the ID under which the GemFire function is registered.
* @param args an array of Object arguments to the Function call.
*/
public void executeWithNoResult(String functionId, Object... args);
void executeWithNoResult(String functionId, Object... args);
/**
* Execute a GemFire Function using a native GemFire {@link org.apache.geode.cache.execute.Execution} instance.
@@ -80,6 +80,6 @@ public interface GemfireFunctionOperations {
* @return the Function execution result.
* @see org.springframework.data.gemfire.function.execution.GemfireFunctionCallback
*/
public abstract <T> T execute(GemfireFunctionCallback<T> callback);
<T> T execute(GemfireFunctionCallback<T> callback);
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2017 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.util;
import java.util.Optional;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionAttributes;
import org.springframework.util.StringUtils;
/**
* The RegionUtils class...
*
* @author John Blum
* @since 1.0.0
*/
public abstract class RegionUtils extends CacheUtils {
public static boolean isClient(Region region) {
return Optional.ofNullable(region)
.map(Region::getAttributes)
.map(RegionAttributes::getPoolName)
.filter(StringUtils::hasText)
.isPresent();
}
}

View File

@@ -43,6 +43,7 @@ public abstract class SpringUtils {
/* (non-Javadoc) */
public static BeanDefinition addDependsOn(BeanDefinition bean, String... beanNames) {
List<String> dependsOnList = new ArrayList<>();
Collections.addAll(dependsOnList, nullSafeArray(bean.getDependsOn(), String.class));

View File

@@ -0,0 +1,290 @@
/*
* Copyright 2017 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;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Arrays;
import java.util.Collections;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.query.Index;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.data.gemfire.config.schema.SchemaObjectDefinition;
import org.springframework.data.gemfire.config.schema.SchemaObjectType;
import org.springframework.data.gemfire.config.schema.definitions.IndexDefinition;
import org.springframework.data.gemfire.config.schema.definitions.RegionDefinition;
/**
* The GemfireAdminOperationsUnitTests class...
*
* @author John Blum
* @since 1.0.0
*/
@RunWith(MockitoJUnitRunner.class)
public class GemfireAdminOperationsUnitTests {
@Mock
private GemfireAdminOperations adminOperations;
private Index mockIndex(String name) {
Index mockIndex = mock(Index.class, name);
when(mockIndex.getName()).thenReturn(name);
return mockIndex;
}
@SuppressWarnings("unchecked")
private <K, V> Region<K, V> mockRegion(String name) {
Region<K, V> mockRegion = mock(Region.class, name);
when(mockRegion.getName()).thenReturn(name);
return mockRegion;
}
private SchemaObjectDefinition newGenericSchemaObjectDefinition(String name, SchemaObjectType type) {
return mock(SchemaObjectDefinition.class, name);
}
@Test
public void createRegionsWithArrayCallsCreateRegion() {
doCallRealMethod().when(adminOperations).createRegions(ArgumentMatchers.<RegionDefinition[]>any());
RegionDefinition definitionOne = RegionDefinition.from(mockRegion("RegionOne"));
RegionDefinition definitionTwo = RegionDefinition.from(mockRegion("RegionTwo"));
adminOperations.createRegions(definitionOne, definitionTwo);
verify(adminOperations, times(1)).createRegion(eq(definitionOne));
verify(adminOperations, times(1)).createRegion(eq(definitionTwo));
}
@Test
public void createRegionsWithEmptyArray() {
doCallRealMethod().when(adminOperations).createRegions(ArgumentMatchers.<RegionDefinition[]>any());
adminOperations.createRegions();
verify(adminOperations, never()).createRegion(any(RegionDefinition.class));
}
@Test
public void createRegionsWithNullArray() {
doCallRealMethod().when(adminOperations).createRegions(ArgumentMatchers.<RegionDefinition[]>any());
adminOperations.createRegions((RegionDefinition[]) null);
verify(adminOperations, never()).createRegion(any(RegionDefinition.class));
}
@Test
@SuppressWarnings("unchecked")
public void createRegionsWithIterableCallsCreateRegion() {
doCallRealMethod().when(adminOperations).createRegions(any(Iterable.class));
RegionDefinition definitionOne = RegionDefinition.from(mockRegion("RegionOne"));
RegionDefinition definitionTwo = RegionDefinition.from(mockRegion("RegionTwo"));
adminOperations.createRegions(Arrays.asList(definitionOne, definitionTwo));
verify(adminOperations, times(1)).createRegion(eq(definitionOne));
verify(adminOperations, times(1)).createRegion(eq(definitionTwo));
}
@Test
@SuppressWarnings("unchecked")
public void createRegionsWithEmptyIterableCallsCreateRegion() {
doCallRealMethod().when(adminOperations).createRegions(any(Iterable.class));
adminOperations.createRegions(Collections.emptyList());
verify(adminOperations, never()).createRegion(any(RegionDefinition.class));
}
@Test
@SuppressWarnings("unchecked")
public void createRegionsWithNullIterableCallsCreateRegion() {
adminOperations.createRegions((Iterable) null);
verify(adminOperations, never()).createRegion(any(RegionDefinition.class));
}
@Test
public void createLuceneIndexesWithArrayCallsCreateLuceneIndex() {
doCallRealMethod().when(adminOperations).createLuceneIndexes(ArgumentMatchers.<SchemaObjectDefinition[]>any());
SchemaObjectDefinition definitionOne = newGenericSchemaObjectDefinition("LucenIndexOne",
SchemaObjectType.LUCENE_INDEX);
SchemaObjectDefinition definitionTwo = newGenericSchemaObjectDefinition("LucenIndexOne",
SchemaObjectType.LUCENE_INDEX);
adminOperations.createLuceneIndexes(definitionOne, definitionTwo);
verify(adminOperations, times(1)).createLuceneIndex(eq(definitionOne));
verify(adminOperations, times(1)).createLuceneIndex(eq(definitionTwo));
}
@Test
public void createLuceneIndexesWithEmptyArray() {
doCallRealMethod().when(adminOperations).createLuceneIndexes(ArgumentMatchers.<SchemaObjectDefinition[]>any());
adminOperations.createLuceneIndexes();
verify(adminOperations, never()).createLuceneIndex(any(SchemaObjectDefinition.class));
}
@Test
public void createLuceneIndexesWithNullArray() {
doCallRealMethod().when(adminOperations).createLuceneIndexes(ArgumentMatchers.<SchemaObjectDefinition[]>any());
adminOperations.createLuceneIndexes((SchemaObjectDefinition) null);
verify(adminOperations, never()).createLuceneIndex(any(SchemaObjectDefinition.class));
}
@Test
@SuppressWarnings("unchecked")
public void createLuceneIndexesWithIterableCallsCreateLuceneIndex() {
doCallRealMethod().when(adminOperations).createLuceneIndexes(any(Iterable.class));
SchemaObjectDefinition definitionOne = newGenericSchemaObjectDefinition("LucenIndexOne",
SchemaObjectType.LUCENE_INDEX);
SchemaObjectDefinition definitionTwo = newGenericSchemaObjectDefinition("LucenIndexOne",
SchemaObjectType.LUCENE_INDEX);
adminOperations.createLuceneIndexes(Arrays.asList(definitionOne, definitionTwo));
verify(adminOperations, times(1)).createLuceneIndex(eq(definitionOne));
verify(adminOperations, times(1)).createLuceneIndex(eq(definitionTwo));
}
@Test
@SuppressWarnings("unchecked")
public void createLuceneIndexesWithEmptyIterableCallsCreateLuceneIndex() {
doCallRealMethod().when(adminOperations).createLuceneIndexes(any(Iterable.class));
adminOperations.createLuceneIndexes(Collections.emptyList());
verify(adminOperations, never()).createLuceneIndex(any(SchemaObjectDefinition.class));
}
@Test
@SuppressWarnings("unchecked")
public void createLuceneIndexesWithNullIterableCallsCreateLuceneIndex() {
adminOperations.createLuceneIndexes((Iterable) null);
verify(adminOperations, never()).createLuceneIndex(any(SchemaObjectDefinition.class));
}
@Test
public void createIndexesWithArrayCallsCreateIndex() {
doCallRealMethod().when(adminOperations).createIndexes(ArgumentMatchers.<IndexDefinition[]>any());
IndexDefinition definitionOne = IndexDefinition.from(mockIndex("IndexOne"));
IndexDefinition definitionTwo = IndexDefinition.from(mockIndex("IndexTwo"));
adminOperations.createIndexes(definitionOne, definitionTwo);
verify(adminOperations, times(1)).createIndex(eq(definitionOne));
verify(adminOperations, times(1)).createIndex(eq(definitionTwo));
}
@Test
public void createIndexesWithEmptyArray() {
doCallRealMethod().when(adminOperations).createIndexes(ArgumentMatchers.<IndexDefinition[]>any());
adminOperations.createIndexes();
verify(adminOperations, never()).createIndex(any(IndexDefinition.class));
}
@Test
public void createIndexesWithNullArray() {
doCallRealMethod().when(adminOperations).createIndexes(ArgumentMatchers.<IndexDefinition[]>any());
adminOperations.createIndexes();
verify(adminOperations, never()).createIndex(any(IndexDefinition.class));
}
@Test
@SuppressWarnings("unchecked")
public void createIndexesWithIterableCallsCreateIndex() {
doCallRealMethod().when(adminOperations).createIndexes(any(Iterable.class));
IndexDefinition definitionOne = IndexDefinition.from(mockIndex("IndexOne"));
IndexDefinition definitionTwo = IndexDefinition.from(mockIndex("IndexTwo"));
adminOperations.createIndexes(Arrays.asList(definitionOne, definitionTwo));
verify(adminOperations, times(1)).createIndex(eq(definitionOne));
verify(adminOperations, times(1)).createIndex(eq(definitionTwo));
}
@Test
@SuppressWarnings("unchecked")
public void createIndexesWithEmptyIterable() {
doCallRealMethod().when(adminOperations).createIndexes(any(Iterable.class));
adminOperations.createIndexes(Collections.emptyList());
verify(adminOperations, never()).createIndex(any(IndexDefinition.class));
}
@Test
@SuppressWarnings("unchecked")
public void createIndexesWithNullIterable() {
adminOperations.createIndexes((Iterable) null);
verify(adminOperations, never()).createIndex(any(IndexDefinition.class));
}
}

View File

@@ -0,0 +1,156 @@
/*
* Copyright 2017 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.functions;
import static org.assertj.core.api.Assertions.assertThat;
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.when;
import java.util.Collections;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.query.Index;
import org.apache.geode.cache.query.QueryException;
import org.apache.geode.cache.query.QueryService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.data.gemfire.IndexType;
import org.springframework.data.gemfire.config.schema.definitions.IndexDefinition;
/**
* Unit tests for {@link CreateIndexFunction}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mock
* @see org.mockito.Mockito
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.query.Index
* @see org.apache.geode.cache.query.QueryService
* @see org.springframework.data.gemfire.config.schema.definitions.IndexDefinition
* @since 2.0.0
*/
@RunWith(MockitoJUnitRunner.class)
public class CreateIndexFunctionUnitTests {
@Mock
private Cache mockCache;
private CreateIndexFunction createIndexFunction;
@Mock
private Index mockIndex;
@Mock
private QueryService mockQueryService;
@Before
public void setup() {
this.createIndexFunction = spy(new CreateIndexFunction());
doReturn(this.mockCache).when(this.createIndexFunction).resolveCache();
when(this.mockCache.getQueryService()).thenReturn(this.mockQueryService);
when(this.mockIndex.getName()).thenReturn("MockIndex");
}
@Test
public void createsFunctionalIndex() throws QueryException {
when(this.mockIndex.getIndexedExpression()).thenReturn("age");
when(this.mockIndex.getFromClause()).thenReturn("/Customers");
when(this.mockIndex.getType()).thenReturn(IndexType.FUNCTIONAL.getGemfireIndexType());
when(this.mockQueryService.getIndexes()).thenReturn(null);
IndexDefinition indexDefinition = IndexDefinition.from(this.mockIndex);
assertThat(this.createIndexFunction.createIndex(indexDefinition)).isTrue();
verify(this.mockCache, times(2)).getQueryService();
verify(this.mockQueryService, times(1)).getIndexes();
verify(this.mockQueryService, times(1))
.createIndex(eq("MockIndex"), eq("age"), eq("/Customers"));
}
@Test
public void createsHashIndex() throws QueryException {
Index mockIndexTwo = mock(Index.class, "MockIndexTwo");
when(mockIndexTwo.getName()).thenReturn("MockIndexTwo");
when(this.mockIndex.getIndexedExpression()).thenReturn("name");
when(this.mockIndex.getFromClause()).thenReturn("/Customers");
when(this.mockIndex.getType()).thenReturn(IndexType.HASH.getGemfireIndexType());
when(this.mockQueryService.getIndexes()).thenReturn(Collections.singleton(mockIndexTwo));
IndexDefinition indexDefinition = IndexDefinition.from(this.mockIndex);
assertThat(this.createIndexFunction.createIndex(indexDefinition)).isTrue();
verify(this.mockCache, times(2)).getQueryService();
verify(this.mockQueryService, times(1)).getIndexes();
verify(mockIndexTwo, times(1)).getName();
verify(this.mockQueryService, times(1))
.createHashIndex(eq("MockIndex"), eq("name"), eq("/Customers"));
}
@Test
public void createsKeyIndex() throws QueryException {
when(this.mockIndex.getIndexedExpression()).thenReturn("id");
when(this.mockIndex.getFromClause()).thenReturn("/Customers");
when(this.mockIndex.getType()).thenReturn(IndexType.PRIMARY_KEY.getGemfireIndexType());
when(this.mockQueryService.getIndexes()).thenReturn(Collections.emptyList());
IndexDefinition indexDefinition = IndexDefinition.from(this.mockIndex);
assertThat(this.createIndexFunction.createIndex(indexDefinition)).isTrue();
verify(this.mockCache, times(2)).getQueryService();
verify(this.mockQueryService, times(1)).getIndexes();
verify(this.mockQueryService, times(1))
.createKeyIndex(eq("MockIndex"), eq("id"), eq("/Customers"));
}
@Test
public void doesNotCreateIndexWhenIndexAlreadyExists() throws QueryException {
when(this.mockQueryService.getIndexes()).thenReturn(Collections.singleton(this.mockIndex));
IndexDefinition indexDefinition = IndexDefinition.from(this.mockIndex);
assertThat(this.createIndexFunction.createIndex(indexDefinition)).isFalse();
verify(this.mockCache, times(1)).getQueryService();
verify(this.mockQueryService, times(1)).getIndexes();
verify(this.mockQueryService, never()).createIndex(anyString(), anyString(), anyString());
verify(this.mockQueryService, never()).createIndex(anyString(), anyString(), anyString(), anyString());
verify(this.mockQueryService, never()).createHashIndex(anyString(), anyString(), anyString());
verify(this.mockQueryService, never()).createHashIndex(anyString(), anyString(), anyString(), anyString());
verify(this.mockQueryService, never()).createKeyIndex(anyString(), anyString(), anyString());
}
}

View File

@@ -0,0 +1,104 @@
/*
* Copyright 2017 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.functions;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
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.when;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionFactory;
import org.apache.geode.cache.RegionShortcut;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.data.gemfire.config.schema.definitions.RegionDefinition;
/**
* Unit tests for {@link CreateRegionFunction}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mock
* @see org.mockito.Mockito
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.Region
* @see org.springframework.data.gemfire.config.schema.definitions.RegionDefinition
* @since 2.0.0
*/
@RunWith(MockitoJUnitRunner.class)
public class CreateRegionFunctionUnitTests {
@Mock
private Cache mockCache;
@Mock
private Region<Object, Object> mockRegion;
private CreateRegionFunction createRegionFunction;
@Before
public void setup() {
this.createRegionFunction = spy(new CreateRegionFunction());
doReturn(this.mockCache).when(this.createRegionFunction).resolveCache();
when(this.mockRegion.getName()).thenReturn("MockRegion");
}
@Test
@SuppressWarnings("unchecked")
public void createsRegionWhenRegionDoesNotExist() {
RegionDefinition regionDefinition = RegionDefinition.from(this.mockRegion);
RegionFactory<Object, Object> mockRegionFactory = mock(RegionFactory.class);
when(this.mockCache.getRegion(anyString())).thenReturn(null);
when(this.mockCache.createRegionFactory(any(RegionShortcut.class))).thenReturn(mockRegionFactory);
assertThat(this.createRegionFunction.createRegion(regionDefinition)).isTrue();
verify(this.mockCache, times(1)).getRegion(eq("MockRegion"));
verify(this.mockCache, times(1)).createRegionFactory(eq(RegionShortcut.PARTITION));
verify(mockRegionFactory, times(1)).create(eq("MockRegion"));
}
@Test
public void doesNotCreateRegionWhenRegionAlreadyExists() {
RegionDefinition regionDefinition = RegionDefinition.from(this.mockRegion);
when(this.mockCache.getRegion(anyString())).thenReturn(this.mockRegion);
assertThat(this.createRegionFunction.createRegion(regionDefinition)).isFalse();
verify(this.mockCache, times(1)).getRegion(eq("MockRegion"));
verify(this.mockCache, never()).createRegionFactory();
}
}

View File

@@ -0,0 +1,124 @@
/*
* Copyright 2017 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.functions;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Arrays;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.query.Index;
import org.apache.geode.cache.query.QueryService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
/**
* Unit tests for {@link ListIndexesFunction}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mock
* @see org.mockito.Mockito
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.query.Index
* @see org.apache.geode.cache.query.QueryService
* @see org.springframework.data.gemfire.config.admin.functions.ListIndexesFunction
* @since 2.0.0
*/
@RunWith(MockitoJUnitRunner.class)
public class ListIndexesFunctionUnitTests {
@Mock
private Cache mockCache;
@Mock
private Index mockIndexOne;
@Mock
private Index mockIndexTwo;
private ListIndexesFunction listIndexesFunction;
@Mock
private QueryService mockQueryService;
@Before
public void setup() {
this.listIndexesFunction = spy(new ListIndexesFunction());
doReturn(this.mockCache).when(this.listIndexesFunction).resolveCache();
when(this.mockCache.getQueryService()).thenReturn(this.mockQueryService);
when(this.mockIndexOne.getName()).thenReturn("MockIndexOne");
when(this.mockIndexTwo.getName()).thenReturn("MockIndexTwo");
}
@Test
public void listIndexesReturnsIndexNames() {
when(this.mockQueryService.getIndexes()).thenReturn(Arrays.asList(this.mockIndexOne, this.mockIndexTwo));
assertThat(this.listIndexesFunction.listIndexes()).contains("MockIndexOne", "MockIndexTwo");
verify(this.listIndexesFunction, times(1)).resolveCache();
verify(this.mockCache, times(1)).getQueryService();
verify(this.mockQueryService, times(1)).getIndexes();
verify(this.mockIndexOne, times(1)).getName();
verify(this.mockIndexTwo, times(1)).getName();
}
@Test
public void listIndexesReturnsEmptySetWhenCacheIsNull() {
doReturn(null).when(this.listIndexesFunction).resolveCache();
assertThat(this.listIndexesFunction.listIndexes()).isEmpty();
verify(this.listIndexesFunction, times(1)).resolveCache();
}
@Test
public void listIndexesReturnsEmptySetWhenQueryServiceIsNull() {
when(this.mockCache.getQueryService()).thenReturn(null);
assertThat(this.listIndexesFunction.listIndexes()).isEmpty();
verify(this.listIndexesFunction, times(1)).resolveCache();
verify(this.mockCache, times(1)).getQueryService();
}
@Test
public void listIndexesReturnsEmptySetWhenQueryServiceGetIndexesIsNull() {
when(this.mockQueryService.getIndexes()).thenReturn(null);
assertThat(this.listIndexesFunction.listIndexes()).isEmpty();
verify(this.listIndexesFunction, times(1)).resolveCache();
verify(this.mockCache, times(1)).getQueryService();
verify(this.mockQueryService, times(1)).getIndexes();
}
}

View File

@@ -0,0 +1,233 @@
/*
* Copyright 2017 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.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.data.gemfire.util.ArrayUtils.asArray;
import static org.springframework.data.gemfire.util.CollectionUtils.asSet;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionAttributes;
import org.apache.geode.cache.Scope;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.execute.Function;
import org.apache.geode.cache.query.Index;
import org.apache.geode.management.internal.cli.domain.RegionInformation;
import org.apache.geode.management.internal.cli.functions.GetRegionsFunction;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.data.gemfire.client.function.ListRegionsOnServerFunction;
import org.springframework.data.gemfire.config.admin.functions.CreateIndexFunction;
import org.springframework.data.gemfire.config.admin.functions.CreateRegionFunction;
import org.springframework.data.gemfire.config.admin.functions.ListIndexesFunction;
import org.springframework.data.gemfire.config.schema.definitions.IndexDefinition;
import org.springframework.data.gemfire.config.schema.definitions.RegionDefinition;
import org.springframework.data.gemfire.function.execution.GemfireFunctionOperations;
/**
* Unit tests for {@link FunctionGemfireAdminTemplate}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mock
* @see org.mockito.Mockito
* @see org.springframework.data.gemfire.config.admin.remote.FunctionGemfireAdminTemplate
* @since 2.0.0
*/
@RunWith(MockitoJUnitRunner.class)
public class FunctionGemfireAdminTemplateUnitTests {
private FunctionGemfireAdminTemplate template;
@Mock
private ClientCache mockClientCache;
@Mock
private GemfireFunctionOperations mockFunctionOperations;
@Mock
private Index mockIndex;
@Mock
private Region mockRegion;
@Before
public void setup() {
this.template = spy(new FunctionGemfireAdminTemplate(this.mockClientCache));
when(this.template.newGemfireFunctionOperations(any(ClientCache.class)))
.thenReturn(this.mockFunctionOperations);
when(this.mockIndex.getName()).thenReturn("MockIndex");
when(this.mockRegion.getName()).thenReturn("MockRegion");
}
private Region mockRegion(String name) {
Region mockRegion = mock(Region.class, name);
when(mockRegion.getFullPath()).thenReturn(String.format("%1$s%2$s", Region.SEPARATOR, name));
RegionAttributes mockRegionAttributes = mock(RegionAttributes.class,
String.format("Mock%sRegionAttributes", name));
when(mockRegionAttributes.getDataPolicy()).thenReturn(DataPolicy.PARTITION);
when(mockRegionAttributes.getScope()).thenReturn(Scope.DISTRIBUTED_ACK);
when(mockRegion.getAttributes()).thenReturn(mockRegionAttributes);
return mockRegion;
}
private RegionInformation newRegionInformation(String regionName) {
return new RegionInformation(mockRegion(regionName), false);
}
@Test
public void constructFunctionGemfireAdminTemplateWithClientCache() {
FunctionGemfireAdminTemplate template = new FunctionGemfireAdminTemplate(this.mockClientCache);
assertThat(template).isNotNull();
assertThat(template.getClientCache()).isSameAs(this.mockClientCache);
}
@Test(expected = IllegalArgumentException.class)
public void constructFunctionGemfireAdminTemplateWithNull() {
try {
new FunctionGemfireAdminTemplate(null);
}
catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("ClientCache is required");
assertThat(expected).hasNoCause();
throw expected;
}
}
@Test
public void getAvailableServerRegionsExecutesListRegionsOnServerFunction() {
when(this.mockFunctionOperations.executeAndExtract(any(Function.class)))
.thenReturn(asSet("RegionOne", "RegionTwo"));
Iterable<String> availableServerRegions = this.template.getAvailableServerRegions();
assertThat(availableServerRegions).isNotNull();
assertThat(availableServerRegions).hasSize(2);
assertThat(availableServerRegions).contains("RegionOne", "RegionTwo");
verify(this.mockFunctionOperations, times(1))
.executeAndExtract(isA(ListRegionsOnServerFunction.class));
}
@Test
public void getAvailableServerRegionsExecutesGetRegionsFunction() {
when(this.mockFunctionOperations.executeAndExtract(isA(ListRegionsOnServerFunction.class)))
.thenThrow(new RuntimeException("TEST"));
Object[] regionInformation = asArray(newRegionInformation("MockRegionOne"),
newRegionInformation("MockRegionTwo"));
when(this.mockFunctionOperations.executeAndExtract(isA(GetRegionsFunction.class)))
.thenReturn(regionInformation);
Iterable<String> availableServerRegions = this.template.getAvailableServerRegions();
assertThat(availableServerRegions).isNotNull();
assertThat(availableServerRegions).hasSize(2);
assertThat(availableServerRegions).contains("MockRegionOne", "MockRegionTwo");
verify(this.mockFunctionOperations, times(1))
.executeAndExtract(isA(ListRegionsOnServerFunction.class));
verify(this.mockFunctionOperations, times(1))
.executeAndExtract(isA(GetRegionsFunction.class));
}
@Test
public void getAvailableServerRegionIndexesCallsExecuteWithListIndexesFunctionId() {
this.template.getAvailableServerRegionIndexes();
verify(this.mockFunctionOperations, times(1))
.executeAndExtract(eq(ListIndexesFunction.LIST_INDEXES_FUNCTION_ID));
}
@Test
public void createRegionCallsExecuteWithCreateRegionFunctionIdAndRegionDefinition() {
RegionDefinition regionDefinition = RegionDefinition.from(this.mockRegion);
this.template.createRegion(regionDefinition);
verify(this.mockFunctionOperations, times(1))
.executeAndExtract(eq(CreateRegionFunction.CREATE_REGION_FUNCTION_ID), eq(regionDefinition));
}
@Test
public void createIndexCallsExecuteWithCreateIndexFunctionIdAndIndexDefinition() {
IndexDefinition indexDefinition = IndexDefinition.from(this.mockIndex);
this.template.createIndex(indexDefinition);
verify(this.mockFunctionOperations, times(1))
.executeAndExtract(eq(CreateIndexFunction.CREATE_INDEX_FUNCTION_ID), eq(indexDefinition));
}
@Test
public void containsRegionInformationIsNullSafe() {
assertThat(this.template.containsRegionInformation(null)).isFalse();
}
@Test
public void containsRegionInformationReturnsFalseForNonObjectArrayResult() {
assertThat(this.template.containsRegionInformation(newRegionInformation("TestRegion"))).isFalse();
}
@Test
public void containsRegionInformationReturnsFalseForEmptyObjectArrayResult() {
assertThat(this.template.containsRegionInformation(asArray())).isFalse();
}
@Test
public void containsRegionInformationReturnsFalseForObjectArrayContainingNonRegionInformation() {
assertThat(this.template.containsRegionInformation(asArray(mockRegion("TestRegion")))).isFalse();
}
@Test
public void containsRegionInformationReturnsTrueForObjectArrayWithRegionInformation() {
assertThat(this.template.containsRegionInformation(asArray(newRegionInformation("TestRegion"))))
.isTrue();
}
}

View File

@@ -0,0 +1,195 @@
/*
* Copyright 2017 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.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.net.URI;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.query.Index;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.data.gemfire.IndexType;
import org.springframework.data.gemfire.config.schema.definitions.IndexDefinition;
import org.springframework.data.gemfire.config.schema.definitions.RegionDefinition;
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.util.MultiValueMap;
import org.springframework.web.client.RestOperations;
/**
* Unit tests for {@link RestHttpGemfireAdminTemplate}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mock
* @see org.mockito.Mockito
* @see org.springframework.data.gemfire.config.admin.remote.RestHttpGemfireAdminTemplate
* @since 2.0.0
*/
@RunWith(MockitoJUnitRunner.class)
public class RestHttpGemfireAdminTemplateUnitTests {
@Mock
private ClientCache mockClientCache;
@Mock
private Index mockIndex;
@Mock
private Region mockRegion;
private RestHttpGemfireAdminTemplate template;
@Mock
private RestOperations mockRestOperations;
@Before
public void setup() {
this.template = new RestHttpGemfireAdminTemplate(this.mockClientCache) {
@Override protected RestOperations newRestOperations() {
return mockRestOperations;
}
};
when(this.mockRegion.getName()).thenReturn("MockRegion");
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());
}
@Test
public void constructDefaultRestHttpGemfireAdminTemplate() {
RestHttpGemfireAdminTemplate template = new RestHttpGemfireAdminTemplate(this.mockClientCache);
assertThat(template).isNotNull();
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();
}
@Test
public void constructCustomRestHttpGemfireAdminTemplate() {
RestHttpGemfireAdminTemplate template =
new RestHttpGemfireAdminTemplate(this.mockClientCache, "skullbox", 8080);
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();
}
@Test
@SuppressWarnings("unchecked")
public void createIndexCallsGemFireManagementRestApi() {
IndexDefinition indexDefinition = IndexDefinition.from(this.mockIndex);
when(this.mockRestOperations.exchange(any(RequestEntity.class), eq(String.class))).thenAnswer(invocation -> {
RequestEntity requestEntity = invocation.getArgument(0);
assertThat(requestEntity).isNotNull();
assertThat(requestEntity.getMethod()).isEqualTo(HttpMethod.POST);
assertThat(requestEntity.getUrl()).isEqualTo(URI.create("http://localhost:7070/gemfire/v1/indexes"));
HttpHeaders headers = requestEntity.getHeaders();
assertThat(headers).isNotNull();
assertThat(headers.getContentType()).isEqualTo(MediaType.APPLICATION_FORM_URLENCODED);
Object body = requestEntity.getBody();
assertThat(body).isInstanceOf(MultiValueMap.class);
MultiValueMap<String, Object> requestBody = (MultiValueMap<String, Object>) body;
assertThat(requestBody.getFirst("name")).isEqualTo(indexDefinition.getName());
assertThat(requestBody.getFirst("expression")).isEqualTo(indexDefinition.getExpression());
assertThat(requestBody.getFirst("region")).isEqualTo(indexDefinition.getFromClause());
assertThat(requestBody.getFirst("type")).isEqualTo(indexDefinition.getIndexType().toString());
return new ResponseEntity(HttpStatus.OK);
});
this.template.createIndex(indexDefinition);
verify(this.mockRestOperations, times(1))
.exchange(isA(RequestEntity.class), eq(String.class));
}
@Test
@SuppressWarnings("unchecked")
public void createRegionCallsGemFireManagementRestApi() {
RegionDefinition regionDefinition = RegionDefinition.from(this.mockRegion);
when(this.mockRestOperations.exchange(any(RequestEntity.class), eq(String.class))).thenAnswer(invocation -> {
RequestEntity requestEntity = invocation.getArgument(0);
assertThat(requestEntity).isNotNull();
assertThat(requestEntity.getMethod()).isEqualTo(HttpMethod.POST);
assertThat(requestEntity.getUrl()).isEqualTo(URI.create("http://localhost:7070/gemfire/v1/regions"));
HttpHeaders headers = requestEntity.getHeaders();
assertThat(headers).isNotNull();
assertThat(headers.getContentType()).isEqualTo(MediaType.APPLICATION_FORM_URLENCODED);
Object body = requestEntity.getBody();
assertThat(body).isInstanceOf(MultiValueMap.class);
MultiValueMap<String, Object> requestBody = (MultiValueMap<String, Object>) body;
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));
return new ResponseEntity(HttpStatus.OK);
});
this.template.createRegion(regionDefinition);
verify(this.mockRestOperations, times(1))
.exchange(isA(RequestEntity.class), eq(String.class));
}
}

View File

@@ -0,0 +1,267 @@
/*
* Copyright 2017 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 java.util.Arrays;
import java.util.Collections;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Import;
import org.springframework.data.gemfire.IndexFactoryBean;
import org.springframework.data.gemfire.IndexType;
import org.springframework.data.gemfire.PartitionedRegionFactoryBean;
import org.springframework.data.gemfire.ReplicatedRegionFactoryBean;
import org.springframework.data.gemfire.client.ClientRegionFactoryBean;
import org.springframework.data.gemfire.config.admin.GemfireAdminOperations;
import org.springframework.data.gemfire.config.admin.functions.CreateIndexFunction;
import org.springframework.data.gemfire.config.admin.functions.CreateRegionFunction;
import org.springframework.data.gemfire.config.admin.functions.ListIndexesFunction;
import org.springframework.data.gemfire.config.admin.remote.RestHttpGemfireAdminTemplate;
import org.springframework.data.gemfire.function.config.EnableGemfireFunctions;
import org.springframework.data.gemfire.process.ProcessWrapper;
import org.springframework.data.gemfire.support.ConnectionEndpoint;
import org.springframework.data.gemfire.test.support.ClientServerIntegrationTestsSupport;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
/**
* Integration tests for the {@link EnableClusterConfiguration} annotation
* and {@link ClusterConfigurationConfiguration} class.
*
* @author John Blum
* @see org.junit.Test
* @see org.springframework.context.annotation.Bean
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.context.annotation.Import
* @see org.springframework.data.gemfire.IndexFactoryBean
* @see org.springframework.data.gemfire.client.ClientRegionFactoryBean
* @see org.springframework.data.gemfire.config.admin.GemfireAdminOperations
* @see org.springframework.data.gemfire.config.annotation.ClusterConfigurationConfiguration
* @see org.springframework.data.gemfire.config.annotation.EnableClusterConfiguration
* @see org.springframework.data.gemfire.process.ProcessWrapper
* @see org.springframework.data.gemfire.test.support.ClientServerIntegrationTestsSupport
* @see org.springframework.test.context.ContextConfiguration
* @see org.springframework.test.context.junit4.SpringRunner
* @since 2.0.0
*/
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = EnableClusterConfigurationIntegrationTests.TestConfiguration.class)
@SuppressWarnings("unused")
public class EnableClusterConfigurationIntegrationTests extends ClientServerIntegrationTestsSupport {
private static final String LOG_LEVEL = "warning";
private static ProcessWrapper gemfireServer;
@Autowired
private ClientCache gemfireCache;
private GemfireAdminOperations adminOperations;
@BeforeClass
public static void startGemFireServer() throws Exception {
int availablePort = findAvailablePort();
gemfireServer = run(GemFireServerConfiguration.class,
String.format("-D%s=%d", GEMFIRE_CACHE_SERVER_PORT_PROPERTY, availablePort));
waitForServerToStart("localhost", availablePort);
System.setProperty(GEMFIRE_CACHE_SERVER_PORT_PROPERTY, String.valueOf(availablePort));
}
@AfterClass
public static void stopGemFireServer() {
System.clearProperty(GEMFIRE_CACHE_SERVER_PORT_PROPERTY);
stop(gemfireServer);
}
@Before
public void setup() {
this.adminOperations = new RestHttpGemfireAdminTemplate(this.gemfireCache,
"localhost", Integer.getInteger(GEMFIRE_CACHE_SERVER_PORT_PROPERTY, 40404));
}
@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"));
}
@Configuration
@EnableClusterConfiguration
@Import(GemFireClientConfiguration.class)
static class TestConfiguration {
}
@ClientCacheApplication(logLevel = LOG_LEVEL, subscriptionEnabled = true)
static class GemFireClientConfiguration {
@Bean
ClientCacheConfigurer clientCachePoolPortConfigurer(
@Value("${" + GEMFIRE_CACHE_SERVER_PORT_PROPERTY + ":40404}") int port) {
return (bean, clientCacheFactoryBean) -> clientCacheFactoryBean.setServers(
Collections.singletonList(new ConnectionEndpoint("localhost", port)));
}
@Bean("IndexOne")
@DependsOn("RegionOne")
IndexFactoryBean indexOne(GemFireCache gemfireCache) {
IndexFactoryBean indexFactory = new IndexFactoryBean();
indexFactory.setCache(gemfireCache);
indexFactory.setExpression("id");
indexFactory.setFrom("/RegionOne");
indexFactory.setType(IndexType.KEY);
return indexFactory;
}
@Bean("RegionOne")
ClientRegionFactoryBean<Object, Object> regionOne(GemFireCache gemfireCache) {
ClientRegionFactoryBean<Object, Object> clientRegionFactory = new ClientRegionFactoryBean<>();
clientRegionFactory.setCache(gemfireCache);
clientRegionFactory.setClose(false);
clientRegionFactory.setShortcut(ClientRegionShortcut.PROXY);
return clientRegionFactory;
}
@Bean("RegionTwo")
ClientRegionFactoryBean<Object, Object> regionTwo(GemFireCache gemfireCache) {
ClientRegionFactoryBean<Object, Object> clientRegionFactory = new ClientRegionFactoryBean<>();
clientRegionFactory.setCache(gemfireCache);
clientRegionFactory.setClose(false);
clientRegionFactory.setShortcut(ClientRegionShortcut.PROXY);
return clientRegionFactory;
}
@Bean("RegionThree")
ClientRegionFactoryBean<Object, Object> regionThree(GemFireCache gemfireCache) {
ClientRegionFactoryBean<Object, Object> clientRegionFactory = new ClientRegionFactoryBean<>();
clientRegionFactory.setCache(gemfireCache);
clientRegionFactory.setClose(false);
clientRegionFactory.setShortcut(ClientRegionShortcut.CACHING_PROXY);
return clientRegionFactory;
}
}
@CacheServerApplication(name = "EnableClusterConfigurationIntegrationTests", logLevel = LOG_LEVEL)
@EnableGemfireFunctions
static class GemFireServerConfiguration {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(
EnableClusterConfigurationIntegrationTests.GemFireServerConfiguration.class);
applicationContext.registerShutdownHook();
}
@Bean
CreateIndexFunction createIndexFunction() {
return new CreateIndexFunction();
}
@Bean
CreateRegionFunction createRegionFunction() {
return new CreateRegionFunction();
}
@Bean
ListIndexesFunction listIndexesFunction() {
return new ListIndexesFunction();
}
@Bean
CacheServerConfigurer cacheServerPortConfigurer(
@Value("${" + GEMFIRE_CACHE_SERVER_PORT_PROPERTY + ":40404}") int port) {
return (bean, cacheServerFactoryBean) -> cacheServerFactoryBean.setPort(port);
}
@Bean("IndexTwo")
@DependsOn("RegionTwo")
IndexFactoryBean indexTwo(GemFireCache gemfireCache) {
IndexFactoryBean indexFactory = new IndexFactoryBean();
indexFactory.setCache(gemfireCache);
indexFactory.setExpression("name");
indexFactory.setFrom("/RegionTwo");
indexFactory.setType(IndexType.HASH);
return indexFactory;
}
@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> regionFactoryBean = new ReplicatedRegionFactoryBean<>();
regionFactoryBean.setCache(gemfireCache);
regionFactoryBean.setClose(false);
regionFactoryBean.setPersistent(false);
return regionFactoryBean;
}
}
}

View File

@@ -0,0 +1,153 @@
/*
* Copyright 2017 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.schema;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
import org.springframework.core.Ordered;
/**
* Unit tests for {@link SchemaObjectDefinition}.
*
* @author John Blum
* @see org.junit.Test
* @see org.springframework.data.gemfire.config.schema.SchemaObjectDefinition
* @since 2.0.0
*/
public class SchemaObjectDefinitionUnitTests {
private SchemaObjectDefinition newSchemaObjectDefinition(String name) {
return new TestSchemaObjectDefinition(name);
}
@Test
public void constructSchemaObjectDefinitionWithName() {
SchemaObjectDefinition schemaObjectDefinition = newSchemaObjectDefinition("TEST");
assertThat(schemaObjectDefinition).isNotNull();
assertThat(schemaObjectDefinition.getName()).isEqualTo("TEST");
assertThat(schemaObjectDefinition.getType()).isEqualTo(SchemaObjectType.UNKNOWN);
}
private SchemaObjectDefinition testConstructSchemaObjectDefinitionWithInvalidName(String name) {
try {
return newSchemaObjectDefinition(name);
}
catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("Name [%s] is required", name);
assertThat(expected).hasNoCause();
throw expected;
}
}
@Test(expected = IllegalArgumentException.class)
public void constructSchemaObjectDefinitionWithNull() {
testConstructSchemaObjectDefinitionWithInvalidName(null);
}
@Test(expected = IllegalArgumentException.class)
public void constructSchemaObjectDefinitionWithNoName() {
testConstructSchemaObjectDefinitionWithInvalidName(" ");
}
@Test(expected = IllegalArgumentException.class)
public void constructSchemaObjectDefinitionWithEmptyName() {
testConstructSchemaObjectDefinitionWithInvalidName("");
}
@Test
public void equalSchemaObjectDefinitionsAreEqual() {
SchemaObjectDefinition schemaObjectDefinitionOne = newSchemaObjectDefinition("TEST");
SchemaObjectDefinition schemaObjectDefinitionTwo = newSchemaObjectDefinition("TEST");
assertThat(schemaObjectDefinitionOne).isNotSameAs(schemaObjectDefinitionTwo);
assertThat(schemaObjectDefinitionOne).isEqualTo(schemaObjectDefinitionTwo);
}
@Test
public void identicalSchemaObjectDefinitionsAreEquals() {
SchemaObjectDefinition schemaObjectDefinition = newSchemaObjectDefinition("TEST");
assertThat(schemaObjectDefinition).isEqualTo(schemaObjectDefinition);
}
@Test
public void unequalSchemaObjectDefinitionsAreNotEqual() {
SchemaObjectDefinition schemaObjectDefinitionOne = newSchemaObjectDefinition("TEST");
SchemaObjectDefinition schemaObjectDefinitionTwo = newSchemaObjectDefinition("test");
assertThat(schemaObjectDefinitionOne).isNotSameAs(schemaObjectDefinitionTwo);
assertThat(schemaObjectDefinitionOne).isNotEqualTo(schemaObjectDefinitionTwo);
}
@Test
public void equalSchemaObjectDefinitionsHaveTheSameHashCode() {
SchemaObjectDefinition schemaObjectDefinitionOne = newSchemaObjectDefinition("TEST");
SchemaObjectDefinition schemaObjectDefinitionTwo = newSchemaObjectDefinition("TEST");
assertThat(schemaObjectDefinitionOne).isNotSameAs(schemaObjectDefinitionTwo);
assertThat(schemaObjectDefinitionOne.hashCode()).isEqualTo(schemaObjectDefinitionTwo.hashCode());
}
@Test
public void identicalSchemaObjectDefinitionsHaveTheSameHashCode() {
SchemaObjectDefinition schemaObjectDefinition = newSchemaObjectDefinition("TEST");
assertThat(schemaObjectDefinition.hashCode()).isEqualTo(schemaObjectDefinition.hashCode());
}
@Test
public void unequalSchemaObjectDefinitionsHaveDifferentHashCodes() {
SchemaObjectDefinition schemaObjectDefinitionOne = newSchemaObjectDefinition("TEST");
SchemaObjectDefinition schemaObjectDefinitionTwo = newSchemaObjectDefinition("test");
assertThat(schemaObjectDefinitionOne).isNotSameAs(schemaObjectDefinitionTwo);
assertThat(schemaObjectDefinitionOne.hashCode()).isNotEqualTo(schemaObjectDefinitionTwo.hashCode());
}
@Test
public void toStringPrintsNameAndType() {
assertThat(newSchemaObjectDefinition("test").toString()).isEqualTo("UNKNOWN[test]");
}
private static final class TestSchemaObjectDefinition extends SchemaObjectDefinition {
private TestSchemaObjectDefinition(String name) {
super(name);
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
@Override
public SchemaObjectType getType() {
return SchemaObjectType.UNKNOWN;
}
}
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright 2017 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.schema;
import static java.util.Arrays.stream;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.springframework.data.gemfire.util.CollectionUtils.asSet;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.DiskStore;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.asyncqueue.AsyncEventQueue;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.client.Pool;
import org.apache.geode.cache.execute.Function;
import org.apache.geode.cache.lucene.LuceneIndex;
import org.apache.geode.cache.query.Index;
import org.apache.geode.cache.wan.GatewayReceiver;
import org.apache.geode.cache.wan.GatewaySender;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
/**
* Unit tests for {@link SchemaObjectType}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mock
* @see org.mockito.Mockito
* @see org.springframework.data.gemfire.config.schema.SchemaObjectType
* @since 1.0.0
*/
@RunWith(MockitoJUnitRunner.class)
public class SchemaObjectTypeUnitTests {
@Test
public void objectTypesAreSetAndCorrect() {
Set<Class<?>> expectedSchemaObjectTypes = asSet(AsyncEventQueue.class, Cache.class, ClientCache.class,
DiskStore.class, Function.class, GatewayReceiver.class, GatewaySender.class, Index.class, LuceneIndex.class,
Pool.class, Region.class, Void.class);
Set<Class<?>> actualSchemaObjectTypes = stream(SchemaObjectType.values())
.map(SchemaObjectType::getObjectType)
.collect(Collectors.toSet());
assertThat(actualSchemaObjectTypes).hasSameSizeAs(expectedSchemaObjectTypes);
assertThat(actualSchemaObjectTypes).containsAll(expectedSchemaObjectTypes);
}
@Test
public void fromClass() {
stream(SchemaObjectType.values()).forEach(it ->
assertThat(SchemaObjectType.from(it.getObjectType())).isSameAs(it));
}
@Test
public void fromNullIsUnknown() {
assertThat(SchemaObjectType.from(null)).isSameAs(SchemaObjectType.UNKNOWN);
assertThat(SchemaObjectType.from((Object) null)).isSameAs(SchemaObjectType.UNKNOWN);
}
@Test
public void fromObject() {
stream(SchemaObjectType.values()).filter(it -> !SchemaObjectType.UNKNOWN.equals(it)).forEach(it ->
assertThat(SchemaObjectType.from(mock(it.getObjectType()))).isSameAs(it));
}
@Test
public void fromUntypedObjectIsUnknown() {
assertThat(SchemaObjectType.from(new Object())).isSameAs(SchemaObjectType.UNKNOWN);
}
}

View File

@@ -0,0 +1,316 @@
/*
* Copyright 2017 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.schema.definitions;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.eq;
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 java.io.IOException;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.query.Index;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.data.gemfire.IndexType;
import org.springframework.data.gemfire.config.admin.GemfireAdminOperations;
import org.springframework.data.gemfire.config.schema.SchemaObjectType;
import org.springframework.data.gemfire.test.support.IOUtils;
/**
* Unit tests for {@link IndexDefinition}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mock
* @see org.mockito.Mockito
* @see org.apache.geode.cache.Region
* @see org.apache.geode.cache.query.Index
* @see org.springframework.data.gemfire.IndexType
* @see org.springframework.data.gemfire.config.schema.definitions.IndexDefinition
* @since 2.0.0
*/
@RunWith(MockitoJUnitRunner.class)
public class IndexDefinitionUnitTests {
@Mock
private Index mockIndex;
@Mock
private Region<?, ?> mockRegion;
@Before
public void setup() {
when(this.mockIndex.getName()).thenReturn("MockIndex");
when(this.mockRegion.getFullPath()).thenReturn(String.format("%sMockRegion", Region.SEPARATOR));
}
private Index mockIndex(String name, String expression, String fromClause, IndexType type) {
Index mockIndex = mock(Index.class);
when(mockIndex.getName()).thenReturn(name);
when(mockIndex.getIndexedExpression()).thenReturn(expression);
when(mockIndex.getFromClause()).thenReturn(fromClause);
when(mockIndex.getType()).thenReturn(type.getGemfireIndexType());
return mockIndex;
}
@Test
public void fromIndexCreatesAnIndexDefinition() {
Index mockIndex = mockIndex("TestIndex", "id", "/Customers", IndexType.PRIMARY_KEY);
IndexDefinition indexDefinition = IndexDefinition.from(mockIndex);
assertThat(indexDefinition).isNotNull();
assertThat(indexDefinition.getExpression()).isEqualTo(mockIndex.getIndexedExpression());
assertThat(indexDefinition.getFromClause()).isEqualTo(mockIndex.getFromClause());
assertThat(indexDefinition.getIndex()).isSameAs(mockIndex);
assertThat(indexDefinition.getIndexType()).isEqualTo(IndexType.valueOf(mockIndex.getType()));
assertThat(indexDefinition.getType()).isEqualTo(SchemaObjectType.INDEX);
}
@Test(expected = IllegalArgumentException.class)
public void fromNullIndexThrowsIllegalArgumentException() {
try {
IndexDefinition.from(null);
}
catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("Index is required");
assertThat(expected).hasNoCause();
throw expected;
}
}
@Test
public void createCallsGemfireAdminOperationsCreateIndexWithThis() {
GemfireAdminOperations mockAdminOperations = mock(GemfireAdminOperations.class);
IndexDefinition indexDefinition = IndexDefinition.from(this.mockIndex);
assertThat(indexDefinition).isNotNull();
assertThat(indexDefinition.getIndex()).isSameAs(this.mockIndex);
indexDefinition.create(mockAdminOperations);
verify(mockAdminOperations, times(1)).createIndex(eq(indexDefinition));
}
@Test
@SuppressWarnings("deprecation")
public void asDifferentIndexTypes() {
when(this.mockIndex.getType()).thenReturn(IndexType.FUNCTIONAL.getGemfireIndexType());
IndexDefinition indexDefinition = IndexDefinition.from(this.mockIndex);
assertThat(indexDefinition).isNotNull();
assertThat(indexDefinition.getIndex()).isSameAs(this.mockIndex);
assertThat(indexDefinition.getIndexType()).isEqualTo(IndexType.FUNCTIONAL);
assertThat(indexDefinition.as(IndexType.HASH)).isSameAs(indexDefinition);
assertThat(indexDefinition.getIndexType()).isEqualTo(IndexType.HASH);
assertThat(indexDefinition.as(org.apache.geode.cache.query.IndexType.PRIMARY_KEY)).isSameAs(indexDefinition);
assertThat(indexDefinition.getIndexType()).isEqualTo(IndexType.PRIMARY_KEY);
}
@Test
public void asNullIndexType() {
when(this.mockIndex.getType()).thenReturn(IndexType.FUNCTIONAL.getGemfireIndexType());
IndexDefinition indexDefinition = IndexDefinition.from(this.mockIndex);
assertThat(indexDefinition).isNotNull();
assertThat(indexDefinition.getIndex()).isSameAs(this.mockIndex);
assertThat(indexDefinition.getIndexType()).isEqualTo(IndexType.FUNCTIONAL);
assertThat(indexDefinition.as((IndexType) null)).isSameAs(indexDefinition);
assertThat(indexDefinition.getIndexType()).isEqualTo(IndexType.FUNCTIONAL);
assertThat(indexDefinition.as(IndexType.HASH)).isSameAs(indexDefinition);
assertThat(indexDefinition.getIndexType()).isEqualTo(IndexType.HASH);
assertThat(indexDefinition.as((IndexType) null)).isSameAs(indexDefinition);
assertThat(indexDefinition.getIndexType()).isEqualTo(IndexType.FUNCTIONAL);
}
private void testHavingIllegalExpression(String expression) {
try {
IndexDefinition.from(this.mockIndex).having(expression);
}
catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("Expression is required");
assertThat(expected).hasNoCause();
throw expected;
}
}
@Test
public void havingExpression() {
when(this.mockIndex.getIndexedExpression()).thenReturn("id");
IndexDefinition indexDefinition = IndexDefinition.from(this.mockIndex);
assertThat(indexDefinition).isNotNull();
assertThat(indexDefinition.getIndex()).isSameAs(this.mockIndex);
assertThat(indexDefinition.getExpression()).isEqualTo("id");
assertThat(indexDefinition.having("age")).isSameAs(indexDefinition);
assertThat(indexDefinition.getExpression()).isEqualTo("age");
}
@Test(expected = IllegalArgumentException.class)
public void havingEmptyExpression() {
testHavingIllegalExpression("");
}
@Test(expected = IllegalArgumentException.class)
public void havingNoExpression() {
testHavingIllegalExpression(" ");
}
@Test(expected = IllegalArgumentException.class)
public void havingNullExpression() {
testHavingIllegalExpression(null);
}
private void testOnIllegalFromClause(String fromClause) {
try {
IndexDefinition.from(this.mockIndex).on(fromClause);
}
catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("From Clause is required");
assertThat(expected).hasNoCause();
throw expected;
}
}
@Test
public void onFromClause() {
when(this.mockIndex.getFromClause()).thenReturn("/People");
IndexDefinition indexDefinition = IndexDefinition.from(this.mockIndex);
assertThat(indexDefinition).isNotNull();
assertThat(indexDefinition.getFromClause()).isEqualTo(this.mockIndex.getFromClause());
assertThat(indexDefinition.on("/Customers")).isSameAs(indexDefinition);
assertThat(indexDefinition.getFromClause()).isEqualTo("/Customers");
}
@Test(expected = IllegalArgumentException.class)
public void onEmptyFromClause() {
testOnIllegalFromClause("");
}
@Test(expected = IllegalArgumentException.class)
public void onNoFromClause() {
testOnIllegalFromClause(" ");
}
@Test(expected = IllegalArgumentException.class)
public void onNullFromClause() {
testOnIllegalFromClause(null);
}
@Test
public void onRegion() {
when(this.mockIndex.getFromClause()).thenReturn("/Mock");
IndexDefinition indexDefinition = IndexDefinition.from(this.mockIndex);
assertThat(indexDefinition).isNotNull();
assertThat(indexDefinition.getFromClause()).isEqualTo(this.mockIndex.getFromClause());
assertThat(indexDefinition.on(this.mockRegion)).isSameAs(indexDefinition);
assertThat(indexDefinition.getFromClause()).isEqualTo(this.mockRegion.getFullPath());
}
@Test(expected = IllegalArgumentException.class)
public void onNullRegion() {
try {
IndexDefinition.from(this.mockIndex).on((Region) null);
}
catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("Region is required");
assertThat(expected).hasNoCause();
throw expected;
}
}
@Test
public void withName() {
IndexDefinition indexDefinition = IndexDefinition.from(this.mockIndex);
assertThat(indexDefinition).isNotNull();
assertThat(indexDefinition.getName()).isEqualTo("MockIndex");
assertThat(indexDefinition.with("TestIndex")).isSameAs(indexDefinition);
assertThat(indexDefinition.getName()).isEqualTo("TestIndex");
assertThat(indexDefinition.with("")).isSameAs(indexDefinition);
assertThat(indexDefinition.getName()).isEqualTo("MockIndex");
assertThat(indexDefinition.with("UniqueIndex")).isSameAs(indexDefinition);
assertThat(indexDefinition.getName()).isEqualTo("UniqueIndex");
assertThat(indexDefinition.with(" ")).isSameAs(indexDefinition);
assertThat(indexDefinition.getName()).isEqualTo("MockIndex");
assertThat(indexDefinition.with("NonUniqueIndex")).isSameAs(indexDefinition);
assertThat(indexDefinition.getName()).isEqualTo("NonUniqueIndex");
assertThat(indexDefinition.with(null)).isSameAs(indexDefinition);
assertThat(indexDefinition.getName()).isEqualTo("MockIndex");
}
@Test
public void serializeDeserializeIsSuccessful() throws ClassNotFoundException, IOException {
when(this.mockIndex.getIndexedExpression()).thenReturn("age");
when(this.mockIndex.getFromClause()).thenReturn("/Customers");
when(this.mockIndex.getType()).thenReturn(IndexType.FUNCTIONAL.getGemfireIndexType());
IndexDefinition indexDefinition = IndexDefinition.from(this.mockIndex);
byte[] indexDefinitionBytes = IOUtils.serializeObject(indexDefinition);
IndexDefinition deserializedIndexDefinition = IOUtils.deserializeObject(indexDefinitionBytes);
assertThat(deserializedIndexDefinition).isNotNull();
assertThat(deserializedIndexDefinition).isNotSameAs(indexDefinition);
assertThat(deserializedIndexDefinition.getIndex()).isInstanceOf(Index.class);
assertThat(deserializedIndexDefinition.getIndex()).isNotSameAs(this.mockIndex);
assertThat(deserializedIndexDefinition.getName()).isEqualTo("MockIndex");
assertThat(deserializedIndexDefinition.getExpression()).isEqualTo("age");
assertThat(deserializedIndexDefinition.getFromClause()).isEqualTo("/Customers");
assertThat(deserializedIndexDefinition.getIndexType()).isEqualTo(IndexType.FUNCTIONAL);
}
}

View File

@@ -0,0 +1,151 @@
/*
* Copyright 2017 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.schema.definitions;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.eq;
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 java.io.IOException;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionShortcut;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.data.gemfire.config.admin.GemfireAdminOperations;
import org.springframework.data.gemfire.config.schema.SchemaObjectType;
import org.springframework.data.gemfire.test.support.IOUtils;
/**
* Unit tests for {@link RegionDefinition}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mock
* @see org.mockito.Mockito
* @see org.springframework.data.gemfire.config.schema.definitions.RegionDefinition
* @since 2.0.0
*/
@RunWith(MockitoJUnitRunner.class)
public class RegionDefinitionUnitTests {
@Mock
private Region<?, ?> mockRegion;
@Before
public void setup() {
assertThat(this.mockRegion).isNotNull();
when(this.mockRegion.getName()).thenReturn("MockRegion");
}
@Test
public void fromRegionCreatesRegionDefinition() {
RegionDefinition regionDefinition = RegionDefinition.from(this.mockRegion);
assertThat(regionDefinition).isNotNull();
assertThat(regionDefinition.getName()).isEqualTo(this.mockRegion.getName());
assertThat(regionDefinition.getRegion()).isSameAs(this.mockRegion);
assertThat(regionDefinition.getRegionShortcut()).isEqualTo(RegionDefinition.DEFAULT_REGION_SHORTCUT);
assertThat(regionDefinition.getType()).isEqualTo(SchemaObjectType.REGION);
}
@Test(expected = IllegalArgumentException.class)
public void fromNullRegionThrowsIllegalArgumentException() {
try {
RegionDefinition.from(null);
}
catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("Region is required");
assertThat(expected).hasNoCause();
throw expected;
}
}
@Test
public void create() {
GemfireAdminOperations mockAdminOperations = mock(GemfireAdminOperations.class);
RegionDefinition regionDefinition = RegionDefinition.from(this.mockRegion);
assertThat(regionDefinition).isNotNull();
assertThat(regionDefinition.getRegion()).isSameAs(this.mockRegion);
regionDefinition.create(mockAdminOperations);
verify(mockAdminOperations, times(1)).createRegion(eq(regionDefinition));
}
@Test
public void havingRegionShortcut() {
RegionDefinition regionDefinition = RegionDefinition.from(this.mockRegion);
assertThat(regionDefinition).isNotNull();
assertThat(regionDefinition.getRegion()).isSameAs(this.mockRegion);
assertThat(regionDefinition.getRegionShortcut()).isEqualTo(RegionDefinition.DEFAULT_REGION_SHORTCUT);
assertThat(regionDefinition.having(RegionShortcut.LOCAL)).isSameAs(regionDefinition);
assertThat(regionDefinition.getRegionShortcut()).isEqualTo(RegionShortcut.LOCAL);
assertThat(regionDefinition.having(null)).isSameAs(regionDefinition);
assertThat(regionDefinition.getRegionShortcut()).isEqualTo(RegionDefinition.DEFAULT_REGION_SHORTCUT);
assertThat(regionDefinition.having(RegionShortcut.REPLICATE)).isSameAs(regionDefinition);
assertThat(regionDefinition.getRegionShortcut()).isEqualTo(RegionShortcut.REPLICATE);
}
@Test
public void withName() {
RegionDefinition regionDefinition = RegionDefinition.from(this.mockRegion);
assertThat(regionDefinition).isNotNull();
assertThat(regionDefinition.getRegion()).isSameAs(this.mockRegion);
assertThat(regionDefinition.getName()).isEqualTo(this.mockRegion.getName());
assertThat(regionDefinition.with("/Test")).isSameAs(regionDefinition);
assertThat(regionDefinition.getName()).isEqualTo("/Test");
assertThat(regionDefinition.with(" ")).isSameAs(regionDefinition);
assertThat(regionDefinition.getName()).isEqualTo(this.mockRegion.getName());
assertThat(regionDefinition.with("/Mock")).isSameAs(regionDefinition);
assertThat(regionDefinition.getName()).isEqualTo("/Mock");
assertThat(regionDefinition.with(null)).isSameAs(regionDefinition);
assertThat(regionDefinition.getName()).isEqualTo(this.mockRegion.getName());
}
@Test
public void serializeDeserializeIsSuccessful() throws IOException, ClassNotFoundException {
RegionDefinition regionDefinition = RegionDefinition.from(this.mockRegion).having(RegionShortcut.REPLICATE);
byte[] regionDefinitionBytes = IOUtils.serializeObject(regionDefinition);
RegionDefinition deserializedRegionDefinition = IOUtils.deserializeObject(regionDefinitionBytes);
assertThat(deserializedRegionDefinition).isNotNull();
assertThat(deserializedRegionDefinition.getRegion()).isNull();
assertThat(deserializedRegionDefinition.getName()).isEqualTo("MockRegion");
assertThat(deserializedRegionDefinition.getRegionShortcut()).isEqualTo(RegionShortcut.REPLICATE);
}
}

View File

@@ -0,0 +1,151 @@
/*
* Copyright 2017 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.schema.support;
import static java.util.Arrays.stream;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.eq;
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.util.CollectionUtils.asSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionAttributes;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.context.ApplicationContext;
/**
* Unit tests for {@link ClientRegionCollector}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mock
* @see org.mockito.Mockito
* @see org.springframework.data.gemfire.config.schema.support.ClientRegionCollector
* @since 2.0.0
*/
@RunWith(MockitoJUnitRunner.class)
public class ClientRegionCollectorUnitTests {
@Mock
private ApplicationContext mockApplicationContext;
@Mock
private GemFireCache mockCache;
private ClientRegionCollector clientRegionCollector = new ClientRegionCollector();
@SuppressWarnings("unchecked")
private <K, V> Region<K, V> mockRegion(String name) {
Region<K, V> mockRegion = mock(Region.class, name);
when(mockRegion.getName()).thenReturn(name);
return mockRegion;
}
private Map<String, Region> asMap(Region<?, ?>... regions) {
return stream(regions).collect(Collectors.toMap(Region::getName, Function.identity()));
}
@Test
public void collectClientRegionsFromApplicationContext() {
Region mockRegionOne = mockRegion("MockRegionOne");
Region mockRegionTwo = mockRegion("MockRegionTwo");
Region mockRegionThree = mockRegion("MockRegionThree");
RegionAttributes mockRegionAttributes = mock(RegionAttributes.class);
when(mockRegionAttributes.getPoolName()).thenReturn("TestPool");
when(mockRegionTwo.getAttributes()).thenReturn(mockRegionAttributes);
Map<String, Region> regionBeans = asMap(mockRegionOne, mockRegionTwo, mockRegionThree);
when(this.mockApplicationContext.getBeansOfType(eq(Region.class))).thenReturn(regionBeans);
Set<Region> clientRegions = this.clientRegionCollector.collectFrom(this.mockApplicationContext);
assertThat(clientRegions).isNotNull();
assertThat(clientRegions).hasSize(1);
assertThat(clientRegions).containsAll(asSet(mockRegionTwo));
verify(this.mockApplicationContext, times(1)).getBeansOfType(eq(Region.class));
}
@Test
public void collectClientRegionsFromApplicationContextWithNoClientRegions() {
Region mockRegionOne = mockRegion("MockRegionOne");
Region mockRegionTwo = mockRegion("MockRegionTwo");
Region mockRegionThree = mockRegion("MockRegionThree");
RegionAttributes mockRegionAttributes = mock(RegionAttributes.class);
when(mockRegionAttributes.getPoolName()).thenReturn(" ");
when(mockRegionOne.getAttributes()).thenReturn(mockRegionAttributes);
when(mockRegionThree.getAttributes()).thenReturn(mockRegionAttributes);
Map<String, Region> regionBeans = asMap(mockRegionOne, mockRegionTwo, mockRegionThree);
when(this.mockApplicationContext.getBeansOfType(eq(Region.class))).thenReturn(regionBeans);
Set<Region> clientRegions = this.clientRegionCollector.collectFrom(this.mockApplicationContext);
assertThat(clientRegions).isNotNull();
assertThat(clientRegions).isEmpty();
verify(this.mockApplicationContext, times(1)).getBeansOfType(eq(Region.class));
}
@Test
@SuppressWarnings("unchecked")
public void collectClientRegionsFromGemFireCache() {
Region mockRegionOne = mockRegion("MockRegionOne");
Region mockRegionTwo = mockRegion("MockRegionTwo");
Region mockRegionThree = mockRegion("MockRegionThree");
RegionAttributes mockRegionAttributes = mock(RegionAttributes.class);
when(mockRegionAttributes.getPoolName()).thenReturn("TestPool");
when(mockRegionOne.getAttributes()).thenReturn(mockRegionAttributes);
when(mockRegionTwo.getAttributes()).thenReturn(mockRegionAttributes);
when(this.mockCache.rootRegions()).thenReturn(asSet(mockRegionOne, mockRegionTwo, mockRegionThree));
Set<Region> clientRegions = this.clientRegionCollector.collectFrom(this.mockCache);
assertThat(clientRegions).isNotNull();
assertThat(clientRegions).hasSize(2);
assertThat(clientRegions).containsAll(asSet(mockRegionOne, mockRegionTwo));
verify(this.mockCache, times(1)).rootRegions();
}
}

View File

@@ -0,0 +1,182 @@
/*
* Copyright 2017 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.schema.support;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.data.gemfire.util.CollectionUtils.asSet;
import java.util.Collections;
import org.apache.geode.cache.GemFireCache;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.data.gemfire.config.schema.SchemaObjectCollector;
import org.springframework.data.gemfire.config.schema.SchemaObjectType;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
/**
* Unit tests for {@link ComposableSchemaObjectCollector}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mock
* @see org.mockito.Mockito
* @see org.springframework.data.gemfire.config.schema.support.ComposableSchemaObjectCollector
* @since 2.0.0
*/
@RunWith(MockitoJUnitRunner.class)
public class ComposableSchemaObjectCollectorUnitTests {
@Mock
private ApplicationContext mockApplicationContext;
@Mock
private GemFireCache mockCache;
@Mock
private SchemaObjectCollector<Object> mockSchemaObjectCollectorOne;
@Mock
private SchemaObjectCollector<Object> mockSchemaObjectCollectorTwo;
private <T> Iterable<T> emptyIterable() {
return Collections::emptyIterator;
}
@Test
public void composeArrayWithNoElements() {
assertThat(ComposableSchemaObjectCollector.compose()).isNull();
}
@Test
public void composeArrayWithOneElement() {
assertThat(ComposableSchemaObjectCollector.compose(this.mockSchemaObjectCollectorOne))
.isSameAs(this.mockSchemaObjectCollectorOne);
}
@Test
public void composeArrayWithTwoElements() {
SchemaObjectCollector<?> composedSchemaObjectCollector = ComposableSchemaObjectCollector.compose(
this.mockSchemaObjectCollectorOne, this.mockSchemaObjectCollectorTwo);
assertThat(composedSchemaObjectCollector).isInstanceOf(ComposableSchemaObjectCollector.class);
assertThat((ComposableSchemaObjectCollector) composedSchemaObjectCollector).hasSize(2);
assertThat((ComposableSchemaObjectCollector) composedSchemaObjectCollector)
.containsAll(asSet(this.mockSchemaObjectCollectorOne, this.mockSchemaObjectCollectorTwo));
}
@Test
public void composeIterableWithNoElements() {
assertThat(ComposableSchemaObjectCollector.compose(emptyIterable())).isNull();
}
@Test
public void composableIterableWithOneElement() {
assertThat(ComposableSchemaObjectCollector.compose(Collections.singleton(this.mockSchemaObjectCollectorTwo)))
.isSameAs(this.mockSchemaObjectCollectorTwo);
}
@Test
public void composeIterableWithTwoElements() {
SchemaObjectCollector<?> composedSchemaObjectCollector = ComposableSchemaObjectCollector.compose(
asSet(this.mockSchemaObjectCollectorOne, this.mockSchemaObjectCollectorTwo));
assertThat(composedSchemaObjectCollector).isInstanceOf(ComposableSchemaObjectCollector.class);
assertThat((ComposableSchemaObjectCollector) composedSchemaObjectCollector)
.containsAll(asSet(this.mockSchemaObjectCollectorOne, this.mockSchemaObjectCollectorTwo));
}
@Test
@SuppressWarnings("unchecked")
public void collectFromApplicationContext() {
SchemaObject region = SchemaObject.of(SchemaObjectType.REGION);
SchemaObject index = SchemaObject.of(SchemaObjectType.INDEX);
SchemaObject diskStore = SchemaObject.of(SchemaObjectType.DISK_STORE);
when(this.mockSchemaObjectCollectorOne.collectFrom(any(ApplicationContext.class)))
.thenReturn(asSet(region, index));
when(this.mockSchemaObjectCollectorTwo.collectFrom(any(ApplicationContext.class)))
.thenReturn(asSet(diskStore));
SchemaObjectCollector composedSchemaObjectCollector = ComposableSchemaObjectCollector.compose(
asSet(this.mockSchemaObjectCollectorOne, this.mockSchemaObjectCollectorTwo));
assertThat(composedSchemaObjectCollector).isNotNull();
Iterable<Object> schemaObjects = composedSchemaObjectCollector.collectFrom(this.mockApplicationContext);
assertThat(schemaObjects).isNotNull();
assertThat(schemaObjects).hasSize(3);
assertThat(schemaObjects).contains(region, index, diskStore);
verify(this.mockSchemaObjectCollectorOne, times(1))
.collectFrom(eq(this.mockApplicationContext));
verify(this.mockSchemaObjectCollectorTwo, times(1))
.collectFrom(eq(this.mockApplicationContext));
}
@Test
@SuppressWarnings("unchecked")
public void collectFromGemFireCache() {
SchemaObject asyncEventQueue = SchemaObject.of(SchemaObjectType.ASYNC_EVENT_QUEUE);
SchemaObject gatewayReceiver = SchemaObject.of(SchemaObjectType.GATEWAY_RECEIVER);
SchemaObject gatewaySender = SchemaObject.of(SchemaObjectType.GATEWAY_SENDER);
when(this.mockSchemaObjectCollectorOne.collectFrom(any(GemFireCache.class)))
.thenReturn(asSet(gatewayReceiver, gatewaySender));
when(this.mockSchemaObjectCollectorTwo.collectFrom(any(GemFireCache.class)))
.thenReturn(asSet(asyncEventQueue));
SchemaObjectCollector composedSchemaObjectCollector = ComposableSchemaObjectCollector.compose(
asSet(this.mockSchemaObjectCollectorOne, this.mockSchemaObjectCollectorTwo));
assertThat(composedSchemaObjectCollector).isNotNull();
Iterable<Object> schemaObjects = composedSchemaObjectCollector.collectFrom(this.mockCache);
assertThat(schemaObjects).isNotNull();
assertThat(schemaObjects).hasSize(3);
assertThat(schemaObjects).contains(gatewayReceiver, gatewaySender, asyncEventQueue);
verify(this.mockSchemaObjectCollectorOne, times(1)).collectFrom(eq(this.mockCache));
verify(this.mockSchemaObjectCollectorTwo, times(1)).collectFrom(eq(this.mockCache));
}
@RequiredArgsConstructor(staticName = "of")
static class SchemaObject {
@NonNull SchemaObjectType type;
}
}

View File

@@ -0,0 +1,169 @@
/*
* Copyright 2017 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.schema.support;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atMost;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.data.gemfire.util.CollectionUtils.asSet;
import java.util.Arrays;
import java.util.Collections;
import java.util.Optional;
import org.apache.geode.cache.Region;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.data.gemfire.config.schema.SchemaObjectDefiner;
import org.springframework.data.gemfire.config.schema.SchemaObjectType;
import org.springframework.data.gemfire.config.schema.definitions.RegionDefinition;
/**
* Unit tests for {@link ComposableSchemaObjectDefiner}.
*
* @author John Blum
* @see org.junit.Test
* @see SchemaObjectDefiner
* @see ComposableSchemaObjectDefiner
* @since 2.0.0
*/
@RunWith(MockitoJUnitRunner.class)
public class ComposableSchemaObjectDefinerUnitTests {
@Mock
private SchemaObjectDefiner mockHandlerOne;
@Mock
private SchemaObjectDefiner mockHandlerTwo;
private <T> Iterable<T> emptyIterable() {
return Collections::<T>emptyIterator;
}
@Test
public void composeArrayWithNoElements() {
assertThat(ComposableSchemaObjectDefiner.compose()).isNull();
}
@Test
public void composeArrayWithOneElement() {
assertThat(ComposableSchemaObjectDefiner.compose(this.mockHandlerOne)).isSameAs(this.mockHandlerOne);
}
@Test
public void composeArrayWithTwoElements() {
SchemaObjectDefiner handler =
ComposableSchemaObjectDefiner.compose(this.mockHandlerOne, this.mockHandlerTwo);
assertThat(handler).isInstanceOf(ComposableSchemaObjectDefiner.class);
assertThat((ComposableSchemaObjectDefiner) handler)
.containsAll(asSet(this.mockHandlerOne, this.mockHandlerTwo));
}
@Test
public void composeIterableWithNoElements() {
assertThat(ComposableSchemaObjectDefiner.compose(emptyIterable())).isNull();
}
@Test
public void composeIterableWithOneElement() {
assertThat(ComposableSchemaObjectDefiner.compose(Collections.singleton(this.mockHandlerTwo)))
.isSameAs(this.mockHandlerTwo);
}
@Test
public void composeIterableWithTwoElements() {
SchemaObjectDefiner handler =
ComposableSchemaObjectDefiner.compose(asSet(this.mockHandlerOne, this.mockHandlerTwo));
assertThat(handler).isInstanceOf(ComposableSchemaObjectDefiner.class);
assertThat((ComposableSchemaObjectDefiner) handler)
.containsAll(asSet(this.mockHandlerOne, this.mockHandlerTwo));
}
@Test
public void getSchemaObjectTypesIsComposed() {
when(this.mockHandlerOne.getSchemaObjectTypes())
.thenReturn(asSet(SchemaObjectType.INDEX, SchemaObjectType.LUCENE_INDEX));
when(this.mockHandlerTwo.getSchemaObjectTypes())
.thenReturn(Collections.singleton(SchemaObjectType.DISK_STORE));
SchemaObjectDefiner handler =
ComposableSchemaObjectDefiner.compose(this.mockHandlerOne, this.mockHandlerTwo);
assertThat(handler).isNotNull();
assertThat(handler.getSchemaObjectTypes()).containsAll(
Arrays.asList(SchemaObjectType.INDEX, SchemaObjectType.LUCENE_INDEX, SchemaObjectType.DISK_STORE));
}
@Test
public void defineReturnsRegionDefinition() {
Region<?, ?> mockRegion = mock(Region.class);
when(mockRegion.getName()).thenReturn("MockRegion");
Optional<RegionDefinition> regionDefinition = Optional.of(RegionDefinition.from(mockRegion));
when(this.mockHandlerTwo.canDefine(any(Region.class))).thenReturn(true);
when(this.mockHandlerTwo.define(any(Region.class))).thenAnswer(invocation -> regionDefinition);
SchemaObjectDefiner handler =
ComposableSchemaObjectDefiner.compose(this.mockHandlerOne, this.mockHandlerTwo);
assertThat(handler).isNotNull();
assertThat(handler.define(mockRegion)).isEqualTo(regionDefinition);
verify(this.mockHandlerOne, atMost(1)).canDefine(eq(mockRegion));
verify(this.mockHandlerOne, never()).define(any());
verify(this.mockHandlerTwo, times(1)).canDefine(eq(mockRegion));
verify(this.mockHandlerTwo, times(1)).define(eq(mockRegion));
}
@Test
public void defineReturnsEmptyOptional() {
Object object = new Object();
when(this.mockHandlerOne.canDefine(any(Object.class))).thenReturn(false);
when(this.mockHandlerTwo.canDefine(any(Object.class))).thenReturn(false);
SchemaObjectDefiner handler =
ComposableSchemaObjectDefiner.compose(this.mockHandlerOne, this.mockHandlerTwo);
assertThat(handler).isNotNull();
assertThat(handler.define(object).isPresent()).isFalse();
verify(this.mockHandlerOne, times(1)).canDefine(eq(object));
verify(this.mockHandlerOne, never()).define(any());
verify(this.mockHandlerTwo, times(1)).canDefine(eq(object));
verify(this.mockHandlerTwo, never()).define(any());
}
}

View File

@@ -0,0 +1,148 @@
/*
* Copyright 2017 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.schema.support;
import static java.util.Arrays.stream;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.eq;
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.util.CollectionUtils.asSet;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.query.Index;
import org.apache.geode.cache.query.QueryService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.context.ApplicationContext;
/**
* Unit tests for {@link IndexCollector}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mock
* @see org.mockito.Mockito
* @since 2.0.0
*/
@RunWith(MockitoJUnitRunner.class)
public class IndexCollectorUnitTests {
@Mock
private ApplicationContext mockApplicationContext;
@Mock
private GemFireCache mockCache;
private IndexCollector indexCollector = new IndexCollector();
@Mock
private QueryService mockQueryService;
@Before
public void setup() {
when(this.mockCache.getQueryService()).thenReturn(this.mockQueryService);
}
private Index mockIndex(String name) {
Index mockIndex = mock(Index.class, name);
when(mockIndex.getName()).thenReturn(name);
return mockIndex;
}
private Map<String, Index> asMap(Index... indexes) {
return stream(indexes).collect(Collectors.toMap(Index::getName, Function.identity()));
}
@Test
public void collectIndexesFromApplicationContext() {
Index mockIndexOne = mockIndex("MockIndexOne");
Index mockIndexTwo = mockIndex("MockIndexTwo");
Map<String, Index> indexBeans = asMap(mockIndexOne, mockIndexTwo);
when(this.mockApplicationContext.getBeansOfType(eq(Index.class))).thenReturn(indexBeans);
Set<Index> indexes = this.indexCollector.collectFrom(this.mockApplicationContext);
assertThat(indexes).isNotNull();
assertThat(indexes).hasSize(2);
assertThat(indexes).containsAll(asSet(mockIndexOne, mockIndexTwo));
verify(this.mockApplicationContext, times(1)).getBeansOfType(eq(Index.class));
}
@Test
public void collectIndexesFromApplicationContextWhenNoBeansOfTypeIndexExist() {
when(this.mockApplicationContext.getBeansOfType(eq(Index.class))).thenReturn(Collections.emptyMap());
Set<Index> indexes = this.indexCollector.collectFrom(this.mockApplicationContext);
assertThat(indexes).isNotNull();
assertThat(indexes).isEmpty();
verify(this.mockApplicationContext, times(1)).getBeansOfType(eq(Index.class));
}
@Test
public void collectIndexesFromGemFireCache() {
Index mockIndexOne = mockIndex("MockIndexOne");
Index mockIndexTwo = mockIndex("MockIndexTwo");
when(this.mockQueryService.getIndexes()).thenReturn(asSet(mockIndexOne, mockIndexTwo));
Set<Index> indexes = this.indexCollector.collectFrom(this.mockCache);
assertThat(indexes).isNotNull();
assertThat(indexes).hasSize(2);
assertThat(indexes).containsAll(asSet(mockIndexOne, mockIndexTwo));
verify(this.mockCache, times(1)).getQueryService();
verify(this.mockQueryService, times(1)).getIndexes();
}
@Test
public void collectIndexesFromGemFireCacheWhenNoIndexesExist() {
when(this.mockQueryService.getIndexes()).thenReturn(Collections.emptySet());
Set<Index> indexes = this.indexCollector.collectFrom(this.mockCache);
assertThat(indexes).isNotNull();
assertThat(indexes).isEmpty();
verify(this.mockCache, times(1)).getQueryService();
verify(this.mockQueryService, times(1)).getIndexes();
}
}

View File

@@ -0,0 +1,127 @@
/*
* Copyright 2017 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.schema.support;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.query.Index;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.data.gemfire.IndexType;
import org.springframework.data.gemfire.config.schema.SchemaObjectType;
import org.springframework.data.gemfire.config.schema.definitions.IndexDefinition;
/**
* Unit tests for {@link IndexDefiner}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mock
* @see org.mockito.Mockito
* @see org.apache.geode.cache.query.Index
* @see org.springframework.data.gemfire.IndexType
* @see org.springframework.data.gemfire.config.schema.definitions.IndexDefinition
* @see IndexDefiner
* @since 1.0.0
*/
@RunWith(MockitoJUnitRunner.class)
public class IndexDefinerUnitTests {
@Mock
private Index mockIndex;
@Mock
private Region<?, ?> mockRegion;
private IndexDefiner indexInstanceHandler = new IndexDefiner();
@Before
@SuppressWarnings("unchecked")
public void setup() {
when(this.mockIndex.getName()).thenReturn("MockIndex");
when(this.mockIndex.getIndexedExpression()).thenReturn("testExpression");
when(this.mockIndex.getFromClause()).thenReturn("/TestFromClause");
when(this.mockIndex.getType()).thenReturn(IndexType.HASH.getGemfireIndexType());
}
@Test
public void canHandleIndexInstanceIsTrue() {
assertThat(this.indexInstanceHandler.canDefine(this.mockIndex)).isTrue();
}
@Test
public void canHandleNullInstanceIsFalse() {
assertThat(this.indexInstanceHandler.canDefine((Object) null)).isFalse();
}
@Test
public void canHandleRegionInstanceIsFalse() {
assertThat(this.indexInstanceHandler.canDefine(this.mockRegion)).isFalse();
}
@Test
public void canHandleIndexTypeIsTrue() {
assertThat(this.indexInstanceHandler.canDefine(Index.class)).isTrue();
}
@Test
public void canHandleNullTypeIsFalse() {
assertThat(this.indexInstanceHandler.canDefine((Class<?>) null)).isFalse();
}
@Test
public void canHandleRegionTypeIsFalse() {
assertThat(this.indexInstanceHandler.canDefine(Region.class)).isFalse();
}
@Test
public void canHandleIndexSchemaObjectTypeIsTrue() {
assertThat(this.indexInstanceHandler.canDefine(SchemaObjectType.INDEX)).isTrue();
}
@Test
public void canHandleNullSchemaObjectTypeIsFalse() {
assertThat(this.indexInstanceHandler.canDefine((SchemaObjectType) null)).isFalse();
}
@Test
public void canHandleRegionSchemaObjectTypeIsFalse() {
assertThat(this.indexInstanceHandler.canDefine(SchemaObjectType.REGION)).isFalse();
}
@Test
public void defineForIndexObject() {
IndexDefinition indexDefinition = this.indexInstanceHandler.define(this.mockIndex).orElse(null);
assertThat(indexDefinition).isNotNull();
assertThat(indexDefinition.getExpression()).isEqualTo("testExpression");
assertThat(indexDefinition.getFromClause()).isEqualTo("/TestFromClause");
assertThat(indexDefinition.getIndexType()).isEqualTo(IndexType.HASH);
assertThat(indexDefinition.getType()).isEqualTo(SchemaObjectType.INDEX);
}
@Test
public void defineForRegionObject() {
assertThat(this.indexInstanceHandler.define(this.mockRegion).isPresent()).isFalse();
}
}

View File

@@ -0,0 +1,140 @@
/*
* Copyright 2017 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.schema.support;
import static java.util.Arrays.stream;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.eq;
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.util.CollectionUtils.asSet;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.Region;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.context.ApplicationContext;
/**
* Unit tests for {@link RegionCollector}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mock
* @see org.mockito.Mockito
* @see org.springframework.data.gemfire.config.schema.support.RegionCollector
* @since 2.0.0
*/
@RunWith(MockitoJUnitRunner.class)
public class RegionCollectorUnitTests {
@Mock
private ApplicationContext mockApplicationContext;
@Mock
private GemFireCache mockCache;
private RegionCollector regionCollector = new RegionCollector();
@SuppressWarnings("unchecked")
private <K, V> Region<K, V> mockRegion(String name) {
Region<K, V> mockRegion = mock(Region.class, name);
when(mockRegion.getName()).thenReturn(name);
return mockRegion;
}
private Map<String, Region> asMap(Region<?, ?>... regions) {
return stream(regions).collect(Collectors.toMap(Region::getName, Function.identity()));
}
@Test
public void collectRegionsFromApplicationContext() {
Region mockRegionOne = mockRegion("MockRegionOne");
Region mockRegionTwo = mockRegion("MockRegionTwo");
Map<String, Region> regionBeans = asMap(mockRegionOne, mockRegionTwo);
when(this.mockApplicationContext.getBeansOfType(eq(Region.class))).thenReturn(regionBeans);
Set<Region> actualRegions = this.regionCollector.collectFrom(this.mockApplicationContext);
assertThat(actualRegions).isNotNull();
assertThat(actualRegions).hasSize(2);
assertThat(actualRegions).containsAll(asSet(mockRegionOne, mockRegionTwo));
verify(this.mockApplicationContext, times(1)).getBeansOfType(eq(Region.class));
}
@Test
public void collectRegionsFromApplicationContextWhenNoBeansOfTypeRegionExist() {
when(this.mockApplicationContext.getBeansOfType(eq(Region.class))).thenReturn(Collections.emptyMap());
Set<Region> actualRegions = this.regionCollector.collectFrom(this.mockApplicationContext);
assertThat(actualRegions).isNotNull();
assertThat(actualRegions).isEmpty();
verify(this.mockApplicationContext, times(1)).getBeansOfType(eq(Region.class));
}
@Test
@SuppressWarnings("unchecked")
public void collectRegionsFromGemFireCache() {
Region mockRegionOne = mockRegion("MockRegionOne");
Region mockRegionTwo = mockRegion("MockRegionTwo");
when(this.mockCache.rootRegions()).thenReturn(asSet(mockRegionOne, mockRegionTwo));
Set<Region> actualRegions = this.regionCollector.collectFrom(this.mockCache);
assertThat(actualRegions).isNotNull();
assertThat(actualRegions).hasSize(2);
assertThat(actualRegions).containsAll(asSet(mockRegionOne, mockRegionTwo));
verify(this.mockCache, times(1)).rootRegions();
}
@Test
@SuppressWarnings("unchecked")
public void collectRegionsFromGemFireCacheWhenNoRootRegionsExist() {
when(this.mockCache.rootRegions()).thenReturn(Collections.emptySet());
Set<Region> actualRegions = this.regionCollector.collectFrom(this.mockCache);
assertThat(actualRegions).isNotNull();
assertThat(actualRegions).isEmpty();
verify(this.mockCache, times(1)).rootRegions();
}
}

View File

@@ -0,0 +1,140 @@
/*
* Copyright 2017 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.schema.support;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionAttributes;
import org.apache.geode.cache.RegionShortcut;
import org.apache.geode.cache.query.Index;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.data.gemfire.config.schema.SchemaObjectType;
import org.springframework.data.gemfire.config.schema.definitions.RegionDefinition;
/**
* Unit tests for {@link RegionDefiner}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mock
* @see org.mockito.Mockito
* @see org.apache.geode.cache.Region
* @see org.springframework.data.gemfire.config.schema.definitions.RegionDefinition
* @since 2.0.0
*/
@RunWith(MockitoJUnitRunner.class)
public class RegionDefinerUnitTests {
@Mock
private Index mockIndex;
@Mock
private Region<Object, Object> mockRegion;
private RegionDefiner regionInstanceHandler = new RegionDefiner();
@Before
public void setup() {
when(this.mockRegion.getName()).thenReturn("MockRegion");
}
@Test
public void canHandleIndexInstanceIsFalse() {
assertThat(this.regionInstanceHandler.canDefine(this.mockIndex)).isFalse();
}
@Test
public void canHandleNullInstanceIsFalse() {
assertThat(this.regionInstanceHandler.canDefine((Object) null)).isFalse();
}
@Test
public void canHandleRegionInstanceIsTrue() {
assertThat(this.regionInstanceHandler.canDefine(this.mockRegion)).isTrue();
}
@Test
public void canHandleIndexTypeIsFalse() {
assertThat(this.regionInstanceHandler.canDefine(Index.class)).isFalse();
}
@Test
public void canHandleNullTypeIsFalse() {
assertThat(this.regionInstanceHandler.canDefine((Class<?>) null)).isFalse();
}
@Test
public void canHandleRegionTypeIsTrue() {
assertThat(this.regionInstanceHandler.canDefine(Region.class)).isTrue();
}
@Test
public void canHandleIndexSchemaObjectTypeIsFalse() {
assertThat(this.regionInstanceHandler.canDefine(SchemaObjectType.INDEX)).isFalse();
}
@Test
public void canHandleNullSchemaObjectTypeIsFalse() {
assertThat(this.regionInstanceHandler.canDefine((SchemaObjectType) null)).isFalse();
}
@Test
public void canHandleRegionSchemaObjectTypeIsTrue() {
assertThat(this.regionInstanceHandler.canDefine(SchemaObjectType.REGION)).isTrue();
}
@Test
@SuppressWarnings("unchecked")
public void defineClientRegion() {
RegionAttributes<Object, Object> mockRegionAttributes = mock(RegionAttributes.class);
when(this.mockRegion.getAttributes()).thenReturn(mockRegionAttributes);
when(mockRegionAttributes.getPoolName()).thenReturn("TestPool");
RegionDefinition regionDefinition = this.regionInstanceHandler.define(this.mockRegion).orElse(null);
assertThat(regionDefinition).isNotNull();
assertThat(regionDefinition.getName()).isEqualTo(this.mockRegion.getName());
assertThat(regionDefinition.getRegionShortcut()).isEqualTo(RegionShortcut.PARTITION);
assertThat(regionDefinition.getType()).isEqualTo(SchemaObjectType.REGION);
}
@Test
@SuppressWarnings("unchecked")
public void definePeerRegion() {
RegionAttributes<Object, Object> mockRegionAttributes = mock(RegionAttributes.class);
when(this.mockRegion.getAttributes()).thenReturn(mockRegionAttributes);
when(mockRegionAttributes.getPoolName()).thenReturn(" ");
assertThat(this.regionInstanceHandler.define(this.mockRegion).isPresent()).isFalse();
}
@Test
public void defineWithNull() {
assertThat(this.regionInstanceHandler.define(null).isPresent()).isFalse();
}
}

View File

@@ -16,8 +16,13 @@
package org.springframework.data.gemfire.test.support;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -35,19 +40,57 @@ public abstract class IOUtils {
/* (non-Javadoc) */
public static boolean close(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
return true;
}
catch (IOException ignore) {
catch (IOException cause) {
if (log.isLoggable(Level.FINE)) {
log.fine(String.format("Failed to close the Closeable object (%1$s) due to an I/O error:%n%2$s",
closeable, ThrowableUtils.toString(ignore)));
closeable, ThrowableUtils.toString(cause)));
}
}
}
return false;
}
/* (non-Javadoc) */
@SuppressWarnings("unchecked")
public static <T> T deserializeObject(byte[] objectBytes) throws IOException, ClassNotFoundException {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(objectBytes);
ObjectInputStream objectInputStream = null;
try {
objectInputStream = new ObjectInputStream(byteArrayInputStream);
return (T) objectInputStream.readObject();
}
finally {
IOUtils.close(objectInputStream);
}
}
/* (non-Javadoc) */
public static byte[] serializeObject(Serializable obj) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = null;
try {
objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(obj);
objectOutputStream.flush();
return byteArrayOutputStream.toByteArray();
}
finally {
IOUtils.close(objectOutputStream);
}
}
}