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 448b597e9..91ef6d841 100644 --- a/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryBean.java +++ b/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryBean.java @@ -43,6 +43,7 @@ import org.springframework.util.StringUtils; * * @author Dirk Mahler * @author Oliver Gierke + * @author Mark Paluch */ public abstract class CdiRepositoryBean implements Bean, PassivationCapable { @@ -50,6 +51,7 @@ public abstract class CdiRepositoryBean implements Bean, PassivationCapabl private final Set qualifiers; private final Class repositoryType; + private final Object customImplementation; private final BeanManager beanManager; private final String passivationId; @@ -63,6 +65,18 @@ public abstract class CdiRepositoryBean implements Bean, PassivationCapabl * @param beanManager the CDI {@link BeanManager}, must not be {@literal null}. */ public CdiRepositoryBean(Set qualifiers, Class repositoryType, BeanManager beanManager) { + this(qualifiers, repositoryType, beanManager, null); + } + + /** + * Creates a new {@link CdiRepositoryBean}. + * + * @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}. + */ + public CdiRepositoryBean(Set qualifiers, Class repositoryType, BeanManager beanManager, Object customImplementation) { Assert.notNull(qualifiers); Assert.notNull(beanManager); @@ -72,6 +86,7 @@ public abstract class CdiRepositoryBean implements Bean, PassivationCapabl this.qualifiers = qualifiers; this.repositoryType = repositoryType; this.beanManager = beanManager; + this.customImplementation = customImplementation; this.passivationId = createPassivationId(qualifiers, repositoryType); } @@ -162,7 +177,7 @@ public abstract class CdiRepositoryBean implements Bean, PassivationCapabl creationalContext.release(); } - /* + /* * (non-Javadoc) * @see javax.enterprise.inject.spi.Bean#getQualifiers() */ @@ -228,7 +243,7 @@ public abstract class CdiRepositoryBean implements Bean, PassivationCapabl return Collections.emptySet(); } - /* + /* * (non-Javadoc) * @see javax.enterprise.inject.spi.Bean#getScope() */ @@ -251,15 +266,29 @@ public abstract class CdiRepositoryBean implements Bean, PassivationCapabl * @param repositoryType will never be {@literal null}. * @return */ - protected abstract T create(CreationalContext creationalContext, Class repositoryType); + protected T create(CreationalContext creationalContext, Class repositoryType) { + return create(creationalContext, repositoryType, customImplementation); + } - /* + /** + * Creates the actual component instance. + * + * @param creationalContext will never be {@literal null}. + * @param repositoryType will never be {@literal null}. + * @param customImplementation can be {@literal null}. + * @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"); + } + + /* * (non-Javadoc) * @see java.lang.Object#toString() */ @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/CdiRepositoryConfigurationSource.java new file mode 100644 index 000000000..6e6f5271c --- /dev/null +++ b/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryConfigurationSource.java @@ -0,0 +1,32 @@ +/* + * Copyright 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. + * 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.repository.cdi; + +/** + * Interface containing the configurable options for the Spring Data repository subsystem using CDI. + * + * @author Mark Paluch + */ +public interface CdiRepositoryConfigurationSource { + + /** + * Returns the configured postfix to be used for looking up custom implementation classes. + * + * @return the postfix to use or {@literal null} in case none is configured. + */ + String getRepositoryImplementationPostfix(); +} 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 df211f8a5..ad97517a4 100644 --- a/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryExtensionSupport.java +++ b/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryExtensionSupport.java @@ -15,36 +15,42 @@ */ package org.springframework.data.repository.cdi; -import java.lang.annotation.Annotation; -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.spi.AfterDeploymentValidation; -import javax.enterprise.inject.spi.AnnotatedType; -import javax.enterprise.inject.spi.BeanManager; -import javax.enterprise.inject.spi.Extension; -import javax.enterprise.inject.spi.ProcessAnnotatedType; +import javax.enterprise.inject.UnsatisfiedResolutionException; +import javax.enterprise.inject.spi.*; 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; +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; +import org.springframework.core.io.ResourceLoader; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.type.classreading.CachingMetadataReaderFactory; +import org.springframework.core.type.classreading.MetadataReaderFactory; 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.ClassUtils; +import org.springframework.util.StringUtils; /** * Base class for {@link Extension} implementations that create instances for Spring Data repositories. * * @author Dirk Mahler * @author Oliver Gierke + * @author Mark Paluch */ public abstract class CdiRepositoryExtensionSupport implements Extension { @@ -52,6 +58,15 @@ public abstract class CdiRepositoryExtensionSupport implements Extension { 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); + } /** * Implementation of a an observer which checks for Spring Data repository types and stores them in @@ -160,6 +175,89 @@ 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. + * + * @return an available CdiRepositoryConfigurationSource instance or a default configuration. + */ + protected CdiRepositoryConfigurationSource lookupConfiguration(BeanManager beanManager, Set qualifiers) { + Set> beans = beanManager.getBeans(CdiRepositoryConfigurationSource.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; + } + }; + } + + Bean bean = beans.iterator().next(); + CreationalContext creationalContext = beanManager.createCreationalContext(bean); + return (CdiRepositoryConfigurationSource) beanManager.getReference(bean, CdiRepositoryConfigurationSource.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 Object findCustomImplementation(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); + } + } + + return null; + } + + /** + * 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. + */ + protected Class findCustomImplementationClass(Class repositoryType, CdiRepositoryConfigurationSource cdiRepositoryConfiguration) { + + String className = constructCustomImplementationClassName(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); + } + } + + return null; + } + + private String constructCustomImplementationClassName(Class repositoryType, CdiRepositoryConfigurationSource cdiRepositoryConfiguration) { + String configuredPostfix = cdiRepositoryConfiguration.getRepositoryImplementationPostfix(); + String implementationPostfix = StringUtils.hasText(configuredPostfix) ? configuredPostfix : DefaultRepositoryConfiguration.DEFAULT_REPOSITORY_IMPLEMENTATION_POSTFIX; + return ClassUtils.getShortName(repositoryType) + implementationPostfix; + } + @SuppressWarnings("all") static class DefaultAnnotationLiteral extends AnnotationLiteral implements Default { diff --git a/src/main/java/org/springframework/data/repository/config/CustomRepositoryImplementationDetector.java b/src/main/java/org/springframework/data/repository/config/CustomRepositoryImplementationDetector.java new file mode 100644 index 000000000..ebdff8037 --- /dev/null +++ b/src/main/java/org/springframework/data/repository/config/CustomRepositoryImplementationDetector.java @@ -0,0 +1,115 @@ +/* + * Copyright 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. + * 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.repository.config; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; +import org.springframework.core.env.Environment; +import org.springframework.core.io.ResourceLoader; +import org.springframework.core.type.classreading.MetadataReaderFactory; +import org.springframework.core.type.filter.RegexPatternTypeFilter; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * Detects the custom implementation for a {@link org.springframework.data.repository.Repository} + * + * @author Mark Paluch + */ +public class CustomRepositoryImplementationDetector { + + private static final String CUSTOM_IMPLEMENTATION_RESOURCE_PATTERN = "**/*%s.class"; + + private MetadataReaderFactory metadataReaderFactory; + private Environment environment; + private ResourceLoader resourceLoader; + + /** + * + * 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) { + + 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; + } + + /** + * 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!"); + + // 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)); + + Set definitions = new HashSet(); + + for (String basePackage : basePackages) { + definitions.addAll(provider.findCandidateComponents(basePackage)); + } + + if (definitions.isEmpty()) { + return null; + } + + if (definitions.size() == 1) { + return (AbstractBeanDefinition) definitions.iterator().next(); + } + + 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))); + } +} diff --git a/src/main/java/org/springframework/data/repository/config/DefaultRepositoryConfiguration.java b/src/main/java/org/springframework/data/repository/config/DefaultRepositoryConfiguration.java index 9e70c1322..f71c7f8ce 100644 --- a/src/main/java/org/springframework/data/repository/config/DefaultRepositoryConfiguration.java +++ b/src/main/java/org/springframework/data/repository/config/DefaultRepositoryConfiguration.java @@ -29,8 +29,8 @@ import org.springframework.util.StringUtils; public class DefaultRepositoryConfiguration implements RepositoryConfiguration { + public static final String DEFAULT_REPOSITORY_IMPLEMENTATION_POSTFIX = "Impl"; private static final Key DEFAULT_QUERY_LOOKUP_STRATEGY = Key.CREATE_IF_NOT_FOUND; - private static final String DEFAULT_REPOSITORY_IMPLEMENTATION_POSTFIX = "Impl"; private final T configurationSource; private final BeanDefinition definition; diff --git a/src/main/java/org/springframework/data/repository/config/RepositoryBeanDefinitionBuilder.java b/src/main/java/org/springframework/data/repository/config/RepositoryBeanDefinitionBuilder.java index cfe587ef2..a86dcac8e 100644 --- a/src/main/java/org/springframework/data/repository/config/RepositoryBeanDefinitionBuilder.java +++ b/src/main/java/org/springframework/data/repository/config/RepositoryBeanDefinitionBuilder.java @@ -15,25 +15,16 @@ */ package org.springframework.data.repository.config; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.regex.Pattern; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; import org.springframework.core.env.Environment; import org.springframework.core.io.ResourceLoader; import org.springframework.core.type.classreading.CachingMetadataReaderFactory; import org.springframework.core.type.classreading.MetadataReaderFactory; -import org.springframework.core.type.filter.RegexPatternTypeFilter; import org.springframework.data.repository.query.ExtensionAwareEvaluationContextProvider; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -46,14 +37,13 @@ import org.springframework.util.StringUtils; class RepositoryBeanDefinitionBuilder { private static final Logger LOGGER = LoggerFactory.getLogger(RepositoryBeanDefinitionBuilder.class); - private static final String CUSTOM_IMPLEMENTATION_RESOURCE_PATTERN = "**/*%s.class"; private final BeanDefinitionRegistry registry; private final RepositoryConfigurationExtension extension; private final ResourceLoader resourceLoader; - private final Environment environment; private final MetadataReaderFactory metadataReaderFactory; + private CustomRepositoryImplementationDetector implementationDetector; /** * Creates a new {@link RepositoryBeanDefinitionBuilder} from the given {@link BeanDefinitionRegistry}, @@ -69,21 +59,20 @@ class RepositoryBeanDefinitionBuilder { Assert.notNull(extension, "RepositoryConfigurationExtension must not be null!"); Assert.notNull(resourceLoader, "ResourceLoader must not be null!"); - Assert.notNull(environment, "Environement must not be null!"); + Assert.notNull(environment, "Environment must not be null!"); this.registry = registry; this.extension = extension; this.resourceLoader = resourceLoader; this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader); - this.environment = environment; + this.implementationDetector = new CustomRepositoryImplementationDetector(metadataReaderFactory, environment, resourceLoader); } /** * Builds a new {@link BeanDefinitionBuilder} from the given {@link BeanDefinitionRegistry} and {@link ResourceLoader} * . * - * @param registry must not be {@literal null}. - * @param resourceLoader must not be {@literal null}. + * @param configuration must not be {@literal null}. * @return */ public BeanDefinitionBuilder build(RepositoryConfiguration configuration) { @@ -136,7 +125,8 @@ class RepositoryBeanDefinitionBuilder { return beanName; } - AbstractBeanDefinition beanDefinition = detectCustomImplementation(configuration); + AbstractBeanDefinition beanDefinition = implementationDetector.detectCustomImplementation( + configuration.getImplementationClassName(), configuration.getBasePackages()); if (null == beanDefinition) { return null; @@ -154,47 +144,5 @@ class RepositoryBeanDefinitionBuilder { return beanName; } - /** - * Tries to detect a custom implementation for a repository bean by classpath scanning. - * - * @param config must not be {@literal null}. - * @return the {@code AbstractBeanDefinition} of the custom implementation or {@literal null} if none found - */ - private AbstractBeanDefinition detectCustomImplementation(RepositoryConfiguration configuration) { - // Build pattern to lookup implementation class - String className = configuration.getImplementationClassName(); - 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)); - - Set definitions = new HashSet(); - - for (String basePackage : configuration.getBasePackages()) { - definitions.addAll(provider.findCandidateComponents(basePackage)); - } - - if (definitions.isEmpty()) { - return null; - } - - if (definitions.size() == 1) { - return (AbstractBeanDefinition) definitions.iterator().next(); - } - - 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))); - } } diff --git a/src/test/java/org/springframework/data/repository/cdi/AnotherRepository.java b/src/test/java/org/springframework/data/repository/cdi/AnotherRepository.java new file mode 100644 index 000000000..feeeda0c6 --- /dev/null +++ b/src/test/java/org/springframework/data/repository/cdi/AnotherRepository.java @@ -0,0 +1,25 @@ +/* + * Copyright 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. + * 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.repository.cdi; + +import java.io.Serializable; + +import org.springframework.data.repository.Repository; + +public interface AnotherRepository extends Repository, AnotherRepositoryCustom { + +} diff --git a/src/test/java/org/springframework/data/repository/cdi/AnotherRepositoryCustom.java b/src/test/java/org/springframework/data/repository/cdi/AnotherRepositoryCustom.java new file mode 100644 index 000000000..1c50e68d0 --- /dev/null +++ b/src/test/java/org/springframework/data/repository/cdi/AnotherRepositoryCustom.java @@ -0,0 +1,21 @@ +/* + * Copyright 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. + * 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.repository.cdi; + +public interface AnotherRepositoryCustom { + int returnZero(); +} diff --git a/src/test/java/org/springframework/data/repository/cdi/AnotherRepositoryImpl.java b/src/test/java/org/springframework/data/repository/cdi/AnotherRepositoryImpl.java new file mode 100644 index 000000000..e8a0448f8 --- /dev/null +++ b/src/test/java/org/springframework/data/repository/cdi/AnotherRepositoryImpl.java @@ -0,0 +1,25 @@ +/* + * Copyright 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. + * 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.repository.cdi; + +public class AnotherRepositoryImpl implements AnotherRepositoryCustom { + + @Override + public int returnZero() { + return 0; + } +} diff --git a/src/test/java/org/springframework/data/repository/cdi/CdiRepositoryExtensionSupportIntegrationTests.java b/src/test/java/org/springframework/data/repository/cdi/CdiRepositoryExtensionSupportIntegrationTests.java index 5c37cf5ae..feabed15f 100644 --- a/src/test/java/org/springframework/data/repository/cdi/CdiRepositoryExtensionSupportIntegrationTests.java +++ b/src/test/java/org/springframework/data/repository/cdi/CdiRepositoryExtensionSupportIntegrationTests.java @@ -15,8 +15,8 @@ */ package org.springframework.data.repository.cdi; -import static org.junit.Assert.*; import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; import org.junit.Test; @@ -36,5 +36,20 @@ public abstract class CdiRepositoryExtensionSupportIntegrationTests { assertThat(client.repository, is(notNullValue())); } + /** + * @see DATACMNS-557 + */ + @Test + public void createsSpringDataRepositoryWithCustimImplBean() { + + assertThat(getBean(AnotherRepository.class), is(notNullValue())); + + RepositoryClient client = getBean(RepositoryClient.class); + assertThat(client.anotherRepository, is(notNullValue())); + + // this will always return 0 since it's a mock + assertThat(client.anotherRepository.returnZero(), is(0)); + } + protected abstract T getBean(Class type); } 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 767e23734..54d5bf9de 100644 --- a/src/test/java/org/springframework/data/repository/cdi/DummyCdiExtension.java +++ b/src/test/java/org/springframework/data/repository/cdi/DummyCdiExtension.java @@ -15,18 +15,21 @@ */ package org.springframework.data.repository.cdi; -import java.lang.annotation.Annotation; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -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.BeanManager; +import java.lang.annotation.Annotation; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.HashMap; +import java.util.Map.Entry; +import java.util.Set; +import org.apache.webbeans.context.AbstractContext; +import org.apache.webbeans.context.creational.BeanInstanceBag; import org.mockito.Mockito; /** @@ -37,10 +40,15 @@ 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()) { - DummyCdiRepositoryBean bean = new DummyCdiRepositoryBean(type.getValue(), type.getKey(), beanManager); + + CdiRepositoryConfigurationSource configurationSource = lookupConfiguration(beanManager, type.getValue()); + Object customImplementation = findCustomImplementation(type.getKey(), beanManager, type.getValue()); + + DummyCdiRepositoryBean bean = new DummyCdiRepositoryBean(type.getValue(), type.getKey(), beanManager, customImplementation); registerBean(bean); afterBeanDiscovery.addBean(bean); } @@ -52,6 +60,10 @@ public class DummyCdiExtension extends CdiRepositoryExtensionSupport { super(qualifiers, repositoryType, beanManager); } + DummyCdiRepositoryBean(Set qualifiers, Class repositoryType, BeanManager beanManager, Object customImplementation) { + super(qualifiers, repositoryType, beanManager, customImplementation); + } + public Class getScope() { return MyScope.class; } @@ -67,4 +79,22 @@ public class DummyCdiExtension extends CdiRepositoryExtensionSupport { @interface MyScope { } + + static class MyCustomScope extends AbstractContext { + + MyCustomScope() { + super(MyScope.class); + setActive(true); + } + + @Override + protected void setComponentInstanceMap() { + componentInstanceMap = new HashMap, BeanInstanceBag>(); + } + + @Override + public boolean isActive() { + return true; + } + } } diff --git a/src/test/java/org/springframework/data/repository/cdi/RepositoryClient.java b/src/test/java/org/springframework/data/repository/cdi/RepositoryClient.java index a43c75d7b..fef98a289 100644 --- a/src/test/java/org/springframework/data/repository/cdi/RepositoryClient.java +++ b/src/test/java/org/springframework/data/repository/cdi/RepositoryClient.java @@ -25,4 +25,7 @@ class RepositoryClient { @Inject SampleRepository repository; + + @Inject + AnotherRepository anotherRepository; }