SGF-469 - Add support for CDI.
This commit is contained in:
@@ -35,14 +35,12 @@ import org.w3c.dom.Element;
|
||||
@SuppressWarnings("unused")
|
||||
class GemfireNamespaceHandler extends NamespaceHandlerSupport {
|
||||
|
||||
protected static final List<String> GEMFIRE7_ELEMENTS = Arrays.asList("async-event-queue", "gateway-sender",
|
||||
static final List<String> GEMFIRE7_ELEMENTS = Arrays.asList("async-event-queue", "gateway-sender",
|
||||
"gateway-receiver");
|
||||
|
||||
@Override
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
boolean v7ElementsPresent = GEMFIRE7_ELEMENTS.contains(element.getLocalName());
|
||||
|
||||
if (v7ElementsPresent) {
|
||||
if (GEMFIRE7_ELEMENTS.contains(element.getLocalName())) {
|
||||
ParsingUtils.throwExceptionIfNotGemfireV7(element.getLocalName(), null, parserContext);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright 2012 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.repository.cdi;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import javax.enterprise.context.spi.CreationalContext;
|
||||
import javax.enterprise.inject.spi.Bean;
|
||||
import javax.enterprise.inject.spi.BeanManager;
|
||||
|
||||
import org.springframework.data.gemfire.mapping.GemfireMappingContext;
|
||||
import org.springframework.data.gemfire.repository.support.GemfireRepositoryFactory;
|
||||
import org.springframework.data.repository.cdi.CdiRepositoryBean;
|
||||
import org.springframework.data.repository.config.CustomRepositoryImplementationDetector;
|
||||
|
||||
import com.gemstone.gemfire.cache.Region;
|
||||
|
||||
/**
|
||||
* A CDI-based bean that represents a GemFire Repository.
|
||||
*
|
||||
* @author John Blum
|
||||
* @param <T> class type of the Repository.
|
||||
* @see javax.enterprise.context.spi.CreationalContext
|
||||
* @see javax.enterprise.inject.spi.Bean
|
||||
* @see javax.enterprise.inject.spi.BeanManager
|
||||
* @see org.springframework.data.gemfire.mapping.GemfireMappingContext
|
||||
* @see org.springframework.data.gemfire.repository.support.GemfireRepositoryFactory
|
||||
* @see org.springframework.data.repository.cdi.CdiRepositoryBean
|
||||
* @see org.springframework.data.repository.config.CustomRepositoryImplementationDetector
|
||||
* @see com.gemstone.gemfire.cache.Region
|
||||
* @since 1.8.0
|
||||
*/
|
||||
class GemfireRepositoryBean<T> extends CdiRepositoryBean<T> {
|
||||
|
||||
static final GemfireMappingContext DEFAULT_GEMFIRE_MAPPING_CONTEXT = new GemfireMappingContext();
|
||||
|
||||
private final Bean<GemfireMappingContext> gemfireMappingContextBean;
|
||||
|
||||
private final BeanManager beanManager;
|
||||
|
||||
private final Set<Bean<Region>> regionBeans;
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@SuppressWarnings("unchecked")
|
||||
GemfireRepositoryBean(BeanManager beanManager, Class<T> repositoryType, Set<Annotation> qualifiers,
|
||||
CustomRepositoryImplementationDetector detector, Bean<GemfireMappingContext> gemfireMappingContextBean,
|
||||
Set<Bean<Region>> regionBeans) {
|
||||
|
||||
super(qualifiers, repositoryType, beanManager, detector);
|
||||
|
||||
this.beanManager = beanManager;
|
||||
this.gemfireMappingContextBean = gemfireMappingContextBean;
|
||||
this.regionBeans = regionBeans;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of the given {@link Bean} from the container.
|
||||
*
|
||||
* @param <S> the actual class type of the {@link Bean}.
|
||||
* @param bean the {@link Bean} defining the instance to create.
|
||||
* @param type the expected component type of the instance created from the {@link Bean}.
|
||||
* @return an instance of the given {@link Bean}.
|
||||
* @see javax.enterprise.inject.spi.BeanManager#getReference(Bean, Type, CreationalContext)
|
||||
* @see javax.enterprise.inject.spi.Bean
|
||||
* @see java.lang.reflect.Type
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <S> S getDependencyInstance(Bean<S> bean, Type type) {
|
||||
return (S) beanManager.getReference(bean, type, beanManager.createCreationalContext(bean));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the desired, actual component type from the {@link Bean} in which the instance is created.
|
||||
*
|
||||
* @param <S> the class type of the component.
|
||||
* @param bean the {@link Bean} from which the types are evaluated and an instance is created.
|
||||
* @param targetType the desired class type of the component.
|
||||
* @return a resolved component {@link Type} of the {@link Bean}instance.
|
||||
* @throws IllegalStateException if the desired class type cannot be resolved.
|
||||
* @see javax.enterprise.inject.spi.Bean#getTypes()
|
||||
* @see java.lang.Class
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <S> Type resolveType(Bean<S> bean, Class<S> targetType) {
|
||||
for (Type type : bean.getTypes()) {
|
||||
Type assignableType = (type instanceof ParameterizedType ? ((ParameterizedType) type).getRawType() : type);
|
||||
|
||||
if (assignableType instanceof Class && targetType.isAssignableFrom((Class) assignableType)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException(String.format(
|
||||
"unable to resolve bean instance of type [%1$s] from bean definition [%2$s]",
|
||||
targetType, bean));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
Iterable<Region<?, ?>> resolveGemfireRegions() {
|
||||
Set<Region<?, ?>> regions = new HashSet<Region<?, ?>>(regionBeans.size());
|
||||
|
||||
for (Bean<Region> regionBean : regionBeans) {
|
||||
regions.add(getDependencyInstance(regionBean, resolveType(regionBean, Region.class)));
|
||||
}
|
||||
|
||||
return regions;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
GemfireMappingContext resolveGemfireMappingContext() {
|
||||
return (gemfireMappingContextBean != null
|
||||
? getDependencyInstance(gemfireMappingContextBean, GemfireMappingContext.class)
|
||||
: DEFAULT_GEMFIRE_MAPPING_CONTEXT);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
GemfireRepositoryFactory newGemfireRepositoryFactory() {
|
||||
return new GemfireRepositoryFactory(resolveGemfireRegions(), resolveGemfireMappingContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of the given Repository type as a bean instance in the CDI container.
|
||||
*
|
||||
* @param creationalContext operations used by the {@link javax.enterprise.context.spi.Contextual} implementation
|
||||
* during creation of the bean instance.
|
||||
* @param repositoryType the actual class type of the SD (GemFire) Repository.
|
||||
* @param customImplementation the supporting custom Repository implementing class.
|
||||
* @return a factory used to create instance of {@link org.springframework.data.gemfire.repository.GemfireRepository}.
|
||||
* @see javax.enterprise.context.spi.Contextual#create(javax.enterprise.context.spi.CreationalContext)
|
||||
* @see #newGemfireRepositoryFactory()
|
||||
*/
|
||||
@Override
|
||||
protected T create(CreationalContext<T> creationalContext, Class<T> repositoryType, Object customImplementation) {
|
||||
return newGemfireRepositoryFactory().getRepository(repositoryType, customImplementation);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright 2012 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.repository.cdi;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.enterprise.event.Observes;
|
||||
import javax.enterprise.inject.spi.AfterBeanDiscovery;
|
||||
import javax.enterprise.inject.spi.Bean;
|
||||
import javax.enterprise.inject.spi.BeanManager;
|
||||
import javax.enterprise.inject.spi.ProcessBean;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.gemfire.mapping.GemfireMappingContext;
|
||||
import org.springframework.data.repository.cdi.CdiRepositoryBean;
|
||||
import org.springframework.data.repository.cdi.CdiRepositoryExtensionSupport;
|
||||
|
||||
import com.gemstone.gemfire.cache.Region;
|
||||
|
||||
/**
|
||||
* The GemfireRepositoryExtension class...
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.data.repository.cdi.CdiRepositoryExtensionSupport
|
||||
* @since 1.8.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class GemfireRepositoryExtension extends CdiRepositoryExtensionSupport {
|
||||
|
||||
protected final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
final Map<Set<Annotation>, Bean<GemfireMappingContext>> mappingContexts =
|
||||
new HashMap<Set<Annotation>, Bean<GemfireMappingContext>>();
|
||||
|
||||
final Set<Bean<Region>> regionBeans = new HashSet<Bean<Region>>();
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public GemfireRepositoryExtension() {
|
||||
logger.info("Activating CDI extension for Spring Data GemFire Repositories");
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of an observer that captures GemFire Region beans defined in the CDI container, storing them
|
||||
* along with any defined GemfireMappingContexts for later construction of the Repository beans.
|
||||
*
|
||||
* @param <X> class type of the bean instance.
|
||||
* @param processBean annotated type as defined by CDI.
|
||||
* @see javax.enterprise.inject.spi.ProcessBean
|
||||
* @see javax.enterprise.event.Observes
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
<X> void processBean(@Observes ProcessBean<X> processBean) {
|
||||
Bean<X> bean = processBean.getBean();
|
||||
|
||||
for (Type type : bean.getTypes()) {
|
||||
Type resolvedType = (type instanceof ParameterizedType ? ((ParameterizedType) type).getRawType() : type);
|
||||
|
||||
if (resolvedType instanceof Class<?>) {
|
||||
Class<?> classType = (Class<?>) resolvedType;
|
||||
|
||||
if (Region.class.isAssignableFrom(classType)) {
|
||||
logger.debug("Found Region bean with name {}", bean.getName());
|
||||
regionBeans.add((Bean<Region>) bean);
|
||||
}
|
||||
else if (GemfireMappingContext.class.isAssignableFrom(classType)) {
|
||||
logger.debug("Discovered {} bean with types {} having qualifiers {}",
|
||||
GemfireMappingContext.class.getName(), bean.getTypes(), bean.getQualifiers());
|
||||
mappingContexts.put(bean.getQualifiers(), (Bean<GemfireMappingContext>) bean);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of an observer that registers beans in the CDI container for the detected Spring Data
|
||||
* Repositories.
|
||||
*
|
||||
* Repository beans are associated to the appropriate GemfireMappingContexts based on their qualifiers.
|
||||
*
|
||||
* @param beanManager the BeanManager instance.
|
||||
* @see javax.enterprise.inject.spi.AfterBeanDiscovery
|
||||
* @see javax.enterprise.inject.spi.BeanManager
|
||||
* @see javax.enterprise.event.Observes
|
||||
*/
|
||||
void afterBeanDiscovery(@Observes AfterBeanDiscovery afterBeanDiscovery, BeanManager beanManager) {
|
||||
for (Map.Entry<Class<?>, Set<Annotation>> entry : getRepositoryTypes()) {
|
||||
Class<?> repositoryType = entry.getKey();
|
||||
Set<Annotation> qualifiers = entry.getValue();
|
||||
|
||||
// Create the bean representing the Repository.
|
||||
CdiRepositoryBean<?> repositoryBean = createRepositoryBean(beanManager, repositoryType, qualifiers);
|
||||
|
||||
logger.info("Registering bean for '{}' with qualifiers {}.", repositoryType.getName(), qualifiers);
|
||||
|
||||
// Register the bean with the extension and the container.
|
||||
registerBean(repositoryBean);
|
||||
afterBeanDiscovery.addBean(repositoryBean);
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
<T> CdiRepositoryBean<T> createRepositoryBean(BeanManager beanManager, Class<T> repositoryType,
|
||||
Set<Annotation> qualifiers) {
|
||||
|
||||
// Determine the GemfireMappingContext bean that matches the qualifiers of the Repository.
|
||||
Bean<GemfireMappingContext> gemfireMappingContextBean = mappingContexts.get(qualifiers);
|
||||
|
||||
// Construct and return a GemFire Repository bean.
|
||||
return new GemfireRepositoryBean<T>(beanManager, repositoryType, qualifiers, getCustomImplementationDetector(),
|
||||
gemfireMappingContextBean, regionBeans);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -55,6 +55,7 @@ public class GemfireRepositoryFactoryBean<T extends Repository<S, ID>, S, ID ext
|
||||
extends RepositoryFactoryBeanSupport<T, S, ID> implements ApplicationContextAware {
|
||||
|
||||
private Iterable<Region<?, ?>> regions;
|
||||
|
||||
private MappingContext<? extends GemfirePersistentEntity<?>, GemfirePersistentProperty> context;
|
||||
|
||||
/**
|
||||
@@ -67,7 +68,6 @@ public class GemfireRepositoryFactoryBean<T extends Repository<S, ID>, S, ID ext
|
||||
@Override
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
|
||||
Collection<Region> regions = applicationContext.getBeansOfType(Region.class).values();
|
||||
this.regions = (Iterable) Collections.unmodifiableCollection(regions);
|
||||
}
|
||||
@@ -80,7 +80,6 @@ public class GemfireRepositoryFactoryBean<T extends Repository<S, ID>, S, ID ext
|
||||
* @see org.springframework.data.mapping.context.MappingContext
|
||||
*/
|
||||
public void setGemfireMappingContext(MappingContext<? extends GemfirePersistentEntity<?>, GemfirePersistentProperty> mappingContext) {
|
||||
|
||||
setMappingContext(mappingContext);
|
||||
this.context = mappingContext;
|
||||
}
|
||||
@@ -118,10 +117,8 @@ public class GemfireRepositoryFactoryBean<T extends Repository<S, ID>, S, ID ext
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
|
||||
|
||||
Assert.state(context != null, "GemfireMappingContext must not be null!");
|
||||
|
||||
super.afterPropertiesSet();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user