diff --git a/Spring Data Commons.sonargraph b/Spring Data Commons.sonargraph index 093ca1591..171fe2174 100644 --- a/Spring Data Commons.sonargraph +++ b/Spring Data Commons.sonargraph @@ -1,5 +1,5 @@ - + @@ -66,6 +66,12 @@ + + + + + + @@ -73,6 +79,7 @@ + @@ -82,12 +89,6 @@ - - - - - - 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 91ef6d841..ed0422ac5 100644 --- a/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryBean.java +++ b/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryBean.java @@ -51,7 +51,7 @@ public abstract class CdiRepositoryBean implements Bean, PassivationCapabl private final Set qualifiers; private final Class repositoryType; - private final Object customImplementation; + private final Bean customImplementationBean; private final BeanManager beanManager; private final String passivationId; @@ -74,9 +74,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 customImplementation the custom implementation of the {@link org.springframework.data.repository.Repository}, can be {@literal null}. + * @param customImplementationBean the bean for the custom implementation of the + * {@link org.springframework.data.repository.Repository}, can be {@literal null}. */ - public CdiRepositoryBean(Set qualifiers, Class repositoryType, BeanManager beanManager, Object customImplementation) { + public CdiRepositoryBean(Set qualifiers, Class repositoryType, BeanManager beanManager, + Bean customImplementationBean) { Assert.notNull(qualifiers); Assert.notNull(beanManager); @@ -86,7 +88,7 @@ public abstract class CdiRepositoryBean implements Bean, PassivationCapabl this.qualifiers = qualifiers; this.repositoryType = repositoryType; this.beanManager = beanManager; - this.customImplementation = customImplementation; + this.customImplementationBean = customImplementationBean; this.passivationId = createPassivationId(qualifiers, repositoryType); } @@ -265,8 +267,15 @@ public abstract class CdiRepositoryBean implements Bean, PassivationCapabl * @param creationalContext will never be {@literal null}. * @param repositoryType will never be {@literal null}. * @return + * @deprecated overide {@link #create(CreationalContext, Class, Object)} instead. */ + @Deprecated protected T create(CreationalContext creationalContext, Class repositoryType) { + + Object customImplementation = customImplementationBean == null ? null : beanManager.getReference( + customImplementationBean, customImplementationBean.getBeanClass(), + beanManager.createCreationalContext(customImplementationBean)); + return create(creationalContext, repositoryType, customImplementation); } @@ -279,8 +288,8 @@ public abstract class CdiRepositoryBean implements Bean, PassivationCapabl * @return */ protected T create(CreationalContext creationalContext, Class repositoryType, Object customImplementation) { - throw new UnsupportedOperationException("You have to implement create(CreationalContext, Class, Object) " + - "in order to use custom repository implementations"); + throw new UnsupportedOperationException("You have to implement create(CreationalContext, Class, Object) " + + "in order to use custom repository implementations"); } /* @@ -289,6 +298,7 @@ public abstract class CdiRepositoryBean implements Bean, PassivationCapabl */ @Override public String toString() { - return String.format("JpaRepositoryBean: type='%s', qualifiers=%s", repositoryType.getName(), qualifiers.toString()); + return String + .format("JpaRepositoryBean: type='%s', qualifiers=%s", repositoryType.getName(), qualifiers.toString()); } } diff --git a/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryConfigurationSource.java b/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryConfiguration.java similarity index 95% rename from src/main/java/org/springframework/data/repository/cdi/CdiRepositoryConfigurationSource.java rename to src/main/java/org/springframework/data/repository/cdi/CdiRepositoryConfiguration.java index 6e6f5271c..97f9ad29b 100644 --- a/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryConfigurationSource.java +++ b/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryConfiguration.java @@ -21,7 +21,7 @@ package org.springframework.data.repository.cdi; * * @author Mark Paluch */ -public interface CdiRepositoryConfigurationSource { +public interface CdiRepositoryConfiguration { /** * Returns the configured postfix to be used for looking up custom implementation classes. 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 ad97517a4..2baba6fb8 100644 --- a/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryExtensionSupport.java +++ b/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryExtensionSupport.java @@ -15,17 +15,27 @@ */ 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.*; +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; import javax.enterprise.util.AnnotationLiteral; import javax.inject.Qualifier; -import java.lang.annotation.Annotation; -import java.util.*; -import java.util.Map.Entry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,8 +52,8 @@ 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; -import org.springframework.util.StringUtils; /** * Base class for {@link Extension} implementations that create instances for Spring Data repositories. @@ -55,17 +65,20 @@ import org.springframework.util.StringUtils; 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>(); private final CustomRepositoryImplementationDetector customImplementationDetector; protected CdiRepositoryExtensionSupport() { + Environment environment = new StandardEnvironment(); ResourceLoader resourceLoader = new PathMatchingResourcePatternResolver(getClass().getClassLoader()); MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader); - customImplementationDetector = new CustomRepositoryImplementationDetector(metadataReaderFactory, - environment, resourceLoader); + + this.customImplementationDetector = new CustomRepositoryImplementationDetector(metadataReaderFactory, environment, + resourceLoader); } /** @@ -176,26 +189,24 @@ public abstract class CdiRepositoryExtensionSupport implements Extension { } /** - * Looks up an instance of a {@link CdiRepositoryConfigurationSource}. In case the instance cannot be found within - * the CDI scope, a default configuration is used. + * 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 CdiRepositoryConfigurationSource instance or a default configuration. + * @return an available CdiRepositoryConfiguration instance or a default configuration. */ - protected CdiRepositoryConfigurationSource lookupConfiguration(BeanManager beanManager, Set qualifiers) { - Set> beans = beanManager.getBeans(CdiRepositoryConfigurationSource.class, getQualifiersArray(qualifiers)); + protected CdiRepositoryConfiguration lookupConfiguration(BeanManager beanManager, Set qualifiers) { + + Set> beans = beanManager.getBeans(CdiRepositoryConfiguration.class, getQualifiersArray(qualifiers)); + if (beans.isEmpty()) { - // no own defined type since these would be picked up by CDI by default. - return new CdiRepositoryConfigurationSource() { - @Override - public String getRepositoryImplementationPostfix() { - return DefaultRepositoryConfiguration.DEFAULT_REPOSITORY_IMPLEMENTATION_POSTFIX; - } - }; + return DEFAULT_CONFIGURATION; } Bean bean = beans.iterator().next(); CreationalContext creationalContext = beanManager.createCreationalContext(bean); - return (CdiRepositoryConfigurationSource) beanManager.getReference(bean, CdiRepositoryConfigurationSource.class, creationalContext); + + return (CdiRepositoryConfiguration) beanManager.getReference(bean, CdiRepositoryConfiguration.class, + creationalContext); } private Annotation[] getQualifiersArray(Set qualifiers) { @@ -210,52 +221,54 @@ public abstract class CdiRepositoryExtensionSupport implements Extension { * @param qualifiers * @return the custom implementation instance or null */ - protected Object findCustomImplementation(Class repositoryType, BeanManager beanManager, Set qualifiers) { + protected Bean getCustomImplementationBean(Class repositoryType, BeanManager beanManager, + Set qualifiers) { - CdiRepositoryConfigurationSource cdiRepositoryConfiguration = lookupConfiguration(beanManager, qualifiers); - Class customImplementationClass = findCustomImplementationClass(repositoryType, cdiRepositoryConfiguration); - if (customImplementationClass != null) { - Set> beans = beanManager.getBeans(customImplementationClass, getQualifiersArray(qualifiers)); - if (!beans.isEmpty()) { - Bean bean = beans.iterator().next(); - CreationalContext creationalContext = beanManager.createCreationalContext(bean); - return beanManager.getReference(bean, customImplementationClass, creationalContext); - } + CdiRepositoryConfiguration cdiRepositoryConfiguration = lookupConfiguration(beanManager, qualifiers); + Class customImplementationClass = getCustomImplementationClass(repositoryType, cdiRepositoryConfiguration); + + if (customImplementationClass == null) { + return 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. + * 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 null. + * @return the interface class or {@literal null}. */ - protected Class findCustomImplementationClass(Class repositoryType, CdiRepositoryConfigurationSource cdiRepositoryConfiguration) { + private Class getCustomImplementationClass(Class repositoryType, + CdiRepositoryConfiguration cdiRepositoryConfiguration) { - String className = constructCustomImplementationClassName(repositoryType, cdiRepositoryConfiguration); + String className = getCustomImplementationClassName(repositoryType, cdiRepositoryConfiguration); AbstractBeanDefinition beanDefinition = customImplementationDetector.detectCustomImplementation(className, Collections.singleton(repositoryType.getPackage().getName())); - if (beanDefinition != null) { - try { - return Class.forName(beanDefinition.getBeanClassName()); - } catch (ClassNotFoundException e) { - throw new UnsatisfiedResolutionException(String.format("Unable to resolve class for '%s'", - beanDefinition.getBeanClassName()), e); - } + if (beanDefinition == null) { + return 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 constructCustomImplementationClassName(Class repositoryType, CdiRepositoryConfigurationSource cdiRepositoryConfiguration) { + private String getCustomImplementationClassName(Class repositoryType, + CdiRepositoryConfiguration cdiRepositoryConfiguration) { + String configuredPostfix = cdiRepositoryConfiguration.getRepositoryImplementationPostfix(); - String implementationPostfix = StringUtils.hasText(configuredPostfix) ? configuredPostfix : DefaultRepositoryConfiguration.DEFAULT_REPOSITORY_IMPLEMENTATION_POSTFIX; - return ClassUtils.getShortName(repositoryType) + implementationPostfix; + Assert.hasText(configuredPostfix, "Configured repository postfix must not be null or empty!"); + + return ClassUtils.getShortName(repositoryType) + configuredPostfix; } @SuppressWarnings("all") @@ -271,4 +284,18 @@ public abstract class CdiRepositoryExtensionSupport implements Extension { private static final long serialVersionUID = 7261821376671361463L; 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/main/java/org/springframework/data/repository/config/CustomRepositoryImplementationDetector.java b/src/main/java/org/springframework/data/repository/config/CustomRepositoryImplementationDetector.java index ebdff8037..e4b5262f3 100644 --- a/src/main/java/org/springframework/data/repository/config/CustomRepositoryImplementationDetector.java +++ b/src/main/java/org/springframework/data/repository/config/CustomRepositoryImplementationDetector.java @@ -35,81 +35,82 @@ import org.springframework.util.StringUtils; /** * Detects the custom implementation for a {@link org.springframework.data.repository.Repository} * + * @author Oliver Gierke * @author Mark Paluch */ public class CustomRepositoryImplementationDetector { - private static final String CUSTOM_IMPLEMENTATION_RESOURCE_PATTERN = "**/*%s.class"; + private static final String CUSTOM_IMPLEMENTATION_RESOURCE_PATTERN = "**/*%s.class"; - private MetadataReaderFactory metadataReaderFactory; - private Environment environment; - private ResourceLoader resourceLoader; + private final MetadataReaderFactory metadataReaderFactory; + private final Environment environment; + private final ResourceLoader resourceLoader; /** - * - * Creates a new {@link CustomRepositoryImplementationDetector} from the given {@link org.springframework.core.type.classreading.MetadataReaderFactory}, + * Creates a new {@link CustomRepositoryImplementationDetector} from the given + * {@link org.springframework.core.type.classreading.MetadataReaderFactory}, * {@link org.springframework.core.env.Environment} and {@link org.springframework.core.io.ResourceLoader}. * * @param metadataReaderFactory must not be {@literal null}. * @param environment must not be {@literal null}. * @param resourceLoader must not be {@literal null}. */ - public CustomRepositoryImplementationDetector(MetadataReaderFactory metadataReaderFactory, Environment environment, - ResourceLoader resourceLoader) { + public CustomRepositoryImplementationDetector(MetadataReaderFactory metadataReaderFactory, Environment environment, + ResourceLoader resourceLoader) { - Assert.notNull(metadataReaderFactory, "MetadataReaderFactory must not be null!"); - Assert.notNull(resourceLoader, "ResourceLoader must not be null!"); - Assert.notNull(environment, "Environment must not be null!"); + Assert.notNull(metadataReaderFactory, "MetadataReaderFactory must not be null!"); + Assert.notNull(resourceLoader, "ResourceLoader must not be null!"); + Assert.notNull(environment, "Environment must not be null!"); - this.metadataReaderFactory = metadataReaderFactory; - this.environment = environment; - this.resourceLoader = resourceLoader; - } + this.metadataReaderFactory = metadataReaderFactory; + this.environment = environment; + this.resourceLoader = resourceLoader; + } - /** - * Tries to detect a custom implementation for a repository bean by classpath scanning. - * - * @param className must not be {@literal null}. - * @param basePackages must not be {@literal null}. - * @return the {@code AbstractBeanDefinition} of the custom implementation or {@literal null} if none found - */ - public AbstractBeanDefinition detectCustomImplementation(String className, Iterable basePackages) { + /** + * Tries to detect a custom implementation for a repository bean by classpath scanning. + * + * @param className must not be {@literal null}. + * @param basePackages must not be {@literal null}. + * @return the {@code AbstractBeanDefinition} of the custom implementation or {@literal null} if none found + */ + public AbstractBeanDefinition detectCustomImplementation(String className, Iterable basePackages) { - Assert.notNull(className, "ClassName must not be null!"); - Assert.notNull(basePackages, "BasePackages must not be null!"); + Assert.notNull(className, "ClassName must not be null!"); + Assert.notNull(basePackages, "BasePackages must not be null!"); - // Build pattern to lookup implementation class - Pattern pattern = Pattern.compile(".*\\." + className); + // Build pattern to lookup implementation class + Pattern pattern = Pattern.compile(".*\\." + className); - // Build classpath scanner and lookup bean definition - ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false); - provider.setEnvironment(environment); - provider.setResourceLoader(resourceLoader); - provider.setResourcePattern(String.format(CUSTOM_IMPLEMENTATION_RESOURCE_PATTERN, className)); - provider.setMetadataReaderFactory(metadataReaderFactory); - provider.addIncludeFilter(new RegexPatternTypeFilter(pattern)); + // Build classpath scanner and lookup bean definition + ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false); + provider.setEnvironment(environment); + provider.setResourceLoader(resourceLoader); + provider.setResourcePattern(String.format(CUSTOM_IMPLEMENTATION_RESOURCE_PATTERN, className)); + provider.setMetadataReaderFactory(metadataReaderFactory); + provider.addIncludeFilter(new RegexPatternTypeFilter(pattern)); - Set definitions = new HashSet(); + Set definitions = new HashSet(); - for (String basePackage : basePackages) { - definitions.addAll(provider.findCandidateComponents(basePackage)); - } + for (String basePackage : basePackages) { + definitions.addAll(provider.findCandidateComponents(basePackage)); + } - if (definitions.isEmpty()) { - return null; - } + if (definitions.isEmpty()) { + return null; + } - if (definitions.size() == 1) { - return (AbstractBeanDefinition) definitions.iterator().next(); - } + if (definitions.size() == 1) { + return (AbstractBeanDefinition) definitions.iterator().next(); + } - List implementationClassNames = new ArrayList(); - for (BeanDefinition bean : definitions) { - implementationClassNames.add(bean.getBeanClassName()); - } + List implementationClassNames = new ArrayList(); + for (BeanDefinition bean : definitions) { + implementationClassNames.add(bean.getBeanClassName()); + } - throw new IllegalStateException(String.format( - "Ambiguous custom implementations detected! Found %s but expected a single implementation!", - StringUtils.collectionToCommaDelimitedString(implementationClassNames))); - } + throw new IllegalStateException(String.format( + "Ambiguous custom implementations detected! Found %s but expected a single implementation!", + StringUtils.collectionToCommaDelimitedString(implementationClassNames))); + } } 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 54d5bf9de..0450b06d9 100644 --- a/src/test/java/org/springframework/data/repository/cdi/DummyCdiExtension.java +++ b/src/test/java/org/springframework/data/repository/cdi/DummyCdiExtension.java @@ -15,12 +15,6 @@ */ package org.springframework.data.repository.cdi; -import javax.enterprise.context.NormalScope; -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.BeanManager; import java.lang.annotation.Annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -28,6 +22,14 @@ import java.util.HashMap; import java.util.Map.Entry; import java.util.Set; +import javax.enterprise.context.NormalScope; +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; @@ -40,15 +42,14 @@ import org.mockito.Mockito; */ public class DummyCdiExtension extends CdiRepositoryExtensionSupport { - @SuppressWarnings({"rawtypes", "unchecked"}) + @SuppressWarnings({ "rawtypes", "unchecked" }) void afterBeanDiscovery(@Observes AfterBeanDiscovery afterBeanDiscovery, BeanManager beanManager) { afterBeanDiscovery.addContext(new MyCustomScope()); for (Entry, Set> type : getRepositoryTypes()) { - CdiRepositoryConfigurationSource configurationSource = lookupConfiguration(beanManager, type.getValue()); - Object customImplementation = findCustomImplementation(type.getKey(), beanManager, type.getValue()); - - DummyCdiRepositoryBean bean = new DummyCdiRepositoryBean(type.getValue(), type.getKey(), beanManager, customImplementation); + Bean customImplementation = getCustomImplementationBean(type.getKey(), beanManager, type.getValue()); + DummyCdiRepositoryBean bean = new DummyCdiRepositoryBean(type.getValue(), type.getKey(), beanManager, + customImplementation); registerBean(bean); afterBeanDiscovery.addBean(bean); } @@ -60,7 +61,8 @@ public class DummyCdiExtension extends CdiRepositoryExtensionSupport { super(qualifiers, repositoryType, beanManager); } - DummyCdiRepositoryBean(Set qualifiers, Class repositoryType, BeanManager beanManager, Object customImplementation) { + DummyCdiRepositoryBean(Set qualifiers, Class repositoryType, BeanManager beanManager, + Bean customImplementation) { super(qualifiers, repositoryType, beanManager, customImplementation); } @@ -80,6 +82,7 @@ public class DummyCdiExtension extends CdiRepositoryExtensionSupport { } + @SuppressWarnings("serial") static class MyCustomScope extends AbstractContext { MyCustomScope() {