diff --git a/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryBean.java b/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryBean.java index ed0422ac5..a65a8f459 100644 --- a/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryBean.java +++ b/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2013 the original author or authors. + * Copyright 2011-2014 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. @@ -28,6 +28,7 @@ import javax.enterprise.context.ApplicationScoped; import javax.enterprise.context.spi.CreationalContext; import javax.enterprise.inject.Alternative; import javax.enterprise.inject.Stereotype; +import javax.enterprise.inject.UnsatisfiedResolutionException; import javax.enterprise.inject.spi.Bean; import javax.enterprise.inject.spi.BeanManager; import javax.enterprise.inject.spi.InjectionPoint; @@ -35,7 +36,11 @@ import javax.enterprise.inject.spi.PassivationCapable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.data.repository.config.CustomRepositoryImplementationDetector; +import org.springframework.data.repository.config.DefaultRepositoryConfiguration; import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; /** @@ -48,10 +53,11 @@ import org.springframework.util.StringUtils; public abstract class CdiRepositoryBean implements Bean, PassivationCapable { private static final Logger LOGGER = LoggerFactory.getLogger(CdiRepositoryBean.class); + private static final CdiRepositoryConfiguration DEFAULT_CONFIGURATION = DefaultCdiRepositoryConfiguration.INSTANCE; private final Set qualifiers; private final Class repositoryType; - private final Bean customImplementationBean; + private final CustomRepositoryImplementationDetector detector; private final BeanManager beanManager; private final String passivationId; @@ -74,11 +80,11 @@ public abstract class CdiRepositoryBean implements Bean, PassivationCapabl * @param qualifiers must not be {@literal null}. * @param repositoryType has to be an interface must not be {@literal null}. * @param beanManager the CDI {@link BeanManager}, must not be {@literal null}. - * @param customImplementationBean the bean for the custom implementation of the - * {@link org.springframework.data.repository.Repository}, can be {@literal null}. + * @param detector detector for the custom repository implementations {@link CustomRepositoryImplementationDetector}, + * can be {@literal null}. */ public CdiRepositoryBean(Set qualifiers, Class repositoryType, BeanManager beanManager, - Bean customImplementationBean) { + CustomRepositoryImplementationDetector detector) { Assert.notNull(qualifiers); Assert.notNull(beanManager); @@ -88,7 +94,7 @@ public abstract class CdiRepositoryBean implements Bean, PassivationCapabl this.qualifiers = qualifiers; this.repositoryType = repositoryType; this.beanManager = beanManager; - this.customImplementationBean = customImplementationBean; + this.detector = detector; this.passivationId = createPassivationId(qualifiers, repositoryType); } @@ -179,7 +185,93 @@ public abstract class CdiRepositoryBean implements Bean, PassivationCapabl creationalContext.release(); } - /* + /** + * Looks up an instance of a {@link CdiRepositoryConfiguration}. In case the instance cannot be found within the CDI + * scope, a default configuration is used. + * + * @return an available CdiRepositoryConfiguration instance or a default configuration. + */ + @SuppressWarnings("unchecked") + protected CdiRepositoryConfiguration lookupConfiguration(BeanManager beanManager, Set qualifiers) { + + Set> beans = beanManager.getBeans(CdiRepositoryConfiguration.class, getQualifiersArray(qualifiers)); + + if (beans.isEmpty()) { + return DEFAULT_CONFIGURATION; + } + + Bean bean = (Bean) beans.iterator().next(); + return getDependencyInstance(bean, (Class) bean.getBeanClass()); + } + + /** + * Try to lookup a custom implementation for a {@link org.springframework.data.repository.Repository}. Can only be + * used when a {@code CustomRepositoryImplementationDetector} is provided. + * + * @param repositoryType + * @param beanManager + * @param qualifiers + * @return the custom implementation instance or null + */ + private Bean getCustomImplementationBean(Class repositoryType, BeanManager beanManager, + Set qualifiers) { + + if (detector == null) { + return null; + } + + CdiRepositoryConfiguration cdiRepositoryConfiguration = lookupConfiguration(beanManager, qualifiers); + Class customImplementationClass = getCustomImplementationClass(repositoryType, cdiRepositoryConfiguration); + + if (customImplementationClass == null) { + return null; + } + + Set> beans = beanManager.getBeans(customImplementationClass, getQualifiersArray(qualifiers)); + return beans.isEmpty() ? null : beans.iterator().next(); + } + + /** + * Retrieves a custom repository interfaces from a repository type. This works for the whole class hierarchy and can + * find also a custom repo which is inherieted over many levels. + * + * @param repositoryType The class representing the repository. + * @param cdiRepositoryConfiguration The configuration for CDI usage. + * @return the interface class or {@literal null}. + */ + private Class getCustomImplementationClass(Class repositoryType, + CdiRepositoryConfiguration cdiRepositoryConfiguration) { + + String className = getCustomImplementationClassName(repositoryType, cdiRepositoryConfiguration); + AbstractBeanDefinition beanDefinition = detector.detectCustomImplementation(className, + Collections.singleton(repositoryType.getPackage().getName())); + + if (beanDefinition == null) { + return null; + } + + try { + return Class.forName(beanDefinition.getBeanClassName()); + } catch (ClassNotFoundException e) { + throw new UnsatisfiedResolutionException(String.format("Unable to resolve class for '%s'", + beanDefinition.getBeanClassName()), e); + } + } + + private String getCustomImplementationClassName(Class repositoryType, + CdiRepositoryConfiguration cdiRepositoryConfiguration) { + + String configuredPostfix = cdiRepositoryConfiguration.getRepositoryImplementationPostfix(); + Assert.hasText(configuredPostfix, "Configured repository postfix must not be null or empty!"); + + return ClassUtils.getShortName(repositoryType) + configuredPostfix; + } + + private Annotation[] getQualifiersArray(Set qualifiers) { + return qualifiers.toArray(new Annotation[qualifiers.size()]); + } + + /* * (non-Javadoc) * @see javax.enterprise.inject.spi.Bean#getQualifiers() */ @@ -253,7 +345,7 @@ public abstract class CdiRepositoryBean implements Bean, PassivationCapabl return ApplicationScoped.class; } - /* + /* * (non-Javadoc) * @see javax.enterprise.inject.spi.PassivationCapable#getId() */ @@ -272,6 +364,7 @@ public abstract class CdiRepositoryBean implements Bean, PassivationCapabl @Deprecated protected T create(CreationalContext creationalContext, Class repositoryType) { + Bean customImplementationBean = getCustomImplementationBean(repositoryType, beanManager, qualifiers); Object customImplementation = customImplementationBean == null ? null : beanManager.getReference( customImplementationBean, customImplementationBean.getBeanClass(), beanManager.createCreationalContext(customImplementationBean)); @@ -292,13 +385,27 @@ public abstract class CdiRepositoryBean implements Bean, PassivationCapabl + "in order to use custom repository implementations"); } - /* + /* * (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return String - .format("JpaRepositoryBean: type='%s', qualifiers=%s", repositoryType.getName(), qualifiers.toString()); + .format("CdiRepositoryBean: type='%s', qualifiers=%s", repositoryType.getName(), qualifiers.toString()); + } + + static enum DefaultCdiRepositoryConfiguration implements CdiRepositoryConfiguration { + + INSTANCE; + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.cdi.CdiRepositoryConfiguration#getRepositoryImplementationPostfix() + */ + @Override + public String getRepositoryImplementationPostfix() { + return DefaultRepositoryConfiguration.DEFAULT_REPOSITORY_IMPLEMENTATION_POSTFIX; + } } } diff --git a/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryExtensionSupport.java b/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryExtensionSupport.java index 2baba6fb8..05c00c14d 100644 --- a/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryExtensionSupport.java +++ b/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryExtensionSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2013 the original author or authors. + * Copyright 2011-2014 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. @@ -16,21 +16,17 @@ package org.springframework.data.repository.cdi; import java.lang.annotation.Annotation; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import javax.enterprise.context.spi.CreationalContext; import javax.enterprise.event.Observes; import javax.enterprise.inject.Any; import javax.enterprise.inject.Default; -import javax.enterprise.inject.UnsatisfiedResolutionException; import javax.enterprise.inject.spi.AfterDeploymentValidation; import javax.enterprise.inject.spi.AnnotatedType; -import javax.enterprise.inject.spi.Bean; import javax.enterprise.inject.spi.BeanManager; import javax.enterprise.inject.spi.Extension; import javax.enterprise.inject.spi.ProcessAnnotatedType; @@ -39,7 +35,6 @@ import javax.inject.Qualifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.env.Environment; import org.springframework.core.env.StandardEnvironment; @@ -51,9 +46,6 @@ import org.springframework.data.repository.NoRepositoryBean; import org.springframework.data.repository.Repository; import org.springframework.data.repository.RepositoryDefinition; import org.springframework.data.repository.config.CustomRepositoryImplementationDetector; -import org.springframework.data.repository.config.DefaultRepositoryConfiguration; -import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; /** * Base class for {@link Extension} implementations that create instances for Spring Data repositories. @@ -65,7 +57,6 @@ import org.springframework.util.ClassUtils; public abstract class CdiRepositoryExtensionSupport implements Extension { private static final Logger LOGGER = LoggerFactory.getLogger(CdiRepositoryExtensionSupport.class); - private static final CdiRepositoryConfiguration DEFAULT_CONFIGURATION = DefaultCdiRepositoryConfiguration.INSTANCE; private final Map, Set> repositoryTypes = new HashMap, Set>(); private final Set> eagerRepositories = new HashSet>(); @@ -189,86 +180,10 @@ public abstract class CdiRepositoryExtensionSupport implements Extension { } /** - * Looks up an instance of a {@link CdiRepositoryConfiguration}. In case the instance cannot be found within the CDI - * scope, a default configuration is used. - * - * @return an available CdiRepositoryConfiguration instance or a default configuration. + * @return the {@link CustomRepositoryImplementationDetector} to scan for the custom implementation */ - protected CdiRepositoryConfiguration lookupConfiguration(BeanManager beanManager, Set qualifiers) { - - Set> beans = beanManager.getBeans(CdiRepositoryConfiguration.class, getQualifiersArray(qualifiers)); - - if (beans.isEmpty()) { - return DEFAULT_CONFIGURATION; - } - - Bean bean = beans.iterator().next(); - CreationalContext creationalContext = beanManager.createCreationalContext(bean); - - return (CdiRepositoryConfiguration) beanManager.getReference(bean, CdiRepositoryConfiguration.class, - creationalContext); - } - - private Annotation[] getQualifiersArray(Set qualifiers) { - return qualifiers.toArray(new Annotation[qualifiers.size()]); - } - - /** - * Try to lookup a custom implementation for a {@link org.springframework.data.repository.Repository}. - * - * @param repositoryType - * @param beanManager - * @param qualifiers - * @return the custom implementation instance or null - */ - protected Bean getCustomImplementationBean(Class repositoryType, BeanManager beanManager, - Set qualifiers) { - - CdiRepositoryConfiguration cdiRepositoryConfiguration = lookupConfiguration(beanManager, qualifiers); - Class customImplementationClass = getCustomImplementationClass(repositoryType, cdiRepositoryConfiguration); - - if (customImplementationClass == null) { - return null; - } - - Set> beans = beanManager.getBeans(customImplementationClass, getQualifiersArray(qualifiers)); - return beans.isEmpty() ? null : beans.iterator().next(); - } - - /** - * Retrieves a custom repository interfaces from a repository type. This works for the whole class hierarchy and can - * find also a custom repo which is inherieted over many levels. - * - * @param repositoryType The class representing the repository. - * @param cdiRepositoryConfiguration The configuration for CDI usage. - * @return the interface class or {@literal null}. - */ - private Class getCustomImplementationClass(Class repositoryType, - CdiRepositoryConfiguration cdiRepositoryConfiguration) { - - String className = getCustomImplementationClassName(repositoryType, cdiRepositoryConfiguration); - AbstractBeanDefinition beanDefinition = customImplementationDetector.detectCustomImplementation(className, - Collections.singleton(repositoryType.getPackage().getName())); - - if (beanDefinition == null) { - return null; - } - - try { - return Class.forName(beanDefinition.getBeanClassName()); - } catch (ClassNotFoundException e) { - throw new UnsatisfiedResolutionException(String.format("Unable to resolve class for '%s'", - beanDefinition.getBeanClassName()), e); - } - } - - private String getCustomImplementationClassName(Class repositoryType, - CdiRepositoryConfiguration cdiRepositoryConfiguration) { - - String configuredPostfix = cdiRepositoryConfiguration.getRepositoryImplementationPostfix(); - Assert.hasText(configuredPostfix, "Configured repository postfix must not be null or empty!"); - - return ClassUtils.getShortName(repositoryType) + configuredPostfix; + protected CustomRepositoryImplementationDetector getCustomImplementationDetector() { + return customImplementationDetector; } @SuppressWarnings("all") @@ -285,17 +200,4 @@ public abstract class CdiRepositoryExtensionSupport implements Extension { private static final AnyAnnotationLiteral INSTANCE = new AnyAnnotationLiteral(); } - static enum DefaultCdiRepositoryConfiguration implements CdiRepositoryConfiguration { - - INSTANCE; - - /* - * (non-Javadoc) - * @see org.springframework.data.repository.cdi.CdiRepositoryConfiguration#getRepositoryImplementationPostfix() - */ - @Override - public String getRepositoryImplementationPostfix() { - return DefaultRepositoryConfiguration.DEFAULT_REPOSITORY_IMPLEMENTATION_POSTFIX; - } - } } diff --git a/src/test/java/org/springframework/data/repository/cdi/DummyCdiExtension.java b/src/test/java/org/springframework/data/repository/cdi/DummyCdiExtension.java index 0450b06d9..8e85b76dc 100644 --- a/src/test/java/org/springframework/data/repository/cdi/DummyCdiExtension.java +++ b/src/test/java/org/springframework/data/repository/cdi/DummyCdiExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2011 the original author or authors. + * Copyright 2011-2014 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. @@ -27,29 +27,31 @@ import javax.enterprise.context.spi.Contextual; import javax.enterprise.context.spi.CreationalContext; import javax.enterprise.event.Observes; import javax.enterprise.inject.spi.AfterBeanDiscovery; -import javax.enterprise.inject.spi.Bean; import javax.enterprise.inject.spi.BeanManager; import org.apache.webbeans.context.AbstractContext; import org.apache.webbeans.context.creational.BeanInstanceBag; import org.mockito.Mockito; +import org.springframework.data.repository.config.CustomRepositoryImplementationDetector; /** * Dummy extension of {@link CdiRepositoryExtensionSupport} to allow integration tests. Will create mocks for repository * interfaces being found. * * @author Oliver Gierke + * @author Mark Paluch */ public class DummyCdiExtension extends CdiRepositoryExtensionSupport { @SuppressWarnings({ "rawtypes", "unchecked" }) void afterBeanDiscovery(@Observes AfterBeanDiscovery afterBeanDiscovery, BeanManager beanManager) { + afterBeanDiscovery.addContext(new MyCustomScope()); + for (Entry, Set> type : getRepositoryTypes()) { - Bean customImplementation = getCustomImplementationBean(type.getKey(), beanManager, type.getValue()); DummyCdiRepositoryBean bean = new DummyCdiRepositoryBean(type.getValue(), type.getKey(), beanManager, - customImplementation); + getCustomImplementationDetector()); registerBean(bean); afterBeanDiscovery.addBean(bean); } @@ -62,8 +64,8 @@ public class DummyCdiExtension extends CdiRepositoryExtensionSupport { } DummyCdiRepositoryBean(Set qualifiers, Class repositoryType, BeanManager beanManager, - Bean customImplementation) { - super(qualifiers, repositoryType, beanManager, customImplementation); + CustomRepositoryImplementationDetector detector) { + super(qualifiers, repositoryType, beanManager, detector); } public Class getScope() {