/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.springframework.data.gemfire.client;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.client.ClientRegionFactory;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.apache.geode.cache.execute.Function;
import org.apache.geode.management.internal.cli.domain.RegionInformation;
import org.apache.geode.management.internal.cli.functions.GetRegionsFunction;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.data.gemfire.function.execution.GemfireOnServersFunctionTemplate;
import org.springframework.data.gemfire.client.function.ListRegionsOnServerFunction;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* A Spring {@link BeanFactoryPostProcessor} used to register a Client Region Proxy bean for each Region
* accessible to a GemFire DataSource. If the Region is already defined, the bean definition will not be overridden.
*
* @author David Turanski
* @author John Blum
* @see org.springframework.beans.factory.config.BeanFactoryPostProcessor
* @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory
* @see org.springframework.data.gemfire.function.execution.GemfireOnServersFunctionTemplate
* @see ListRegionsOnServerFunction
* @see org.apache.geode.cache.Region
* @see org.apache.geode.cache.client.ClientCache
* @see org.apache.geode.cache.client.ClientRegionFactory
* @see org.apache.geode.cache.execute.Function
* @see org.apache.geode.management.internal.cli.functions.GetRegionsFunction
* @since 1.2.0
*/
public class GemfireDataSourcePostProcessor implements BeanFactoryPostProcessor {
protected final Log logger = LogFactory.getLog(getClass());
private final ClientCache clientCache;
/**
* Constructs an instance of the GemfireDataSourcePostProcessor BeanFactoryPostProcessor class initialized
* with the specified GemFire ClientCache instance for creating client PROXY Regions for all data Regions
* configured in the GemFire cluster.
*
* @param clientCache the GemFire ClientCache instance.
* @see org.apache.geode.cache.client.ClientCache
*/
public GemfireDataSourcePostProcessor(final ClientCache clientCache) {
this.clientCache = clientCache;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.config.BeanFactoryPostProcessor#postProcessBeanFactory(org.springframework.beans.factory.config.ConfigurableListableBeanFactory)
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
createClientRegionProxies(beanFactory, regionNames());
}
/* (non-Javadoc) */
Iterable<String> regionNames() {
try {
return execute(new ListRegionsOnServerFunction());
}
catch (Exception ignore) {
try {
Object results = execute(new GetRegionsFunction());
List<String> regionNames = Collections.emptyList();
if (containsRegionInformation(results)) {
Object[] resultsArray = (Object[]) results;
regionNames = new ArrayList<String>(resultsArray.length);
for (Object result : resultsArray) {
regionNames.add(((RegionInformation) result).getName());
}
}
return regionNames;
}
catch (Exception e) {
log("Failed to determine the Regions available on the Server: %n%1$s", e);
return Collections.emptyList();
}
}
}
/* (non-Javadoc) */
@SuppressWarnings("unchecked")
<T> T execute(Function gemfireFunction, Object... arguments) {
return new GemfireOnServersFunctionTemplate(clientCache).executeAndExtract(gemfireFunction, arguments);
}
/* (non-Javadoc) */
boolean containsRegionInformation(Object results) {
return (results instanceof Object[] && ((Object[]) results).length > 0
&& ((Object[]) results)[0] instanceof RegionInformation);
}
/* (non-Javadoc) */
void createClientRegionProxies(ConfigurableListableBeanFactory beanFactory, Iterable<String> regionNames) {
if (regionNames.iterator().hasNext()) {
ClientRegionFactory<?, ?> clientRegionFactory = clientCache.createClientRegionFactory(ClientRegionShortcut.PROXY);
for (String regionName : regionNames) {
boolean createRegion = true;
if (beanFactory.containsBean(regionName)) {
Object existingBean = beanFactory.getBean(regionName);
Assert.isTrue(existingBean instanceof Region, String.format(
"Cannot create a client PROXY Region bean named '%1$s'. A bean with this name of type '%2$s' already exists.",
regionName, ObjectUtils.nullSafeClassName(existingBean)));
createRegion = false;
}
if (createRegion) {
log("Creating Region bean with name '%s'...", regionName);
beanFactory.registerSingleton(regionName, clientRegionFactory.create(regionName));
}
else {
log("A Region with name '%s' is already defined.", regionName);
}
}
}
}
/* (non-Javadoc) */
void log(String message, Object... arguments) {
if (logger.isDebugEnabled()) {
logger.debug(String.format(message, arguments));
}
}
}