/*
* Copyright 2010-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire.cache;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.Region;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.support.AbstractCacheManager;
import org.springframework.util.Assert;
/**
* Core Spring Framework {@link CacheManager} implementation backed by a GemFire cache instance
* (either a client or peer cache).
*
* Automatically discovers available caches (or GemFire {@link Region Regions}) when a cache for a given name
* is missing and dynamic cache lookup/creation is enabled.
*
* @author Costin Leau
* @author David Turanski
* @author John Blum
* @see org.springframework.cache.Cache
* @see org.springframework.cache.CacheManager
* @see org.springframework.cache.support.AbstractCacheManager
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.Region
*/
@SuppressWarnings("unused")
public class GemfireCacheManager extends AbstractCacheManager {
private final AtomicBoolean dynamic = new AtomicBoolean(true);
private org.apache.geode.cache.GemFireCache gemfireCache;
private Set<Region<?, ?>> regions;
private Set<String> cacheNames;
/* (non-Javadoc) */
@SuppressWarnings("all")
<T extends GemFireCache> T assertGemFireCacheAvailable(T gemfireCache) {
Assert.state(gemfireCache != null, "A GemFire cache instance is required");
Assert.state(!gemfireCache.isClosed(), String.format("GemFire cache [%s] has been closed",
gemfireCache.getName()));
return gemfireCache;
}
/* (non-Javadoc) */
@SuppressWarnings("all")
Region<?, ?> assertGemFireRegionAvailable(Region<?, ?> region, String cacheName) {
Assert.state(region != null, String.format("No Region for cache name [%s] was found", cacheName));
Assert.state(!region.isDestroyed(), String.format("Region [%s] has been destroyed", cacheName));
return region;
}
/**
* Loads all configured GemFire {@link Region Regions} that will be used by this {@link CacheManager}.
*
* Any GemFire {@link Region Regions} configured with the {@link #regions} property will take precedence over
* any configured {@link #cacheNames}. If no GemFire {@link Region Regions} were configured, then any
* {@link #cacheNames} that were specified will be used to lookup existing GemFire {@link Region Regions}
* to function as Spring {@link Cache Caches}in Spring's caching infrastructure.
*
* However, if neither {@link #regions} nor {@link #cacheNames} were specified, then all defined GemFire
* {@link Region Regions} declared in the Spring application context, as determined by
* {@link GemFireCache#rootRegions()}, will be used as Spring {@link Cache Caches}, and this {@link CacheManager}
* will allow any dynamically created GemFire {@link Region Regions} at runtime to be found and used as a
* Spring {@link Cache} as well.
*
* @return a {@link Collection} of GemFire {@link Region Regions} used by this {@link CacheManager}
* to function as {@link Cache Caches} in Spring's caching infrastructure.
* @throws IllegalStateException if a GemFire cache instance was not provided, the provided GemFire cache instance
* has been closed, no GemFire {@link Region} could be found for a given cache name, or the GemFire {@link Region}
* for the given cache name has been destroyed.
* @see org.springframework.cache.Cache
*/
@Override
protected Collection<Cache> loadCaches() {
Set<Region<?, ?>> regions = resolveRegions(this.gemfireCache, this.regions, this.cacheNames);
Collection<Cache> caches = new HashSet<Cache>(regions.size());
for (Region<?, ?> region : regions) {
caches.add(newGemfireCache(region));
}
return caches;
}
/* (non-Javadoc) */
Set<Region<?, ?>> resolveRegions(GemFireCache gemfireCache, Set<Region<?, ?>> regions, Set<String> cacheNames) {
if (isSet(regions)) {
dynamic.set(false);
return regions;
}
else if (isSet(cacheNames)) {
dynamic.set(false);
regions = new HashSet<Region<?, ?>>(cacheNames.size());
for (String cacheName : cacheNames) {
regions.add(regionFor(gemfireCache, cacheName));
}
return regions;
}
else {
return assertGemFireCacheAvailable(gemfireCache).rootRegions();
}
}
/* (non-Javadoc) */
boolean isSet(Iterable<?> collection) {
return (collection != null && collection.iterator().hasNext());
}
/**
* Constructs a new instance of {@link GemfireCache} initialized with the given GemFire {@link Region}.
*
* @param region GemFire {@link Region} to wrap (adapt).
* @return an instance of {@link GemfireCache} initialized with the given GemFire {@link Region}.
* @see GemfireCache
* @see org.apache.geode.cache.Region
*/
protected GemfireCache newGemfireCache(Region<?, ?> region) {
return GemfireCache.wrap(region);
}
/* (non-Javadoc) */
Region<?, ?> regionFor(GemFireCache gemfireCache, String cacheName) {
return assertGemFireRegionAvailable(assertGemFireCacheAvailable(gemfireCache).getRegion(cacheName), cacheName);
}
/**
* Returns a missing Spring {@link Cache} for the given {@code name}.
*
* To return a missing Spring {@link Cache} for the given {@code name}, dynamic cache lookup/creation must be
* enabled, which means that either the {@link #cacheNames} or {@link #regions} properties must not be set.
* If either property was specified then dynamic Spring {@link Cache} lookup/creation will be disabled and this
* overridden {@link AbstractCacheManager#getMissingCache(String)} method will return {@literal null}.
*
* @param name name of the missing Spring {@link Cache} to lookup (and potentially create).
* @return a Spring {@link Cache} instance for the given {@code name} or {@literal null} if the {@link Cache}
* cannot be found (or possibly created).
* @see org.springframework.cache.support.AbstractCacheManager#getMissingCache(String)
* @see org.springframework.cache.Cache
*/
@Override
protected Cache getMissingCache(String name) {
Cache cache = super.getMissingCache(name);
return (cache != null ? cache : (isDynamic() ? newGemfireCache(regionFor(this.gemfireCache, name)) : null));
}
/**
* Determines whether this {@link CacheManager} allows the dynamic creation of a {@link Cache} at runtime.
*
* @return a boolean value indicating whether dynamic {@link Cache} creation is enabled.
*/
protected boolean isDynamic() {
return dynamic.get();
}
/**
* Sets the GemFire cache instance backing this {@link CacheManager}.
*
* When set, if neither {@link Region Regions} nor {@code cacheNames} were specified, then this {@link CacheManager}
* is capable of creating Spring {@link Cache Caches} backed by existing GemFire {@link Region Regions} used by
* the application at runtime. However, in order to dynamically create Spring {@link Cache Caches} a reference to
* an open GemFire cache instance must be set.
*
* @param gemfireCache the GemFire cache instance used by this {@link CacheManager}
* to manage Spring {@link Cache Caches}.
* @see org.apache.geode.cache.GemFireCache
*/
public void setCache(org.apache.geode.cache.GemFireCache gemfireCache) {
this.gemfireCache = gemfireCache;
}
/**
* Returns the {@link GemFireCache} instance backing this {@link CacheManager}.
*
* @return the {@link GemFireCache} instance backing this {@link CacheManager}.
* @see org.apache.geode.cache.GemFireCache
*/
protected org.apache.geode.cache.GemFireCache getCache() {
return this.gemfireCache;
}
/**
* Sets the names of all Spring {@link Cache Caches} that will be used in the application.
*
* When set, this disables the dynamic capability of this {@link CacheManager} to create Spring {@link Cache Caches}
* at runtime by dynamically looking up existing {@link Region Regions} from the GemFire cache instance.
*
* @param cacheNames {@link Set} of cache names that will be used in the application.
* @see java.util.Set
*/
public void setCacheNames(Set<String> cacheNames) {
this.cacheNames = cacheNames;
}
/**
* Explicitly sets the GemFire {@link Region Regions} to be used as Spring {@link Cache Caches}
* in the application.
*
* When set, this disables the dynamic capability of this {@link CacheManager} to create Spring {@link Cache Caches}
* at runtime by dynamically looking up existing {@link Region Regions} from the GemFire cache instance.
*
* @param regions {@link Set} of GemFire {@link Region Regions} used by this {@link CacheManager}
* as Spring {@link Cache Caches}.
* @see org.apache.geode.cache.Region
*/
public void setRegions(Set<Region<?, ?>> regions) {
this.regions = regions;
}
/**
* Returns the set of GemFire {@link Region Regions} used explicitly as Spring {@link Cache Caches}
* in Spring's caching infrastructure.
*
* @return the set of GemFire {@link Region Regions} functioning as Spring {@link Cache Caches}
* in Spring's caching infrastructure
* @see org.apache.geode.cache.Region
*/
protected Set<Region<?, ?>> getRegions() {
return this.regions;
}
}