SGF-548 - Configure Regions with annotations.

Related JIRA: SGF-250 - @EnableGemfireRegions for @Region.

(cherry picked from commit 701aa5f6ef)
Signed-off-by: John Blum <jblum@pivotal.io>
This commit is contained in:
John Blum
2016-12-08 16:13:56 -08:00
parent 2679d78ac2
commit 5adfa166b0
33 changed files with 3103 additions and 185 deletions

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire;
import org.apache.geode.cache.FixedPartitionAttributes;
@@ -21,15 +22,93 @@ import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
/**
* @author David Turanski
* Spring {@link FactoryBean} to create a instance of the {@link FixedPartitionAttributes}.
*
* @author David Turanski
* @author John Blum
* @see org.springframework.beans.factory.FactoryBean
* @see org.springframework.beans.factory.InitializingBean
* @see org.apache.geode.cache.FixedPartitionAttributes
*/
@SuppressWarnings("unused")
public class FixedPartitionAttributesFactoryBean implements FactoryBean<FixedPartitionAttributes>, InitializingBean {
private Boolean primary;
private String partitionName;
private Integer numBuckets;
private FixedPartitionAttributes fixedPartitionAttributes;
private Integer numBuckets;
private String partitionName;
/**
* @inheritDoc
*/
@Override
@SuppressWarnings("all")
public void afterPropertiesSet() throws Exception {
Assert.hasText(this.partitionName, "partitionName must be specified");
if (this.primary == null && this.numBuckets == null){
this.fixedPartitionAttributes = FixedPartitionAttributes.createFixedPartition(this.partitionName);
}
else if (this.primary == null && this.numBuckets != null){
this.fixedPartitionAttributes = FixedPartitionAttributes.createFixedPartition(
this.partitionName, this.numBuckets);
}
else if (this.primary != null && this.numBuckets == null) {
this.fixedPartitionAttributes = FixedPartitionAttributes.createFixedPartition(
this.partitionName, this.primary);
}
else {
this.fixedPartitionAttributes = FixedPartitionAttributes.createFixedPartition(
this.partitionName, this.primary, this.numBuckets);
}
}
/**
* @inheritDoc
*/
@Override
public FixedPartitionAttributes getObject() throws Exception {
return this.fixedPartitionAttributes;
}
/**
* @inheritDoc
*/
@Override
public Class<?> getObjectType() {
return (this.fixedPartitionAttributes != null ? this.fixedPartitionAttributes.getClass()
: FixedPartitionAttributes.class);
}
/**
* @inheritDoc
*/
@Override
public boolean isSingleton() {
return true;
}
/**
* Set the number of buckets in the Partition Region.
*
* @param numBuckets integer value indicating the number of buckets in the Partition Region.
*/
public void setNumBuckets(Integer numBuckets) {
this.numBuckets = numBuckets;
}
/**
* Set the name of the partition in the Partition Region.
*
* @param partitionName name of the partition.
*/
public void setPartitionName(String partitionName) {
this.partitionName = partitionName;
}
/**
* Sets whether this particular PARTITION Region is the primary (i.e. not secondary).
*
@@ -38,63 +117,4 @@ public class FixedPartitionAttributesFactoryBean implements FactoryBean<FixedPar
public void setPrimary(boolean primary) {
this.primary = primary;
}
/**
* @param partitionName the partitionName to set
*/
public void setPartitionName(String partitionName) {
this.partitionName = partitionName;
}
/**
* @param numBuckets the numBuckets to set
*/
public void setNumBuckets(Integer numBuckets) {
this.numBuckets = numBuckets;
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObject()
*/
@Override
public FixedPartitionAttributes getObject() throws Exception {
return fixedPartitionAttributes;
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
*/
@Override
public Class<?> getObjectType() {
return FixedPartitionAttributes.class;
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#isSingleton()
*/
@Override
public boolean isSingleton() {
return true;
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() throws Exception {
Assert.hasText(partitionName,"partitionName cannot be empty or null");
fixedPartitionAttributes = null;
if (primary == null && numBuckets == null){
fixedPartitionAttributes = FixedPartitionAttributes.createFixedPartition(partitionName);
} else if (primary == null && numBuckets != null){
fixedPartitionAttributes = FixedPartitionAttributes.createFixedPartition(partitionName, numBuckets);
} else if (primary != null && numBuckets == null) {
fixedPartitionAttributes = FixedPartitionAttributes.createFixedPartition(partitionName, primary);
} else {
fixedPartitionAttributes = FixedPartitionAttributes.createFixedPartition(partitionName,primary,numBuckets);
}
}
}

View File

@@ -20,49 +20,84 @@ import java.util.List;
import org.apache.geode.cache.FixedPartitionAttributes;
import org.apache.geode.cache.PartitionAttributes;
import org.apache.geode.cache.PartitionAttributesFactory;
import org.apache.geode.cache.PartitionResolver;
import org.apache.geode.cache.partition.PartitionListener;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.gemfire.util.CollectionUtils;
/**
* Spring-friendly bean for creating {@link PartitionAttributes}. Eliminates the need of using
* a XML 'factory-method' tag and allows the attributes properties to be set directly.
* Spring {@link FactoryBean} for creating {@link PartitionAttributes}.
*
* Eliminates the need to use a XML 'factory-method' tag and allows the attributes properties to be set directly.
*
* @author Costin Leau
* @author David Turanski
* @author John Blum
* @see org.springframework.beans.factory.FactoryBean
* @see org.springframework.beans.factory.InitializingBean
* @see org.apache.geode.cache.FixedPartitionAttributes
* @see org.apache.geode.cache.PartitionAttributes
* @see org.apache.geode.cache.PartitionAttributesFactory
* @see org.apache.geode.cache.PartitionResolver
* @see org.apache.geode.cache.partition.PartitionListener
*/
@SuppressWarnings({ "rawtypes", "unchecked", "unused" })
public class PartitionAttributesFactoryBean implements FactoryBean<PartitionAttributes>, InitializingBean {
public class PartitionAttributesFactoryBean<K, V> implements FactoryBean<PartitionAttributes<K, V>>, InitializingBean {
private final org.apache.geode.cache.PartitionAttributesFactory partitionAttributesFactory =
new org.apache.geode.cache.PartitionAttributesFactory();
private List<PartitionListener> partitionListeners;
private List<PartitionListener> listeners;
private PartitionAttributes<K, V> partitionAttributes;
private final PartitionAttributesFactory<K, V> partitionAttributesFactory =
new PartitionAttributesFactory<>();
/**
* @inheritDoc
*/
@Override
public void afterPropertiesSet() throws Exception {
for (PartitionListener listener : CollectionUtils.nullSafeList(partitionListeners)) {
partitionAttributesFactory.addPartitionListener(listener);
}
this.partitionAttributes = partitionAttributesFactory.create();
}
/**
* @inheritDoc
*/
@Override
public PartitionAttributes getObject() throws Exception {
return partitionAttributesFactory.create();
return this.partitionAttributes;
}
/**
* @inheritDoc
*/
@Override
public Class<?> getObjectType() {
return PartitionAttributes.class;
return (this.partitionAttributes != null ? this.partitionAttributes.getClass() : PartitionAttributes.class);
}
/**
* @inheritDoc
*/
@Override
public boolean isSingleton() {
return false;
}
public void setColocatedWith(String colocatedRegionFullPath) {
partitionAttributesFactory.setColocatedWith(colocatedRegionFullPath);
public void setColocatedWith(String collocatedWith) {
partitionAttributesFactory.setColocatedWith(collocatedWith);
}
public void setFixedPartitionAttributes(List<FixedPartitionAttributes> fixedPartitionAttributes) {
for (FixedPartitionAttributes fpa : fixedPartitionAttributes) {
partitionAttributesFactory.addFixedPartitionAttributes(fpa);
for (FixedPartitionAttributes fixedPartitionAttributesElement :
CollectionUtils.nullSafeList(fixedPartitionAttributes)) {
partitionAttributesFactory.addFixedPartitionAttributes(fixedPartitionAttributesElement);
}
}
@@ -70,12 +105,12 @@ public class PartitionAttributesFactoryBean implements FactoryBean<PartitionAttr
partitionAttributesFactory.setLocalMaxMemory(mb);
}
public void setPartitionResolver(PartitionResolver resolver) {
partitionAttributesFactory.setPartitionResolver(resolver);
public void setPartitionListeners(List<PartitionListener> partitionListeners) {
this.partitionListeners = partitionListeners;
}
public void setPartitionListeners(List<PartitionListener> listeners) {
this.listeners = listeners;
public void setPartitionResolver(PartitionResolver resolver) {
partitionAttributesFactory.setPartitionResolver(resolver);
}
public void setRecoveryDelay(long recoveryDelay) {
@@ -97,15 +132,4 @@ public class PartitionAttributesFactoryBean implements FactoryBean<PartitionAttr
public void setTotalNumBuckets(int numBuckets) {
partitionAttributesFactory.setTotalNumBuckets(numBuckets);
}
@Override
public void afterPropertiesSet() throws Exception {
if (listeners != null) {
for (PartitionListener listener : listeners) {
partitionAttributesFactory.addPartitionListener(listener);
}
}
}
}

View File

@@ -78,6 +78,9 @@ public abstract class RegionFactoryBean<K, V> extends RegionLookupFactoryBean<K,
private CacheWriter<K, V> cacheWriter;
private Class<K> keyConstraint;
private Class<V> valueConstraint;
private DataPolicy dataPolicy;
private EvictionAttributes evictionAttributes;
@@ -146,10 +149,18 @@ public abstract class RegionFactoryBean<K, V> extends RegionLookupFactoryBean<K,
regionFactory.setEvictionAttributes(evictionAttributes);
}
if (keyConstraint != null) {
regionFactory.setKeyConstraint(keyConstraint);
}
if (scope != null) {
regionFactory.setScope(scope);
}
if (valueConstraint != null) {
regionFactory.setValueConstraint(valueConstraint);
}
if (attributes != null) {
Assert.state(!attributes.isLockGrantor() || (scope == null) || scope.isGlobal(),
"Lock Grantor only applies to a 'GLOBAL' scoped Region.");
@@ -671,6 +682,10 @@ public abstract class RegionFactoryBean<K, V> extends RegionLookupFactoryBean<K,
this.gatewaySenders = gatewaySenders;
}
public void setKeyConstraint(Class<K> keyConstraint) {
this.keyConstraint = keyConstraint;
}
public void setPersistent(Boolean persistent) {
this.persistent = persistent;
}
@@ -714,6 +729,10 @@ public abstract class RegionFactoryBean<K, V> extends RegionLookupFactoryBean<K,
this.snapshot = snapshot;
}
public void setValueConstraint(Class<V> valueConstraint) {
this.valueConstraint = valueConstraint;
}
/**
* @inheritDoc
*/

View File

@@ -70,18 +70,17 @@ public abstract class RegionLookupFactoryBean<K, V>
synchronized (this.cache) {
if (isLookupEnabled()) {
if (getParent() != null) {
this.region = getParent().getSubregion(regionName);
}
else {
this.region = this.cache.getRegion(regionName);
}
this.region = (getParent() != null ? getParent().<K, V>getSubregion(regionName)
: this.cache.<K, V>getRegion(regionName));
}
if (region != null) {
log.info(String.format("Found Region [%1$s] in Cache [%2$s]", regionName, cache.getName()));
}
else {
log.info(String.format("Falling back to creating Region [%1$s] in Cache [%2$s]",
regionName, cache.getName()));
region = lookupRegion(cache, regionName);
}
}

View File

@@ -16,12 +16,13 @@
package org.springframework.data.gemfire;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.RegionShortcut;
import org.springframework.util.ObjectUtils;
/**
* The RegionShortcutWrapper enum is a Java enumerated type that wraps GemFire's RegionShortcuts
* with Spring Data GemFire RegionShortcutWrapper enumerated values.
* The {@link RegionShortcutWrapper} enum is a Java enumerated type that wraps GemFire's {@link RegionShortcut RegionShortcuts}
* with Spring Data GemFire {@link RegionShortcutWrapper} enumerated values.
*
* @author John Blum
* @see org.apache.geode.cache.RegionShortcut
@@ -29,38 +30,41 @@ import org.springframework.util.ObjectUtils;
*/
@SuppressWarnings("unused")
public enum RegionShortcutWrapper {
LOCAL(RegionShortcut.LOCAL),
LOCAL_HEAP_LRU(RegionShortcut.LOCAL_HEAP_LRU),
LOCAL_OVERFLOW(RegionShortcut.LOCAL_OVERFLOW),
LOCAL_PERSISTENT(RegionShortcut.LOCAL_PERSISTENT),
LOCAL_PERSISTENT_OVERFLOW(RegionShortcut.LOCAL_PERSISTENT_OVERFLOW),
PARTITION(RegionShortcut.PARTITION),
PARTITION_HEAP_LRU(RegionShortcut.PARTITION_HEAP_LRU),
PARTITION_OVERFLOW(RegionShortcut.PARTITION_OVERFLOW),
PARTITION_PERSISTENT(RegionShortcut.PARTITION_PERSISTENT),
PARTITION_PERSISTENT_OVERFLOW(RegionShortcut.PARTITION_PERSISTENT_OVERFLOW),
PARTITION_PROXY(RegionShortcut.PARTITION_PROXY),
PARTITION_PROXY_REDUNDANT(RegionShortcut.PARTITION_PROXY_REDUNDANT),
PARTITION_REDUNDANT(RegionShortcut.PARTITION_REDUNDANT),
PARTITION_REDUNDANT_HEAP_LRU(RegionShortcut.PARTITION_REDUNDANT_HEAP_LRU),
PARTITION_REDUNDANT_OVERFLOW(RegionShortcut.PARTITION_REDUNDANT_OVERFLOW),
PARTITION_REDUNDANT_PERSISTENT(RegionShortcut.PARTITION_REDUNDANT_PERSISTENT),
PARTITION_REDUNDANT_PERSISTENT_OVERFLOW(RegionShortcut.PARTITION_REDUNDANT_PERSISTENT_OVERFLOW),
REPLICATE(RegionShortcut.REPLICATE),
REPLICATE_HEAP_LRU(RegionShortcut.REPLICATE_HEAP_LRU),
REPLICATE_OVERFLOW(RegionShortcut.REPLICATE_OVERFLOW),
REPLICATE_PERSISTENT(RegionShortcut.REPLICATE_PERSISTENT),
REPLICATE_PERSISTENT_OVERFLOW(RegionShortcut.REPLICATE_PERSISTENT_OVERFLOW),
REPLICATE_PROXY(RegionShortcut.REPLICATE_PROXY),
UNSPECIFIED(null);
LOCAL(RegionShortcut.LOCAL, DataPolicy.NORMAL),
LOCAL_HEAP_LRU(RegionShortcut.LOCAL_HEAP_LRU, DataPolicy.NORMAL),
LOCAL_OVERFLOW(RegionShortcut.LOCAL_OVERFLOW, DataPolicy.NORMAL),
LOCAL_PERSISTENT(RegionShortcut.LOCAL_PERSISTENT, DataPolicy.NORMAL),
LOCAL_PERSISTENT_OVERFLOW(RegionShortcut.LOCAL_PERSISTENT_OVERFLOW, DataPolicy.PERSISTENT_REPLICATE),
PARTITION(RegionShortcut.PARTITION, DataPolicy.PARTITION),
PARTITION_HEAP_LRU(RegionShortcut.PARTITION_HEAP_LRU, DataPolicy.PARTITION),
PARTITION_OVERFLOW(RegionShortcut.PARTITION_OVERFLOW, DataPolicy.PARTITION),
PARTITION_PERSISTENT(RegionShortcut.PARTITION_PERSISTENT, DataPolicy.PERSISTENT_PARTITION),
PARTITION_PERSISTENT_OVERFLOW(RegionShortcut.PARTITION_PERSISTENT_OVERFLOW, DataPolicy.PERSISTENT_PARTITION),
PARTITION_PROXY(RegionShortcut.PARTITION_PROXY, DataPolicy.PARTITION),
PARTITION_PROXY_REDUNDANT(RegionShortcut.PARTITION_PROXY_REDUNDANT, DataPolicy.PARTITION),
PARTITION_REDUNDANT(RegionShortcut.PARTITION_REDUNDANT, DataPolicy.PARTITION),
PARTITION_REDUNDANT_HEAP_LRU(RegionShortcut.PARTITION_REDUNDANT_HEAP_LRU, DataPolicy.PARTITION),
PARTITION_REDUNDANT_OVERFLOW(RegionShortcut.PARTITION_REDUNDANT_OVERFLOW, DataPolicy.PARTITION),
PARTITION_REDUNDANT_PERSISTENT(RegionShortcut.PARTITION_REDUNDANT_PERSISTENT, DataPolicy.PERSISTENT_PARTITION),
PARTITION_REDUNDANT_PERSISTENT_OVERFLOW(RegionShortcut.PARTITION_REDUNDANT_PERSISTENT_OVERFLOW, DataPolicy.PERSISTENT_PARTITION),
REPLICATE(RegionShortcut.REPLICATE, DataPolicy.REPLICATE),
REPLICATE_HEAP_LRU(RegionShortcut.REPLICATE_HEAP_LRU, DataPolicy.REPLICATE),
REPLICATE_OVERFLOW(RegionShortcut.REPLICATE_OVERFLOW, DataPolicy.REPLICATE),
REPLICATE_PERSISTENT(RegionShortcut.REPLICATE_PERSISTENT, DataPolicy.PERSISTENT_REPLICATE),
REPLICATE_PERSISTENT_OVERFLOW(RegionShortcut.REPLICATE_PERSISTENT_OVERFLOW, DataPolicy.PERSISTENT_REPLICATE),
REPLICATE_PROXY(RegionShortcut.REPLICATE_PROXY, DataPolicy.EMPTY),
UNSPECIFIED(null, null);
private final DataPolicy dataPolicy;
private final RegionShortcut regionShortcut;
RegionShortcutWrapper(final RegionShortcut regionShortcut) {
RegionShortcutWrapper(RegionShortcut regionShortcut, DataPolicy dataPolicy) {
this.regionShortcut = regionShortcut;
this.dataPolicy = dataPolicy;
}
public static RegionShortcutWrapper valueOf(final RegionShortcut regionShortcut) {
public static RegionShortcutWrapper valueOf(RegionShortcut regionShortcut) {
for (RegionShortcutWrapper wrapper : values()) {
if (ObjectUtils.nullSafeEquals(wrapper.getRegionShortcut(), regionShortcut)) {
return wrapper;
@@ -70,6 +74,14 @@ public enum RegionShortcutWrapper {
return RegionShortcutWrapper.UNSPECIFIED;
}
public DataPolicy getDataPolicy() {
return this.dataPolicy;
}
public RegionShortcut getRegionShortcut() {
return this.regionShortcut;
}
public boolean isHeapLru() {
return name().contains("HEAP_LRU");
}
@@ -105,9 +117,4 @@ public enum RegionShortcutWrapper {
public boolean isReplicate() {
return name().contains("REPLICATE");
}
public RegionShortcut getRegionShortcut() {
return regionShortcut;
}
}

View File

@@ -85,6 +85,9 @@ public class ClientRegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V>
private CacheWriter<K, V> cacheWriter;
private Class<K> keyConstraint;
private Class<V> valueConstraint;
private ClientRegionShortcut shortcut = null;
private DataPolicy dataPolicy;
@@ -125,6 +128,14 @@ public class ClientRegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V>
setEvictionAttributes(clientRegionFactory);
setPoolName(clientRegionFactory);
if (keyConstraint != null) {
clientRegionFactory.setKeyConstraint(keyConstraint);
}
if (valueConstraint != null) {
clientRegionFactory.setValueConstraint(valueConstraint);
}
return logCreateRegionEvent(create(clientRegionFactory, regionName));
}
@@ -562,6 +573,10 @@ public class ClientRegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V>
return this.interests;
}
public void setKeyConstraint(Class<K> keyConstraint) {
this.keyConstraint = keyConstraint;
}
protected boolean isPersistentUnspecified() {
return (persistent == null);
}
@@ -619,4 +634,8 @@ public class ClientRegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V>
public void setSnapshot(Resource snapshot) {
this.snapshot = snapshot;
}
public void setValueConstraint(Class<V> valueConstraint) {
this.valueConstraint = valueConstraint;
}
}

View File

@@ -16,12 +16,13 @@
package org.springframework.data.gemfire.client;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.springframework.util.ObjectUtils;
/**
* The ClientRegionShortcutWrapper enum is a Java enumerated type that wraps GemFire's ClientRegionShortcuts
* with Spring Data GemFire ClientRegionShortcutWrapper enumerated values.
* The {@link ClientRegionShortcutWrapper} enum is a Java enumerated type that wraps GemFire's {@link ClientRegionShortcut ClientRegionShortcuts}
* with Spring Data GemFire {@link ClientRegionShortcutWrapper} enumerated values.
*
* @author John Blum
* @see org.apache.geode.cache.client.ClientRegionShortcut
@@ -29,24 +30,27 @@ import org.springframework.util.ObjectUtils;
*/
@SuppressWarnings("unused")
public enum ClientRegionShortcutWrapper {
CACHING_PROXY(ClientRegionShortcut.CACHING_PROXY),
CACHING_PROXY_HEAP_LRU(ClientRegionShortcut.CACHING_PROXY_HEAP_LRU),
CACHING_PROXY_OVERFLOW(ClientRegionShortcut.CACHING_PROXY_OVERFLOW),
LOCAL(ClientRegionShortcut.LOCAL),
LOCAL_HEAP_LRU(ClientRegionShortcut.LOCAL_HEAP_LRU),
LOCAL_OVERFLOW(ClientRegionShortcut.LOCAL_OVERFLOW),
LOCAL_PERSISTENT(ClientRegionShortcut.LOCAL_PERSISTENT),
LOCAL_PERSISTENT_OVERFLOW(ClientRegionShortcut.LOCAL_PERSISTENT_OVERFLOW),
PROXY(ClientRegionShortcut.PROXY),
UNSPECIFIED(null);
CACHING_PROXY(ClientRegionShortcut.CACHING_PROXY, DataPolicy.NORMAL),
CACHING_PROXY_HEAP_LRU(ClientRegionShortcut.CACHING_PROXY_HEAP_LRU, DataPolicy.NORMAL),
CACHING_PROXY_OVERFLOW(ClientRegionShortcut.CACHING_PROXY_OVERFLOW, DataPolicy.NORMAL),
LOCAL(ClientRegionShortcut.LOCAL, DataPolicy.NORMAL),
LOCAL_HEAP_LRU(ClientRegionShortcut.LOCAL_HEAP_LRU, DataPolicy.NORMAL),
LOCAL_OVERFLOW(ClientRegionShortcut.LOCAL_OVERFLOW, DataPolicy.NORMAL),
LOCAL_PERSISTENT(ClientRegionShortcut.LOCAL_PERSISTENT, DataPolicy.PERSISTENT_REPLICATE),
LOCAL_PERSISTENT_OVERFLOW(ClientRegionShortcut.LOCAL_PERSISTENT_OVERFLOW, DataPolicy.PERSISTENT_REPLICATE),
PROXY(ClientRegionShortcut.PROXY, DataPolicy.EMPTY),
UNSPECIFIED(null, null);
private final ClientRegionShortcut clientRegionShortcut;
ClientRegionShortcutWrapper(final ClientRegionShortcut clientRegionShortcut) {
private final DataPolicy dataPolicy;
ClientRegionShortcutWrapper(ClientRegionShortcut clientRegionShortcut, DataPolicy dataPolicy) {
this.clientRegionShortcut = clientRegionShortcut;
this.dataPolicy = dataPolicy;
}
public static ClientRegionShortcutWrapper valueOf(final ClientRegionShortcut clientRegionShortcut) {
public static ClientRegionShortcutWrapper valueOf(ClientRegionShortcut clientRegionShortcut) {
for (ClientRegionShortcutWrapper wrapper : values()) {
if (ObjectUtils.nullSafeEquals(wrapper.getClientRegionShortcut(), clientRegionShortcut)) {
return wrapper;
@@ -57,7 +61,11 @@ public enum ClientRegionShortcutWrapper {
}
public ClientRegionShortcut getClientRegionShortcut() {
return clientRegionShortcut;
return this.clientRegionShortcut;
}
public DataPolicy getDataPolicy() {
return this.dataPolicy;
}
public boolean isCaching() {
@@ -87,5 +95,4 @@ public enum ClientRegionShortcutWrapper {
public boolean isProxy() {
return name().contains("PROXY");
}
}

View File

@@ -0,0 +1,110 @@
/*
* Copyright 2016 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.Region;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.AliasFor;
/**
* The {@link EnableEntityDefinedRegions} annotation marks a Spring {@link org.springframework.context.annotation.Configuration @Configuration}
* annotated class to enable the creation of the GemFire/Geode {@link Region Regions} based on
* the application domain model object entities.
*
* @author John Blum
* @see org.springframework.context.annotation.ComponentScan
* @see org.springframework.context.annotation.Import
* @see org.springframework.core.annotation.AliasFor
* @see org.springframework.data.gemfire.config.annotation.EntityDefinedRegionsConfiguration
* @see org.apache.geode.cache.Region
* @since 1.9.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Import(EntityDefinedRegionsConfiguration.class)
@SuppressWarnings({ "unused" })
public @interface EnableEntityDefinedRegions {
/**
* Alias for {@link #basePackages()} attribute.
*
* @return a {@link String} array specifying the packages to search for application persistent entities.
* @see #basePackages()
*/
@AliasFor(attribute = "basePackages")
String[] value() default {};
/**
* Base packages to scan for {@link org.springframework.data.gemfire.mapping.Region @Region} annotated
* application persistent entities. {@link #value()} is an alias for this attribute.
* Use {@link #basePackageClasses()} for a type-safe alternative to String-based package names.
*
* @return a {@link String} array specifying the packages to search for application persistent entities.
* @see #value()
*/
@AliasFor(attribute = "value")
String[] basePackages() default {};
/**
* Type-safe alternative to {@link #basePackages()} for specifying the packages to scan for
* {@link org.springframework.data.gemfire.mapping.Region @Region} annotated application persistent entities.
* The package of each class specified will be scanned. Consider creating a special no-op marker class or interface
* in each package that serves no other purpose than being referenced by this attribute.
*
* @return an array of {@link Class classes} used to determine the packages to scan
* for application persistent entities.
*/
Class<?>[] basePackageClasses() default {};
/**
* Specifies which types are eligible for component scanning. Further narrows the set of candidate components
* from everything in {@link #basePackages()} to everything in the base packages that matches the given filter
* or filters.
*
* @return an array {@link org.springframework.context.annotation.ComponentScan.Filter} of Filters used to
* specify application persistent entities to be included during the component scan.
*/
ComponentScan.Filter[] includeFilters() default {};
/**
* Specifies which types are not eligible for component scanning.
*
* @return an array of {@link org.springframework.context.annotation.ComponentScan.Filter Filters} used to
* specify application persistent entities to be excluded during the component scan.
*/
ComponentScan.Filter[] excludeFilters() default {};
/**
* Determines whether the created {@link Region} will have strongly-typed key and value constraints
* based on the ID and {@link Class} type of application persistent entity.
*
* Defaults to {@literal false}.
*/
boolean strict() default false;
}

View File

@@ -0,0 +1,603 @@
/*
* Copyright 2016 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.apache.geode.internal.lang.ObjectUtils.defaultIfNull;
import static org.springframework.data.gemfire.util.ArrayUtils.defaultIfEmpty;
import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.AspectJTypeFilter;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.core.type.filter.RegexPatternTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.data.gemfire.FixedPartitionAttributesFactoryBean;
import org.springframework.data.gemfire.LocalRegionFactoryBean;
import org.springframework.data.gemfire.PartitionAttributesFactoryBean;
import org.springframework.data.gemfire.PartitionedRegionFactoryBean;
import org.springframework.data.gemfire.RegionAttributesFactoryBean;
import org.springframework.data.gemfire.RegionLookupFactoryBean;
import org.springframework.data.gemfire.ReplicatedRegionFactoryBean;
import org.springframework.data.gemfire.ScopeType;
import org.springframework.data.gemfire.client.ClientRegionFactoryBean;
import org.springframework.data.gemfire.config.annotation.support.GemFireCacheTypeAwareRegionFactoryBean;
import org.springframework.data.gemfire.config.annotation.support.GemFireComponentClassTypeScanner;
import org.springframework.data.gemfire.config.xml.GemfireConstants;
import org.springframework.data.gemfire.mapping.ClientRegion;
import org.springframework.data.gemfire.mapping.GemfireMappingContext;
import org.springframework.data.gemfire.mapping.GemfirePersistentEntity;
import org.springframework.data.gemfire.mapping.LocalRegion;
import org.springframework.data.gemfire.mapping.PartitionRegion;
import org.springframework.data.gemfire.mapping.ReplicateRegion;
import org.springframework.data.gemfire.util.ArrayUtils;
import org.springframework.data.gemfire.util.CollectionUtils;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* The EntityDefinedRegionsConfiguration class...
*
* @author John Blum
* @since 1.0.0
*/
public class EntityDefinedRegionsConfiguration
implements BeanClassLoaderAware, BeanFactoryAware, ImportBeanDefinitionRegistrar {
protected static final Class<? extends RegionLookupFactoryBean> DEFAULT_REGION_FACTORY_BEAN_CLASS =
GemFireCacheTypeAwareRegionFactoryBean.class;
protected static final Map<Class<? extends Annotation>, Class<? extends RegionLookupFactoryBean>> regionAnnotationToRegionFactoryBeanClass =
new HashMap<>();
static {
regionAnnotationToRegionFactoryBeanClass.put(ClientRegion.class, ClientRegionFactoryBean.class);
regionAnnotationToRegionFactoryBeanClass.put(LocalRegion.class, LocalRegionFactoryBean.class);
regionAnnotationToRegionFactoryBeanClass.put(PartitionRegion.class, PartitionedRegionFactoryBean.class);
regionAnnotationToRegionFactoryBeanClass.put(ReplicateRegion.class, ReplicatedRegionFactoryBean.class);
regionAnnotationToRegionFactoryBeanClass.put(org.springframework.data.gemfire.mapping.Region.class,
DEFAULT_REGION_FACTORY_BEAN_CLASS);
}
private BeanFactory beanFactory;
private ClassLoader beanClassLoader;
private GemfireMappingContext mappingContext;
/**
* Returns the {@link Annotation} {@link Class type} that configures and creates {@link Region Regions}
* for application persistent entities.
*
* @return the {@link Annotation} {@link Class type} that configures and creates {@link Region Regions}
* for application persistent entities.
* @see org.springframework.data.gemfire.config.annotation.EnableEntityDefinedRegions
* @see java.lang.annotation.Annotation
* @see java.lang.Class
*/
protected Class<? extends Annotation> getAnnotationType() {
return EnableEntityDefinedRegions.class;
}
/**
* Returns the name of the {@link Annotation} type that configures and creates {@link Region Regions}
* for application persistent entities.
*
* @return the name of the {@link Annotation} type that configures and creates {@link Region Regions}
* for application persistent entities.
* @see java.lang.Class#getName()
* @see #getAnnotationType()
*/
protected String getAnnotationTypeName() {
return getAnnotationType().getName();
}
/**
* Returns the simple name of the {@link Annotation} type that configures and creates {@link Region Regions}
* for application persistent entities.
*
* @return the simple name of the {@link Annotation} type that configures and creates {@link Region Regions}
* for application persistent entities.
* @see java.lang.Class#getSimpleName()
* @see #getAnnotationType()
*/
@SuppressWarnings("unused")
protected String getAnnotationTypeSimpleName() {
return getAnnotationType().getSimpleName();
}
/**
* @inheritDoc
*/
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = classLoader;
}
/* (non-Javadoc) */
protected ClassLoader getBeanClassLoader() {
return this.beanClassLoader;
}
/**
* @inheritDoc
*/
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
/* (non-Javadoc) */
protected BeanFactory getBeanFactory() {
return this.beanFactory;
}
/* (non-Javadoc) */
protected boolean isAnnotationPresent(AnnotationMetadata importingClassMetadata) {
return isAnnotationPresent(importingClassMetadata, getAnnotationTypeName());
}
/* (non-Javadoc) */
protected boolean isAnnotationPresent(AnnotationMetadata importingClassMetadata, String annotationName) {
return importingClassMetadata.hasAnnotation(annotationName);
}
/* (non-Javadoc) */
protected AnnotationAttributes getAnnotationAttributes(AnnotationMetadata importingClassMetadata) {
return getAnnotationAttributes(importingClassMetadata, getAnnotationTypeName());
}
/* (non-Javadoc) */
protected AnnotationAttributes getAnnotationAttributes(AnnotationMetadata importingClassMetadata,
String annotationName) {
return AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(annotationName));
}
/* (non-Javadoc) */
protected GemfirePersistentEntity<?> getPersistentEntity(Class<?> persistentEntityType) {
return resolveMappingContext().getPersistentEntity(persistentEntityType);
}
/* (non-Javadoc) */
protected GemfireMappingContext resolveMappingContext() {
if (this.mappingContext == null) {
try {
this.mappingContext = getBeanFactory().getBean(GemfireMappingContext.class);
}
catch (Throwable ignore) {
this.mappingContext = new GemfireMappingContext();
}
}
return this.mappingContext;
}
/**
* @inheritDoc
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
if (isAnnotationPresent(importingClassMetadata)) {
AnnotationAttributes enableEntityDefinedRegionsAttributes = getAnnotationAttributes(importingClassMetadata);
boolean strict = enableEntityDefinedRegionsAttributes.getBoolean("strict");
for (Class<?> persistentEntityClass : newGemFireComponentClassTypeScanner(
importingClassMetadata, enableEntityDefinedRegionsAttributes).scan()) {
GemfirePersistentEntity persistentEntity = getPersistentEntity(persistentEntityClass);
registerRegionBeanDefinition(persistentEntity, strict, registry);
postProcess(persistentEntity);
}
}
}
/* (non-Javadoc) */
protected GemFireComponentClassTypeScanner newGemFireComponentClassTypeScanner(
AnnotationMetadata importingClassMetadata, AnnotationAttributes enableEntityDefinedRegionsAttributes) {
Set<String> resolvedBasePackages = resolveBasePackages(importingClassMetadata,
enableEntityDefinedRegionsAttributes);
return GemFireComponentClassTypeScanner.from(resolvedBasePackages).with(resolveBeanClassLoader())
.withExcludes(resolveExcludes(enableEntityDefinedRegionsAttributes))
.withIncludes(resolveIncludes(enableEntityDefinedRegionsAttributes))
.withIncludes(regionAnnotatedPersistentEntityTypeFilters());
}
/* (non-Javadoc) */
protected Set<String> resolveBasePackages(AnnotationMetadata importingClassMetaData,
AnnotationAttributes enableEntityDefinedRegionAttributes) {
Set<String> resolvedBasePackages = new HashSet<>();
Collections.addAll(resolvedBasePackages, ArrayUtils.nullSafeArray(defaultIfEmpty(
enableEntityDefinedRegionAttributes.getStringArray("basePackages"),
enableEntityDefinedRegionAttributes.getStringArray("value")), String.class));
for (Class<?> type : ArrayUtils.nullSafeArray(
enableEntityDefinedRegionAttributes.getClassArray("basePackageClasses"), Class.class)) {
resolvedBasePackages.add(type.getPackage().getName());
}
if (resolvedBasePackages.isEmpty()) {
resolvedBasePackages.add(ClassUtils.getPackageName(importingClassMetaData.getClassName()));
}
return resolvedBasePackages;
}
/* (non-Javadoc) */
protected ClassLoader resolveBeanClassLoader() {
return (this.beanClassLoader != null ? this.beanClassLoader : Thread.currentThread().getContextClassLoader());
}
/* (non-Javadoc) */
protected Iterable<TypeFilter> resolveExcludes(AnnotationAttributes enableEntityDefinedRegionsAttributes) {
return parseFilters(enableEntityDefinedRegionsAttributes.getAnnotationArray("excludeFilters"));
}
/* (non-Javadoc) */
protected Iterable<TypeFilter> resolveIncludes(AnnotationAttributes enableEntityDefinedRegionsAttributes) {
return parseFilters(enableEntityDefinedRegionsAttributes.getAnnotationArray("includeFilters"));
}
/* (non-Javadoc) */
private Iterable<TypeFilter> parseFilters(AnnotationAttributes[] componentScanFilterAttributes) {
Set<TypeFilter> typeFilters = new HashSet<>();
for (AnnotationAttributes filterAttributes : ArrayUtils.nullSafeArray(
componentScanFilterAttributes, AnnotationAttributes.class)) {
CollectionUtils.addAll(typeFilters, typeFiltersFor(filterAttributes));
}
return typeFilters;
}
/* (non-Javadoc) */
@SuppressWarnings("unchecked")
private Iterable<TypeFilter> typeFiltersFor(AnnotationAttributes filterAttributes) {
Set<TypeFilter> typeFilters = new HashSet<>();
FilterType filterType = filterAttributes.getEnum("type");
for (Class<?> filterClass : ArrayUtils.nullSafeArray(filterAttributes.getClassArray("value"), Class.class)) {
switch (filterType) {
case ANNOTATION:
Assert.isAssignable(Annotation.class, filterClass,
String.format("@ComponentScan.Filter class [%s] must be an Annotation", filterClass));
typeFilters.add(new AnnotationTypeFilter((Class<Annotation>) filterClass));
break;
case ASSIGNABLE_TYPE:
typeFilters.add(new AssignableTypeFilter(filterClass));
break;
case CUSTOM:
Assert.isAssignable(TypeFilter.class, filterClass,
String.format("@ComponentScan.Filter class [%s] must be a TypeFilter", filterClass));
typeFilters.add(BeanUtils.instantiateClass(filterClass, TypeFilter.class));
break;
default:
throw new IllegalArgumentException(String.format(
"Illegal filter type [%s] when 'value' or 'classes' are specified", filterType));
}
for (String pattern : nullSafeGetPatterns(filterAttributes)) {
switch (filterType) {
case ASPECTJ:
typeFilters.add(new AspectJTypeFilter(pattern, resolveBeanClassLoader()));
break;
case REGEX:
typeFilters.add(new RegexPatternTypeFilter(Pattern.compile(pattern)));
break;
default:
throw new IllegalArgumentException(String.format(
"Illegal filter type [%s] when 'patterns' are specified", filterType));
}
}
}
return typeFilters;
}
/**
* Safely reads the {@code pattern} attribute from the given {@link AnnotationAttributes}
* and returns an empty array if the attribute is not present.
*
* @param filterAttributes {@link AnnotationAttributes} from which to extract the {@code pattern} attribute value.
* @return a {@link String} array.
*/
private String[] nullSafeGetPatterns(AnnotationAttributes filterAttributes) {
try {
return ArrayUtils.nullSafeArray(filterAttributes.getStringArray("pattern"), String.class);
}
catch (IllegalArgumentException ignore) {
return new String[0];
}
}
/* (non-Javadoc) */
@SuppressWarnings("unchecked")
protected Iterable<TypeFilter> regionAnnotatedPersistentEntityTypeFilters() {
Set<TypeFilter> regionAnnotatedPersistentEntityTypeFilters = new HashSet<>();
for (Class<? extends Annotation> annotationType :
org.springframework.data.gemfire.mapping.Region.REGION_ANNOTATION_TYPES) {
regionAnnotatedPersistentEntityTypeFilters.add(new AnnotationTypeFilter(annotationType));
}
return regionAnnotatedPersistentEntityTypeFilters;
}
/* (non-Javadoc) */
protected void registerRegionBeanDefinition(GemfirePersistentEntity persistentEntity, boolean strict,
BeanDefinitionRegistry registry) {
BeanDefinitionBuilder regionFactoryBeanBuilder =
BeanDefinitionBuilder.genericBeanDefinition(resolveRegionFactoryBeanClass(persistentEntity))
.addPropertyReference("cache", GemfireConstants.DEFAULT_GEMFIRE_CACHE_NAME)
.addPropertyValue("close", false);
setRegionAttributes(persistentEntity, regionFactoryBeanBuilder, strict);
registry.registerBeanDefinition(persistentEntity.getRegionName(),
regionFactoryBeanBuilder.getBeanDefinition());
}
/* (non-Javadoc) */
@SuppressWarnings("unchecked")
protected Class<? extends RegionLookupFactoryBean> resolveRegionFactoryBeanClass(
GemfirePersistentEntity persistentEntity) {
return defaultIfNull(regionAnnotationToRegionFactoryBeanClass.get(persistentEntity.getRegionAnnotationType()),
DEFAULT_REGION_FACTORY_BEAN_CLASS);
}
/* (non-Javadoc) */
protected BeanDefinitionBuilder setRegionAttributes(GemfirePersistentEntity persistentEntity,
BeanDefinitionBuilder regionFactoryBeanBuilder, boolean strict) {
Annotation regionAnnotation = persistentEntity.getRegionAnnotation();
if (regionAnnotation != null) {
AnnotationAttributes regionAnnotationAttributes =
AnnotationAttributes.fromMap(AnnotationUtils.getAnnotationAttributes(regionAnnotation));
if (strict) {
regionFactoryBeanBuilder.addPropertyValue("keyConstraint", resolveIdType(persistentEntity));
regionFactoryBeanBuilder.addPropertyValue("valueConstraint", resolveDomainType(persistentEntity));
}
if (regionAnnotationAttributes.containsKey("diskStoreName")) {
String diskStoreName = regionAnnotationAttributes.getString("diskStoreName");
setPropertyValueIfNotDefault(regionFactoryBeanBuilder, "diskStoreName", diskStoreName, "");
if (StringUtils.hasText(diskStoreName)) {
regionFactoryBeanBuilder.addDependsOn(diskStoreName);
}
}
if (regionAnnotationAttributes.containsKey("persistent")) {
setPropertyValueIfNotDefault(regionFactoryBeanBuilder, "persistent",
regionAnnotationAttributes.getBoolean("persistent"), false);
}
BeanDefinitionBuilder regionAttributesFactoryBeanBuilder =
resolveRegionAttributesFactoryBeanBuilder(regionAnnotation, regionFactoryBeanBuilder);
if (regionAnnotationAttributes.containsKey("diskSynchronous")) {
setPropertyValueIfNotDefault(regionAttributesFactoryBeanBuilder, "diskSynchronous",
regionAnnotationAttributes.getBoolean("diskSynchronous"), true);
}
if (regionAnnotationAttributes.containsKey("ignoreJta")) {
setPropertyValueIfNotDefault(regionAttributesFactoryBeanBuilder, "ignoreJTA",
regionAnnotationAttributes.getBoolean("ignoreJta"), false);
}
setClientRegionAttributes(regionAnnotationAttributes, regionFactoryBeanBuilder);
setPartitionRegionAttributes(regionAnnotationAttributes, regionFactoryBeanBuilder,
regionAttributesFactoryBeanBuilder);
setReplicateRegionAttributes(regionAnnotationAttributes, regionFactoryBeanBuilder);
}
return regionFactoryBeanBuilder;
}
/* (non-Javadoc) */
protected Class<?> resolveDomainType(GemfirePersistentEntity persistentEntity) {
return defaultIfNull(persistentEntity.getType(), Object.class);
}
/* (non-Javadoc) */
protected Class<?> resolveIdType(GemfirePersistentEntity persistentEntity) {
return (persistentEntity.hasIdProperty() ?
defaultIfNull(persistentEntity.getIdProperty().getActualType(), Object.class) : Object.class);
}
/* (non-Javadoc) */
protected BeanDefinitionBuilder resolveRegionAttributesFactoryBeanBuilder(Annotation regionAnnotation,
BeanDefinitionBuilder regionFactoryBeanBuilder) {
BeanDefinitionBuilder regionAttributesFactoryBeanBuilder = regionFactoryBeanBuilder;
if (!ClientRegion.class.isAssignableFrom(regionAnnotation.annotationType())) {
regionAttributesFactoryBeanBuilder = BeanDefinitionBuilder.genericBeanDefinition(
RegionAttributesFactoryBean.class);
regionFactoryBeanBuilder.addPropertyValue("attributes",
regionAttributesFactoryBeanBuilder.getBeanDefinition());
}
return regionAttributesFactoryBeanBuilder;
}
/* (non-Javadoc) */
protected BeanDefinitionBuilder setClientRegionAttributes(AnnotationAttributes regionAnnotationAttributes,
BeanDefinitionBuilder regionFactoryBeanBuilder) {
if (regionAnnotationAttributes.containsKey("poolName")) {
setPropertyValueIfNotDefault(regionFactoryBeanBuilder, "poolName",
regionAnnotationAttributes.getString("poolName"), null);
}
if (regionAnnotationAttributes.containsKey("shortcut")) {
setPropertyValueIfNotDefault(regionFactoryBeanBuilder, "shortcut",
regionAnnotationAttributes.getEnum("shortcut"), ClientRegionShortcut.PROXY);
}
return regionFactoryBeanBuilder;
}
/* (non-Javadoc) */
protected BeanDefinitionBuilder setPartitionRegionAttributes(AnnotationAttributes regionAnnotationAttributes,
BeanDefinitionBuilder regionFactoryBeanBuilder, BeanDefinitionBuilder regionAttributesFactoryBeanBuilder) {
if (regionAnnotationAttributes.containsKey("redundantCopies")) {
BeanDefinitionBuilder partitionAttributesFactoryBeanBuilder =
BeanDefinitionBuilder.genericBeanDefinition(PartitionAttributesFactoryBean.class);
String collocatedWith = regionAnnotationAttributes.getString("collocatedWith");
setPropertyValueIfNotDefault(partitionAttributesFactoryBeanBuilder, "colocatedWith", collocatedWith, "");
if (StringUtils.hasText(collocatedWith)) {
regionFactoryBeanBuilder.addDependsOn(collocatedWith);
}
setPropertyReferenceIfSet(partitionAttributesFactoryBeanBuilder, "partitionResolver",
regionAnnotationAttributes.getString("partitionResolverName"));
setPropertyValueIfNotDefault(partitionAttributesFactoryBeanBuilder, "redundantCopies",
regionAnnotationAttributes.<Integer>getNumber("redundantCopies"), 0);
setFixedPartitionRegionAttributes(regionAnnotationAttributes, partitionAttributesFactoryBeanBuilder);
regionAttributesFactoryBeanBuilder.addPropertyValue("partitionAttributes",
partitionAttributesFactoryBeanBuilder.getBeanDefinition());
}
return regionAttributesFactoryBeanBuilder;
}
/* (non-Javadoc) */
protected BeanDefinitionBuilder setFixedPartitionRegionAttributes(AnnotationAttributes regionAnnotationAttributes,
BeanDefinitionBuilder partitionAttributesFactoryBeanBuilder) {
PartitionRegion.FixedPartition[] fixedPartitions = ArrayUtils.nullSafeArray(
regionAnnotationAttributes.getAnnotationArray("fixedPartitions", PartitionRegion.FixedPartition.class),
PartitionRegion.FixedPartition.class);
if (!ObjectUtils.isEmpty(fixedPartitions)) {
ManagedList<BeanDefinition> fixedPartitionAttributesFactoryBeans =
new ManagedList<BeanDefinition>(fixedPartitions.length);
for (PartitionRegion.FixedPartition fixedPartition : fixedPartitions) {
BeanDefinitionBuilder fixedPartitionAttributesFactoryBeanBuilder =
BeanDefinitionBuilder.genericBeanDefinition(FixedPartitionAttributesFactoryBean.class);
fixedPartitionAttributesFactoryBeanBuilder.addPropertyValue("partitionName", fixedPartition.name());
setPropertyValueIfNotDefault(fixedPartitionAttributesFactoryBeanBuilder, "primary",
fixedPartition.primary(), false);
setPropertyValueIfNotDefault(fixedPartitionAttributesFactoryBeanBuilder, "numBuckets",
fixedPartition.numBuckets(), 1);
fixedPartitionAttributesFactoryBeans.add(
fixedPartitionAttributesFactoryBeanBuilder.getBeanDefinition());
}
partitionAttributesFactoryBeanBuilder.addPropertyValue("fixedPartitionAttributes",
fixedPartitionAttributesFactoryBeans);
}
return partitionAttributesFactoryBeanBuilder;
}
/* (non-Javadoc) */
protected BeanDefinitionBuilder setReplicateRegionAttributes(AnnotationAttributes regionAnnotationAttributes,
BeanDefinitionBuilder regionFactoryBeanBuilder) {
if (regionAnnotationAttributes.containsKey("scope")) {
setPropertyValueIfNotDefault(regionFactoryBeanBuilder, "scope",
regionAnnotationAttributes.<ScopeType>getEnum("scope").getScope(), ScopeType.DISTRIBUTED_NO_ACK);
}
return regionFactoryBeanBuilder;
}
/* (non-Javadoc) */
private <T> BeanDefinitionBuilder setPropertyReferenceIfSet(BeanDefinitionBuilder beanDefinitionBuilder,
String propertyName, String beanName) {
return (StringUtils.hasText(beanName) ? beanDefinitionBuilder.addPropertyReference(propertyName, beanName)
: beanDefinitionBuilder);
}
/* (non-Javadoc) */
private <T> BeanDefinitionBuilder setPropertyValueIfNotDefault(BeanDefinitionBuilder beanDefinitionBuilder,
String propertyName, T value, T defaultValue) {
return (value != null && !value.equals(defaultValue) ?
beanDefinitionBuilder.addPropertyValue(propertyName, value) : beanDefinitionBuilder);
}
/**
* Performs addition post processing on the {@link GemfirePersistentEntity} to offer additional feature support
* (e.g. dynamic Index creation).
*
* @param persistentEntity {@link GemfirePersistentEntity} to process.
* @return the given {@link GemfirePersistentEntity}.
* @see org.springframework.data.gemfire.mapping.GemfirePersistentEntity
*/
protected GemfirePersistentEntity<?> postProcess(GemfirePersistentEntity<?> persistentEntity) {
return persistentEntity;
}
}

View File

@@ -0,0 +1,224 @@
/*
* Copyright 2016 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.support;
import static org.apache.geode.internal.lang.ObjectUtils.defaultIfNull;
import static org.springframework.data.gemfire.util.SpringUtils.defaultIfEmpty;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionAttributes;
import org.apache.geode.cache.RegionShortcut;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.data.gemfire.GemfireUtils;
import org.springframework.data.gemfire.GenericRegionFactoryBean;
import org.springframework.data.gemfire.RegionLookupFactoryBean;
import org.springframework.data.gemfire.client.ClientRegionFactoryBean;
import org.springframework.data.gemfire.config.xml.GemfireConstants;
import org.springframework.util.Assert;
/**
* The {@link GemFireCacheTypeAwareRegionFactoryBean} class is a smart Spring {@link FactoryBean} that knows how to
* create a client or server {@link Region} depending on whether the {@link GemFireCache} is
* a {@link org.apache.geode.cache.client.ClientCache} or a peer {@link org.apache.geode.cache.Cache}.
*
* @author John Blum
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.beans.factory.BeanFactoryAware
* @see org.springframework.beans.factory.FactoryBean
* @see org.springframework.data.gemfire.RegionLookupFactoryBean
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.Region
* @since 1.9.0
*/
@SuppressWarnings("unused")
public class GemFireCacheTypeAwareRegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V>
implements BeanFactoryAware {
private GemFireCache gemfireCache;
private BeanFactory beanFactory;
private Boolean close = false;
private Class<K> keyConstraint;
private Class<V> valueConstraint;
private ClientRegionShortcut clientRegionShortcut = ClientRegionShortcut.PROXY;
private DataPolicy dataPolicy = DataPolicy.DEFAULT;
private RegionAttributes<K, V> regionAttributes;
private RegionShortcut serverRegionShortcut;
private String poolName;
private String regionName;
/**
* @inheritDoc
*/
@Override
public Region<K, V> lookupRegion(GemFireCache gemfireCache, String regionName) throws Exception {
return (GemfireUtils.isClient(gemfireCache) ? newClientRegion(gemfireCache, regionName)
: newServerRegion(gemfireCache, regionName));
}
/**
* Constructs a new client {@link Region} using the {@link ClientRegionFactoryBean}.
*
* @param gemfireCache reference to the {@link GemFireCache} used to create/initialize the factory
* used to create the client {@link Region}.
* @param regionName name given to the client {@link Region}.
* @return a new instance of a client {@link Region} with the given {@code regionName}.
* @throws Exception if the client {@link Region} could not be created.
* @see org.springframework.data.gemfire.client.ClientRegionFactoryBean
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.Region
*/
protected Region<K, V> newClientRegion(GemFireCache gemfireCache, String regionName) throws Exception {
ClientRegionFactoryBean<K, V> clientRegion = new ClientRegionFactoryBean<K, V>();
clientRegion.setAttributes(getRegionAttributes());
clientRegion.setBeanFactory(getBeanFactory());
clientRegion.setCache(gemfireCache);
clientRegion.setClose(isClose());
clientRegion.setKeyConstraint(getKeyConstraint());
clientRegion.setPoolName(getPoolName());
clientRegion.setRegionName(regionName);
clientRegion.setShortcut(getClientRegionShortcut());
clientRegion.setValueConstraint(getValueConstraint());
clientRegion.afterPropertiesSet();
return clientRegion.getObject();
}
/**
* Constructs a new server {@link Region} using the {@link GenericRegionFactoryBean}.
*
* @param gemfireCache reference to the {@link GemFireCache} used to create/initialize the factory
* used to create the server {@link Region}.
* @param regionName name given to the server {@link Region}.
* @return a new instance of a server {@link Region} with the given {@code regionName}.
* @throws Exception if the server {@link Region} could not be created.
* @see org.springframework.data.gemfire.GenericRegionFactoryBean
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.Region
*/
protected Region<K, V> newServerRegion(GemFireCache gemfireCache, String regionName) throws Exception {
GenericRegionFactoryBean<K, V> serverRegion = new GenericRegionFactoryBean<K, V>();
serverRegion.setAttributes(getRegionAttributes());
serverRegion.setCache(gemfireCache);
serverRegion.setClose(isClose());
serverRegion.setDataPolicy(getDataPolicy());
serverRegion.setKeyConstraint(getKeyConstraint());
serverRegion.setRegionName(regionName);
serverRegion.setShortcut(getServerRegionShortcut());
serverRegion.setValueConstraint(getValueConstraint());
serverRegion.afterPropertiesSet();
return serverRegion.getObject();
}
public void setAttributes(RegionAttributes<K, V> regionAttributes) {
this.regionAttributes = regionAttributes;
}
protected RegionAttributes<K, V> getRegionAttributes() {
return this.regionAttributes;
}
/**
* @inheritDoc
*/
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
protected BeanFactory getBeanFactory() {
Assert.state(this.beanFactory != null, "BeanFactory was not properly initialized");
return this.beanFactory;
}
public void setClientRegionShortcut(ClientRegionShortcut clientRegionShortcut) {
this.clientRegionShortcut = clientRegionShortcut;
}
protected ClientRegionShortcut getClientRegionShortcut() {
return defaultIfNull(this.clientRegionShortcut, ClientRegionShortcut.PROXY);
}
public void setClose(Boolean close) {
this.close = close;
}
protected Boolean getClose() {
return this.close;
}
protected boolean isClose() {
return Boolean.TRUE.equals(getClose());
}
public void setDataPolicy(DataPolicy dataPolicy) {
this.dataPolicy = dataPolicy;
}
protected DataPolicy getDataPolicy() {
return defaultIfNull(this.dataPolicy, DataPolicy.DEFAULT);
}
public void setKeyConstraint(Class<K> keyConstraint) {
this.keyConstraint = keyConstraint;
}
protected Class<K> getKeyConstraint() {
return this.keyConstraint;
}
public void setPoolName(String poolName) {
this.poolName = poolName;
}
protected String getPoolName() {
return defaultIfEmpty(this.poolName, GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME);
}
public void setServerRegionShortcut(RegionShortcut shortcut) {
this.serverRegionShortcut = shortcut;
}
protected RegionShortcut getServerRegionShortcut() {
return this.serverRegionShortcut;
}
public void setValueConstraint(Class<V> valueConstraint) {
this.valueConstraint = valueConstraint;
}
protected Class<V> getValueConstraint() {
return this.valueConstraint;
}
}

View File

@@ -0,0 +1,298 @@
/*
* Copyright 2016 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.support;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.env.Environment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.data.gemfire.util.ArrayUtils;
import org.springframework.data.gemfire.util.CollectionUtils;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* The {@link GemFireComponentClassTypeScanner} class is a classpath component scanner used to search
* for GemFire components based on {@link Class} type.
*
* @author John Blum
* @see java.lang.Iterable
* @see org.springframework.context.ConfigurableApplicationContext
* @see org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider
* @see org.springframework.core.env.Environment
* @since 1.9.0
*/
@SuppressWarnings("unused")
public class GemFireComponentClassTypeScanner implements Iterable<String> {
/**
* Factory method to construct an instance of the {@link GemFireComponentClassTypeScanner} initialized with
* the given array of base packages to scan.
*
* @param basePackages array of base packages to scan for GemFire components.
* @throws IllegalArgumentException if the array of base packages is {@literal null} or empty.
* @return an initialized instance of {@link GemFireComponentClassTypeScanner}.
* @see #GemFireComponentClassTypeScanner(Set)
*/
public static GemFireComponentClassTypeScanner from(String... basePackages) {
return new GemFireComponentClassTypeScanner(CollectionUtils.asSet(
ArrayUtils.nullSafeArray(basePackages, String.class)));
}
/**
* Factory method to construct an instance of the {@link GemFireComponentClassTypeScanner} initialized with
* the given {@link Iterable} of base packages to scan.
*
* @param basePackages {@link Iterable} of base packages to scan for GemFire components.
* @throws IllegalArgumentException if the {@link Iterable} of base packages is {@literal null} or empty.
* @return an initialized instance of {@link GemFireComponentClassTypeScanner}.
* @see #GemFireComponentClassTypeScanner(Set)
*/
public static GemFireComponentClassTypeScanner from(Iterable<String> basePackages) {
Set<String> basePackageSet = new HashSet<String>();
for (String basePackage : CollectionUtils.nullSafeIterable(basePackages)) {
basePackageSet.add(basePackage);
}
return new GemFireComponentClassTypeScanner(basePackageSet);
}
private ClassLoader entityClassLoader;
private ConfigurableApplicationContext applicationContext;
private Set<TypeFilter> excludes = new HashSet<TypeFilter>();
private Set<TypeFilter> includes = new HashSet<TypeFilter>();
protected final Log log = LogFactory.getLog(getClass());
private final Set<String> basePackages;
/**
* Constructs an instance of the {@link GemFireComponentClassTypeScanner} initialized with
* the given {@link Set} of base packages to scan.
*
* @param basePackages {@link Set} of base packages to scan for GemFire component clases.
* @throws IllegalArgumentException if the {@link Set} is {@literal null} or empty.
* @see java.util.Set
*/
protected GemFireComponentClassTypeScanner(Set<String> basePackages) {
Assert.notEmpty(basePackages, "Base packages must be specified");
this.basePackages = basePackages;
}
/**
* Returns a reference to the Spring {@link org.springframework.context.ApplicationContext}.
*
* @return a reference to the Spring {@link org.springframework.context.ApplicationContext}.
* @see org.springframework.context.ConfigurableApplicationContext
*/
protected ConfigurableApplicationContext getApplicationContext() {
return this.applicationContext;
}
/**
* Returns an unmodifiable {@link Set} of base packages to scan for GemFire components.
*
* @return an unmodifiable {@link Set} of base packages to scan for GemFire components.
*/
protected Set<String> getBasePackages() {
return Collections.unmodifiableSet(this.basePackages);
}
/**
* Returns a reference to the {@link ClassLoader} to find and load GemFire application persistent entity classes.
*
* @return a {@link ClassLoader} to find and load GemFire application persistent entity classes.
* @see org.springframework.beans.factory.config.ConfigurableBeanFactory#getBeanClassLoader()
* @see java.lang.Thread#getContextClassLoader()
* @see java.lang.ClassLoader
* @see #getApplicationContext()
*/
protected ClassLoader getEntityClassLoader() {
ConfigurableApplicationContext applicationContext = getApplicationContext();
return (this.entityClassLoader != null ? this.entityClassLoader
: (applicationContext != null ? applicationContext.getBeanFactory().getBeanClassLoader()
: Thread.currentThread().getContextClassLoader()));
}
/**
* Returns a reference to the Spring {@link Environment} in which the Spring GemFire application is running.
*
* @return a reference to the Spring {@link Environment}.
* @see org.springframework.context.ApplicationContext#getEnvironment()
* @see org.springframework.core.env.Environment
* @see org.springframework.core.env.StandardEnvironment
* @see #getApplicationContext()
*/
protected Environment getEnvironment() {
ConfigurableApplicationContext applicationContext = getApplicationContext();
return (applicationContext != null ? applicationContext.getEnvironment() : new StandardEnvironment());
}
/**
* Returns a collection of {@link TypeFilter TypeFilters} used to exclude types found
* during the classpath component scan.
*
* @return a collection of {@link TypeFilter} objects
* @see org.springframework.core.type.filter.TypeFilter
*/
protected Iterable<TypeFilter> getExcludes() {
return this.excludes;
}
/**
* Returns a collection of {@link TypeFilter TypeFilters} used to include (match) types found
* during the classpath component scan.
*
* @return a collection of {@link TypeFilter} objects
* @see org.springframework.core.type.filter.TypeFilter
*/
protected Iterable<TypeFilter> getIncludes() {
return this.includes;
}
/**
* @inheritDoc
*/
@Override
public Iterator<String> iterator() {
return getBasePackages().iterator();
}
/**
* Scans the {@link Set} of base packages searching for GemFire application components accepted by the filters
* of this scanner.
*
* @return a {@link Set} of GemFire application component {@link Class} types.
* @see #newClassPathScanningCandidateComponentProvider(boolean)
* @see java.util.Set
*/
public Set<Class<?>> scan() {
Set<Class<?>> componentClasses = new HashSet<Class<?>>();
ClassLoader entityClassLoader = getEntityClassLoader();
ClassPathScanningCandidateComponentProvider componentProvider =
newClassPathScanningCandidateComponentProvider();
for (String packageName : this) {
for (BeanDefinition beanDefinition : componentProvider.findCandidateComponents(packageName)) {
try {
componentClasses.add(ClassUtils.forName(beanDefinition.getBeanClassName(), entityClassLoader));
}
catch (ClassNotFoundException ignore) {
log.warn(String.format("Class not found for component type [%s]",
beanDefinition.getBeanClassName()));
}
}
}
return componentClasses;
}
/**
* Constructs a new instance of the {@link ClassPathScanningCandidateComponentProvider} initialized with
* no default filters.
*
* @return a new instance of the {@link ClassPathScanningCandidateComponentProvider}.
* @see org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider
* @see #newClassPathScanningCandidateComponentProvider(boolean)
*/
protected ClassPathScanningCandidateComponentProvider newClassPathScanningCandidateComponentProvider() {
return newClassPathScanningCandidateComponentProvider(false);
}
/**
* Constructs a new instance of the {@link ClassPathScanningCandidateComponentProvider} initialized with
* the {@code useDefaultFilters} boolean value to indicate whether to use default values or not. Additionally,
* the exclude/include filters are also set.
*
* @param useDefaultFilters boolean value to indicate whether to use default filters.
* @return a new instance of the {@link ClassPathScanningCandidateComponentProvider}.
* @see org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider
* @see #newClassPathScanningCandidateComponentProvider(boolean)
*/
protected ClassPathScanningCandidateComponentProvider newClassPathScanningCandidateComponentProvider(
boolean useDefaultFilters) {
ClassPathScanningCandidateComponentProvider componentProvider =
new ClassPathScanningCandidateComponentProvider(useDefaultFilters, getEnvironment());
for (TypeFilter exclude : excludes) {
componentProvider.addExcludeFilter(exclude);
}
for (TypeFilter include : includes) {
componentProvider.addIncludeFilter(include);
}
return componentProvider;
}
/* (non-Javadoc) */
public GemFireComponentClassTypeScanner with(ClassLoader entityClassLoader) {
this.entityClassLoader = entityClassLoader;
return this;
}
/* (non-Javadoc) */
public GemFireComponentClassTypeScanner with(ConfigurableApplicationContext applicationContext) {
this.applicationContext = applicationContext;
return this;
}
/* (non-Javadoc) */
public GemFireComponentClassTypeScanner withExcludes(TypeFilter... excludes) {
return withExcludes(CollectionUtils.asSet(ArrayUtils.nullSafeArray(excludes, TypeFilter.class)));
}
/* (non-Javadoc) */
public GemFireComponentClassTypeScanner withExcludes(Iterable<TypeFilter> excludes) {
for (TypeFilter exclude : CollectionUtils.nullSafeIterable(excludes)) {
this.excludes.add(exclude);
}
return this;
}
/* (non-Javadoc) */
public GemFireComponentClassTypeScanner withIncludes(TypeFilter... includes) {
return withIncludes(CollectionUtils.asSet(ArrayUtils.nullSafeArray(includes, TypeFilter.class)));
}
/* (non-Javadoc) */
public GemFireComponentClassTypeScanner withIncludes(Iterable<TypeFilter> includes) {
for (TypeFilter include : CollectionUtils.nullSafeIterable(includes)) {
this.includes.add(include);
}
return this;
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright 2016 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.mapping;
import java.lang.annotation.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.client.ClientRegionShortcut;
import org.apache.geode.cache.client.Pool;
import org.springframework.core.annotation.AliasFor;
import org.springframework.data.gemfire.config.xml.GemfireConstants;
/**
* {@link Annotation} defining the Client {@link org.apache.geode.cache.Region} in which the application
* persistent entity will be stored.
*
* @author John Blum
* @see org.springframework.data.gemfire.mapping.Region
* @since 1.9.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Region
@SuppressWarnings("unused")
public @interface ClientRegion {
/**
* Name, or fully-qualified bean name of the {@link org.apache.geode.cache.Region}
* in which the application persistent entity will be stored (e.g. "Users", or "/Local/Admin/Users").
*
* Defaults to simple name of the application persistent entity defined by {@link java.lang.Class#getSimpleName()}.
*
* @return the name or fully-qualified path of the {@link Region} in which the application persistent entity
* will be stored.
*/
@AliasFor(annotation = Region.class, attribute = "name")
String name() default "";
/**
* Name, or fully-qualified bean name of the {@link org.apache.geode.cache.Region}
* in which the application persistent entity will be stored (e.g. "Users", or "/Local/Admin/Users").
*
* Defaults to simple name of the application persistent entity defined by {@link java.lang.Class#getSimpleName()}.
*
* @return the name or fully-qualified path of the {@link Region} in which the application persistent entity
* will be stored.
*/
@AliasFor(annotation = Region.class, attribute = "value")
String value() default "";
/**
* Name of the {@link org.apache.geode.cache.DiskStore} in which this persistent entity's data is overflowed
* and/or persisted.
*
* Maybe the name of a Spring bean defined in the Spring context.
*
* Defaults to unset.
*/
String diskStoreName() default "";
/**
* Determines whether disk-based operations (used in overflow and persistent) are synchronous or asynchronous.
*
* Defaults to {@literal synchronous}.
*/
boolean diskSynchronous() default true;
/**
* Name of the GemFire/Geode {@link Pool} used by this persistent entity's {@link org.apache.geode.cache.Region}
* data access operations sent to the corresponding {@link org.apache.geode.cache.Region}
* on the GemFire/Geode Server.
*
* Defaults to {@literal gemfirePool}.
*/
String poolName() default GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME;
/**
* {@link ClientRegionShortcut} used by this persistent entity's client {@link org.apache.geode.cache.Region}
* to define the {@link org.apache.geode.cache.DataPolicy}.
*
* Defaults to {@link ClientRegionShortcut#PROXY}.
*
* @see org.apache.geode.cache.client.ClientRegionShortcut
*/
ClientRegionShortcut shortcut() default ClientRegionShortcut.PROXY;
}

View File

@@ -16,42 +16,109 @@
package org.springframework.data.gemfire.mapping;
import static org.springframework.data.gemfire.util.SpringUtils.defaultIfEmpty;
import java.lang.annotation.Annotation;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.model.BasicPersistentEntity;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.StringUtils;
/**
* {@link PersistentEntity} implementation adding custom Gemfire related metadata, such as the region the entity is
* mapped to etc.
*
* {@link PersistentEntity} implementation adding custom GemFire persistent entity related metadata, such as the
* {@link org.apache.geode.cache.Region} to which the entity is mapped, etc.
*
* @author Oliver Gierke
* @author John Blum
* @see org.springframework.data.gemfire.mapping.GemfirePersistentProperty
* @see org.springframework.data.mapping.model.BasicPersistentEntity
*/
@SuppressWarnings("unused")
public class GemfirePersistentEntity<T> extends BasicPersistentEntity<T, GemfirePersistentProperty> {
private final Annotation regionAnnotation;
private final String regionName;
/**
* Creates a new {@link GemfirePersistentEntity} for the given {@link TypeInformation}.
*
* @param information must not be {@literal null}.
*/
public GemfirePersistentEntity(TypeInformation<T> information) {
/* (non-Javadoc) */
protected static Annotation resolveRegionAnnotation(Class<?> persistentEntityType) {
super(information);
for (Class<? extends Annotation> regionAnnotationType : Region.REGION_ANNOTATION_TYPES) {
Annotation regionAnnotation = AnnotatedElementUtils.getMergedAnnotation(
persistentEntityType, regionAnnotationType);
Class<T> rawType = information.getType();
Region region = rawType.getAnnotation(Region.class);
String defaultRegionName = rawType.getSimpleName();
if (regionAnnotation != null) {
return regionAnnotation;
}
}
this.regionName = (region != null && StringUtils.hasText(region.value()) ? region.value() : defaultRegionName);
return null;
}
/* (non-Javadoc) */
protected static String resolveRegionName(Class<?> persistentEntityType, Annotation regionAnnotation) {
String regionName = (regionAnnotation != null ? AnnotationAttributes.fromMap(
AnnotationUtils.getAnnotationAttributes(regionAnnotation)).getString("value") : null);
return defaultIfEmpty(regionName, persistentEntityType.getSimpleName());
}
/**
* Returns the name of the region the entity shall be stored in.
*
* @return the name of the region the entity shall be stored in.
* Creates a new {@link GemfirePersistentEntity} for the given {@link TypeInformation}.
*
* @param information must not be {@literal null}.
*/
public GemfirePersistentEntity(TypeInformation<T> information) {
super(information);
Class<T> rawType = information.getType();
this.regionAnnotation = resolveRegionAnnotation(rawType);
this.regionName = resolveRegionName(rawType, this.regionAnnotation);
}
/**
* Returns the {@link Region} annotation used to annotate this {@link PersistentEntity} or {@literal null}
* if this {@link PersistentEntity} was not annotated with a {@link Region} annotation.
*
* @param <T> concrete {@link Class} type of the Region {@link Annotation}.
* @return the {@link Region} annotation used to annotate this {@link PersistentEntity} or {@literal null}
* if this {@link PersistentEntity} was not annotated with a {@link Region} annotation.
* @see org.springframework.data.gemfire.mapping.ClientRegion
* @see org.springframework.data.gemfire.mapping.LocalRegion
* @see org.springframework.data.gemfire.mapping.PartitionRegion
* @see org.springframework.data.gemfire.mapping.ReplicateRegion
* @see org.springframework.data.gemfire.mapping.Region
* @see java.lang.annotation.Annotation
*/
@SuppressWarnings("unchecked")
public <T extends Annotation> T getRegionAnnotation() {
return (T) this.regionAnnotation;
}
/**
* Returns the {@link Class} type of the Region {@link Annotation} or {@literal null}
* if this {@link PersistentEntity} was not annotated with a Region {@link Annotation}.
*
* @return the {@link Class} type of the Region {@link Annotation} or {@literal null}
* if this {@link PersistentEntity} was not annotated with a Region {@link Annotation}.
* @see java.lang.annotation.Annotation#annotationType()
* @see #getRegionAnnotation()
*/
public Class<? extends Annotation> getRegionAnnotationType() {
Annotation regionAnnotation = getRegionAnnotation();
return (regionAnnotation != null ? regionAnnotation.annotationType() : null);
}
/**
* Returns the name of the {@link Region} in which this {@link PersistentEntity} will be stored.
*
* @return the name of the {@link Region} in which this {@link PersistentEntity} will be stored.
* @see org.apache.geode.cache.Region#getName()
*/
public String getRegionName() {
return this.regionName;

View File

@@ -0,0 +1,111 @@
/*
* Copyright 2016 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.mapping;
import java.lang.annotation.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.springframework.core.annotation.AliasFor;
/**
* {@link Annotation} defining the Local {@link Region} in which the application persistent entity will be stored.
*
* @author John Blum
* @see org.springframework.data.gemfire.mapping.Region
* @since 1.9.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Region
@SuppressWarnings("unused")
public @interface LocalRegion {
/**
* Name, or fully-qualified bean name of the {@link org.apache.geode.cache.Region}
* in which the application persistent entity will be stored (e.g. "Users", or "/Local/Admin/Users").
*
* Defaults to simple name of the application persistent entity defined by {@link java.lang.Class#getSimpleName()}.
*
* @return the name or fully-qualified path of the {@link Region} in which the application persistent entity
* will be stored.
*/
@AliasFor(annotation = Region.class, attribute = "name")
String name() default "";
/**
* Name, or fully-qualified bean name of the {@link org.apache.geode.cache.Region}
* in which the application persistent entity will be stored (e.g. "Users", or "/Local/Admin/Users").
*
* Defaults to simple name of the application persistent entity defined by {@link java.lang.Class#getSimpleName()}.
*
* @return the name or fully-qualified path of the {@link Region} in which the application persistent entity
* will be stored.
*/
@AliasFor(annotation = Region.class, attribute = "value")
String value() default "";
/**
* Name of the {@link org.apache.geode.cache.DiskStore} in which this persistent entity's data is overflowed
* and/or persisted.
*
* Maybe the name of a Spring bean defined in the Spring context.
*
* Defaults to unset.
*/
String diskStoreName() default "";
/**
* Determines whether disk-based operations (used in overflow and persistent) are synchronous or asynchronous.
*
* Defaults to {@literal synchronous}.
*/
boolean diskSynchronous() default true;
/**
* Determines whether this {@link org.apache.geode.cache.Region Region's} data access operations participates in
* any existing, Global JTA transaction in progress.
*
* Defaults to {@literal false} (will NOT ignore JTA).
*/
boolean ignoreJta() default false;
/**
* Determines whether this persistent entity's {@link org.apache.geode.cache.Region} is persistent,
* storing data to disk.
*
* Note, this setting independent of whether or not the {@link org.apache.geode.cache.Region} associated
* with this persistent entity overflows data to disk during eviction due to entry/heap/memory constraints.
*
* A {@link org.apache.geode.cache.Region} can also be persistent without an explicit
* {@link org.apache.geode.cache.DiskStore} defined; in that case, GemFire/Geode writes to the "DEFAULT"
* {@link org.apache.geode.cache.DiskStore}.
*
* Defaults to {@literal false}.
*
* @see org.apache.geode.cache.DataPolicy
*/
boolean persistent() default false;
}

View File

@@ -0,0 +1,180 @@
/*
* Copyright 2016 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.mapping;
import java.lang.annotation.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.springframework.core.annotation.AliasFor;
/**
* {@link Annotation} defining the Partition {@link Region} in which the application persistent entity will be stored.
*
* @author John Blum
* @see org.springframework.data.gemfire.mapping.Region
* @since 1.9.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Region
@SuppressWarnings("unused")
public @interface PartitionRegion {
/**
* Name, or fully-qualified bean name of the {@link org.apache.geode.cache.Region}
* in which the application persistent entity will be stored (e.g. "Users", or "/Local/Admin/Users").
*
* Defaults to simple name of the application persistent entity defined by {@link java.lang.Class#getSimpleName()}.
*
* @return the name or fully-qualified path of the {@link Region} in which the application persistent entity
* will be stored.
*/
@AliasFor(annotation = Region.class, attribute = "name")
String name() default "";
/**
* Name, or fully-qualified bean name of the {@link org.apache.geode.cache.Region}
* in which the application persistent entity will be stored (e.g. "Users", or "/Local/Admin/Users").
*
* Defaults to simple name of the application persistent entity defined by {@link java.lang.Class#getSimpleName()}.
*
* @return the name or fully-qualified path of the {@link Region} in which the application persistent entity
* will be stored.
*/
@AliasFor(annotation = Region.class, attribute = "value")
String value() default "";
/**
* Sets the name of the {@link org.apache.geode.cache.Region} to which this persistent entity's
* {@link org.apache.geode.cache.Region} will be collocated.
*
* Collocation is used in data access, querying operations where the user wishes to combine data
* from multiple {@link org.apache.geode.cache.Region Regions} into a single result set returned
* from an OQL statement.
*
* Defaults to unset.
*/
String collocatedWith() default "";
/**
* Name of the {@link org.apache.geode.cache.DiskStore} in which this persistent entity's data is overflowed
* and/or persisted.
*
* Maybe the name of a Spring bean defined in the Spring context.
*
* Defaults to unset.
*/
String diskStoreName() default "";
/**
* Determines whether disk-based operations (used in overflow and persistent) are synchronous or asynchronous.
*
* Defaults to {@literal synchronous}.
*/
boolean diskSynchronous() default true;
/**
* Defines an array of fixed partitions in a {@link org.apache.geode.cache.DataPolicy#PARTITION}
* {@link org.apache.geode.cache.Region}
*
* Default is unset.
*
* @see org.apache.geode.cache.FixedPartitionAttributes
*/
FixedPartition[] fixedPartitions() default {};
/**
* Determines whether this {@link org.apache.geode.cache.Region Region's} data access operations participates in
* any existing, Global JTA transaction in progress.
*
* Defaults to {@literal false} (will NOT ignore JTA).
*/
boolean ignoreJta() default false;
/**
* Name of the {@link org.apache.geode.cache.PartitionResolver} used to customize the partitioning strategy
* in this persistent entity's {@link org.apache.geode.cache.DataPolicy#PARTITION}
* {@link org.apache.geode.cache.Region}.
*
* This setting may also be the name of a Spring bean defined in the Spring context.
*
* Defaults to unset, thus using the default GemFire/Geode partitioning strategy.
*/
String partitionResolverName() default "";
/**
* Determines whether this persistent entity's {@link org.apache.geode.cache.Region} is persistent,
* storing data to disk.
*
* Note, this setting independent of whether or not the {@link org.apache.geode.cache.Region} associated
* with this persistent entity overflows data to disk during eviction due to entry/heap/memory constraints.
*
* A {@link org.apache.geode.cache.Region} can also be persistent without an explicit
* {@link org.apache.geode.cache.DiskStore} defined; in that case, GemFire/Geode writes to the "DEFAULT"
* {@link org.apache.geode.cache.DiskStore}.
*
* Defaults to {@literal false}.
*
* @see org.apache.geode.cache.DataPolicy
*/
boolean persistent() default false;
/**
* Defines the number of redundant copies of this persistent entity's data.
*
* Defaults to {@literal 0}.
*/
int redundantCopies() default 0;
/**
* {@link FixedPartition} defined fixed partition meta-data within
* a {@link org.apache.geode.cache.DataPolicy#PARTITION} {@link org.apache.geode.cache.Region}.
*
* @see org.apache.geode.cache.FixedPartitionAttributes
*/
@interface FixedPartition {
/**
* Name of the fixed partition.
*/
String name();
/**
* Set whether this partition is the primary partition in
* the {@link org.apache.geode.cache.DataPolicy#PARTITION} {@link org.apache.geode.cache.Region}.
*
* Defaults to {@literal false}.
*/
boolean primary() default false;
/**
* Defines the number of bucket in this partition.
*
* Defaults to {@literal 1}.
*/
int numBuckets() default 1;
}
}

View File

@@ -13,32 +13,61 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire.mapping;
import java.lang.annotation.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 java.util.Arrays;
import java.util.List;
import org.springframework.core.annotation.AliasFor;
/**
* Annotation to define the region an entity will be stored in.
* {@link Annotation} defining the {@link Region} in which the application persistent entity will be stored.
*
* @author Oliver Gierke
* @author John Blum
* @see org.apache.geode.cache.Region
*/
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@SuppressWarnings("unused")
public @interface Region {
@SuppressWarnings("unchecked")
List<Class<? extends Annotation>> REGION_ANNOTATION_TYPES = Arrays.asList(
ClientRegion.class, LocalRegion.class, PartitionRegion.class, ReplicateRegion.class, Region.class);
/**
* The name, or fully-qualified bean name of the {@link org.apache.geode.cache.Region} the entity
* shall be stored in (e.g. "Users", or "/Local/Admin/Users").
* Name, or fully-qualified bean name of the {@link org.apache.geode.cache.Region}
* in which the application persistent entity will be stored (e.g. "Users", or "/Local/Admin/Users").
*
* @return the name or qualified path of the Region the entity shall be persisted in.
* Defaults to simple name of the application persistent entity defined by {@link java.lang.Class#getSimpleName()}.
*
* @return the name or fully-qualified path of the {@link Region} in which the application persistent entity
* will be stored.
*/
@AliasFor(attribute = "value")
String name() default "";
/**
* Name, or fully-qualified bean name of the {@link org.apache.geode.cache.Region}
* in which the application persistent entity will be stored (e.g. "Users", or "/Local/Admin/Users").
*
* Defaults to simple name of the application persistent entity defined by {@link java.lang.Class#getSimpleName()}.
*
* @return the name or fully-qualified path of the {@link Region} in which the application persistent entity
* will be stored.
*/
@AliasFor(attribute = "name")
String value() default "";
}

View File

@@ -0,0 +1,121 @@
/*
* Copyright 2016 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.mapping;
import java.lang.annotation.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.springframework.core.annotation.AliasFor;
import org.springframework.data.gemfire.ScopeType;
/**
* {@link Annotation} defining the Replicate {@link Region} in which the application persistent entity will be stored.
*
* @author John Blum
* @see org.springframework.data.gemfire.mapping.Region
* @since 1.9.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Region
@SuppressWarnings("unused")
public @interface ReplicateRegion {
/**
* Name, or fully-qualified bean name of the {@link org.apache.geode.cache.Region}
* in which the application persistent entity will be stored (e.g. "Users", or "/Local/Admin/Users").
*
* Defaults to simple name of the application persistent entity defined by {@link java.lang.Class#getSimpleName()}.
*
* @return the name or fully-qualified path of the {@link Region} in which the application persistent entity
* will be stored.
*/
@AliasFor(annotation = Region.class, attribute = "name")
String name() default "";
/**
* Name, or fully-qualified bean name of the {@link org.apache.geode.cache.Region}
* in which the application persistent entity will be stored (e.g. "Users", or "/Local/Admin/Users").
*
* Defaults to simple name of the application persistent entity defined by {@link java.lang.Class#getSimpleName()}.
*
* @return the name or fully-qualified path of the {@link Region} in which the application persistent entity
* will be stored.
*/
@AliasFor(annotation = Region.class, attribute = "value")
String value() default "";
/**
* Name of the {@link org.apache.geode.cache.DiskStore} in which this persistent entity's data is overflowed
* and/or persisted.
*
* Maybe the name of a Spring bean defined in the Spring context.
*
* Defaults to unset.
*/
String diskStoreName() default "";
/**
* Determines whether disk-based operations (used in overflow and persistent) are synchronous or asynchronous.
*
* Defaults to {@literal synchronous}.
*/
boolean diskSynchronous() default true;
/**
* Determines whether this {@link org.apache.geode.cache.Region Region's} data access operations participates in
* any existing, Global JTA transaction in progress.
*
* Defaults to {@literal false} (will NOT ignore JTA).
*/
boolean ignoreJta() default false;
/**
* Determines whether this persistent entity's {@link org.apache.geode.cache.Region} is persistent,
* storing data to disk.
*
* Note, this setting independent of whether or not the {@link org.apache.geode.cache.Region} associated
* with this persistent entity overflows data to disk during eviction due to entry/heap/memory constraints.
*
* A {@link org.apache.geode.cache.Region} can also be persistent without an explicit
* {@link org.apache.geode.cache.DiskStore} defined; in that case, GemFire/Geode writes to the "DEFAULT"
* {@link org.apache.geode.cache.DiskStore}.
*
* Defaults to {@literal false}.
*
* @see org.apache.geode.cache.DataPolicy
*/
boolean persistent() default false;
/**
* Defines the {@link org.apache.geode.cache.Scope} used by this persistent entity's
* {@link org.apache.geode.cache.DataPolicy#REPLICATE} {@link org.apache.geode.cache.Region} to
* acknowledge messages sent between peers.
*
* Defaults to {@link ScopeType#DISTRIBUTED_NO_ACK}
*/
ScopeType scope() default ScopeType.DISTRIBUTED_NO_ACK;
}

View File

@@ -57,7 +57,7 @@ public class GemfireRepositoryConfigurationExtension extends RepositoryConfigura
*/
@Override
protected Collection<Class<? extends Annotation>> getIdentifyingAnnotations() {
return Collections.<Class<? extends Annotation>>singleton(Region.class);
return Region.REGION_ANNOTATION_TYPES;
}
/*

View File

@@ -16,6 +16,8 @@ package org.springframework.data.gemfire.util;
import java.lang.reflect.Array;
import java.util.Arrays;
import org.springframework.util.ObjectUtils;
/**
* The ArrayUtils class is a utility class for working with Object arrays.
*
@@ -37,6 +39,18 @@ public abstract class ArrayUtils {
return elements;
}
/**
* Returns the given {@code array} if not {@literal null} or empty, otherwise returns the {@code defaultArray}.
*
* @param <T> {@link Class} type of the array elements.
* @param array array to evaluate.
* @param defaultArray array to return if the given {@code array} is {@literal null} or empty.
* @return the given {@code array} if not {@literal null} or empty otherwise return the {@code defaultArray}.
*/
public static <T> T[] defaultIfEmpty(T[] array, T[] defaultArray) {
return (!ObjectUtils.isEmpty(array) ? array : defaultArray);
}
/**
* Null-safe method to return the first element in the array or {@literal null}
* if the array is {@literal null} or empty.

View File

@@ -46,6 +46,28 @@ import org.springframework.util.Assert;
@SuppressWarnings("unused")
public abstract class CollectionUtils extends org.springframework.util.CollectionUtils {
/**
* Adds all elements from the given {@link Iterable} to the {@link Collection}.
*
* @param <E> {@link Class} type of the elements in the {@link Collection} and {@link Iterable}.
* @param <T> concrete {@link Class} type of the {@link Collection}.
* @param collection {@link Collection} in which to add the elements from the {@link Iterable}.
* @param iterable {@link Iterable} containing the elements to add to the {@link Collection}.
* @return the given {@link Collection}.
* @throws IllegalArgumentException if {@link Collection} is {@literal null}.
* @see java.lang.Iterable
* @see java.util.Collection
*/
public static <E, T extends Collection<E>> T addAll(T collection, Iterable<E> iterable) {
Assert.notNull(collection, "Collection must not be null");
for (E element : nullSafeIterable(iterable)) {
collection.add(element);
}
return collection;
}
/**
* Returns an unmodifiable {@link Set} containing the elements from the given object array.
*
@@ -60,6 +82,32 @@ public abstract class CollectionUtils extends org.springframework.util.Collectio
return Collections.unmodifiableSet(set);
}
/**
* Returns the given {@link Iterable} if not {@literal null} or empty, otherwise returns the {@code defaultIterable}.
*
* @param <T> concrete {@link Class} type of the {@link Iterable}.
* @param <E> {@link Class} type of the elements in the {@link Iterable Iterables}.
* @param iterable {@link Iterable} to evaluate.
* @param defaultIterable {@link Iterable} to return if the given {@code iterable} is {@literal null} or empty.
* @return {@code iterable} if not {@literal null} or empty otherwise return {@code defaultIterable}.
* @see java.lang.Iterable
*/
public static <E, T extends Iterable<E>> T defaultIfEmpty(T iterable, T defaultIterable) {
return (iterable != null && iterable.iterator().hasNext() ? iterable : defaultIterable);
}
/**
* Returns an empty {@link Iterable} object.
*
* @param <T> {@link Class} type of the elements in the {@link Iterable}.
* @return an empty {@link Iterable}.
* @see java.lang.Iterable
* @see #nullSafeIterable(Iterable)
*/
public static <T> Iterable<T> emptyIterable() {
return nullSafeIterable(null);
}
/**
* Adapts the given Enumeration as an Iterable object for use within a for each loop.
*
@@ -140,6 +188,7 @@ public abstract class CollectionUtils extends org.springframework.util.Collectio
* @see java.util.Collections#emptyMap()
* @see java.util.Map
*/
@SuppressWarnings("all")
public static <K, V> Map<K, V> nullSafeMap(Map<K, V> map) {
return (map != null ? map : Collections.emptyMap());
}