/*
* 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.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.apache.geode.cache.Region;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.gemfire.GenericRegionFactoryBean;
import org.springframework.data.gemfire.LocalRegionFactoryBean;
import org.springframework.data.gemfire.PartitionedRegionFactoryBean;
import org.springframework.data.gemfire.ReplicatedRegionFactoryBean;
import org.springframework.data.gemfire.client.ClientRegionFactoryBean;
import org.springframework.data.gemfire.config.annotation.support.EmbeddedServiceConfigurationSupport;
import org.springframework.data.gemfire.util.ArrayUtils;
import org.springframework.data.gemfire.util.CollectionUtils;
import org.springframework.data.gemfire.util.PropertiesBuilder;
/**
* The OffHeapConfiguration class is a Spring {@link org.springframework.context.annotation.ImportBeanDefinitionRegistrar}
* capable of enabling GemFire cache {@link Region Regions} to use Off-Heap memory for data storage.
*
* @author John Blum
* @see org.springframework.data.gemfire.config.annotation.EnableOffHeap
* @see org.springframework.data.gemfire.config.annotation.support.EmbeddedServiceConfigurationSupport
* @since 1.9.0
*/
public class OffHeapConfiguration extends EmbeddedServiceConfigurationSupport {
/* (non-Javadoc) */
@Override
protected Class getAnnotationType() {
return EnableOffHeap.class;
}
/* (non-Javadoc) */
@Override
protected void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
Map<String, Object> annotationAttributes, BeanDefinitionRegistry registry) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
OffHeapBeanFactoryPostProcessor.class);
builder.addConstructorArgValue(annotationAttributes.get("regionNames"));
registry.registerBeanDefinition(generateBeanName(OffHeapBeanFactoryPostProcessor.class),
builder.getBeanDefinition());
}
/* (non-Javadoc) */
@Override
protected Properties toGemFireProperties(Map<String, Object> annotationAttributes) {
PropertiesBuilder gemfireProperties = PropertiesBuilder.create();
gemfireProperties.setProperty("off-heap-memory-size", annotationAttributes.get("memorySize"));
return gemfireProperties.build();
}
@SuppressWarnings("unused")
protected static class OffHeapBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
protected static final Set<String> REGION_FACTORY_BEAN_TYPES = new HashSet<String>(5);
static {
REGION_FACTORY_BEAN_TYPES.add(ClientRegionFactoryBean.class.getName());
REGION_FACTORY_BEAN_TYPES.add(GenericRegionFactoryBean.class.getName());
REGION_FACTORY_BEAN_TYPES.add(LocalRegionFactoryBean.class.getName());
REGION_FACTORY_BEAN_TYPES.add(PartitionedRegionFactoryBean.class.getName());
REGION_FACTORY_BEAN_TYPES.add(ReplicatedRegionFactoryBean.class.getName());
}
private final Set<String> regionNames;
protected OffHeapBeanFactoryPostProcessor(String[] regionNames) {
this(CollectionUtils.asSet(ArrayUtils.nullSafeArray(regionNames, String.class)));
}
protected OffHeapBeanFactoryPostProcessor(Set<String> regionNames) {
this.regionNames = CollectionUtils.nullSafeSet(regionNames);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition bean = beanFactory.getBeanDefinition(beanName);
if (isTargetedRegionBean(beanName, bean, beanFactory)) {
bean.getPropertyValues().addPropertyValue("offHeap", true);
}
}
}
boolean isTargetedRegionBean(String beanName, BeanDefinition bean,
ConfigurableListableBeanFactory beanFactory) {
return (isRegionBean(bean) && isNamedRegion(beanName, bean, beanFactory));
}
boolean isRegionBean(BeanDefinition bean) {
return (bean != null && REGION_FACTORY_BEAN_TYPES.contains(bean.getBeanClassName()));
}
boolean isNamedRegion(String beanName, BeanDefinition bean, ConfigurableListableBeanFactory beanFactory) {
return (CollectionUtils.isEmpty(regionNames) || CollectionUtils.containsAny(regionNames,
getBeanNames(beanName, bean, beanFactory)));
}
Collection<String> getBeanNames(String beanName, BeanDefinition bean, BeanFactory beanFactory) {
Collection<String> beanNames = new HashSet<String>();
beanNames.add(beanName);
Collections.addAll(beanNames, beanFactory.getAliases(beanName));
PropertyValue regionName = bean.getPropertyValues().getPropertyValue("regionName");
if (regionName != null) {
Object regionNameValue = regionName.getValue();
if (regionNameValue != null) {
beanNames.add(regionNameValue.toString());
}
}
return beanNames;
}
}
}