diff --git a/spring-data-commons-core/pom.xml b/spring-data-commons-core/pom.xml
index 83adecb62..f2ae174b1 100644
--- a/spring-data-commons-core/pom.xml
+++ b/spring-data-commons-core/pom.xml
@@ -13,6 +13,8 @@
2.2.0
+ 1.0
+ 1.1.3
@@ -96,6 +98,29 @@
3.0
true
+
+
+
+ javax.enterprise
+ cdi-api
+ ${cdi.version}
+ provided
+ true
+
+
+
+ javax.el
+ el-api
+ ${cdi.version}
+ test
+
+
+
+ org.apache.openwebbeans.test
+ cditest-owb
+ ${webbeans.version}
+ test
+
diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryBean.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryBean.java
new file mode 100644
index 000000000..9e2885a04
--- /dev/null
+++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryBean.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2011 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.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.enterprise.context.spi.CreationalContext;
+import javax.enterprise.inject.Alternative;
+import javax.enterprise.inject.Stereotype;
+import javax.enterprise.inject.spi.Bean;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.enterprise.inject.spi.InjectionPoint;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.util.Assert;
+
+/**
+ * Base class for {@link Bean} wrappers.
+ *
+ * @author Dirk Mahler
+ * @author Oliver Gierke
+ */
+public abstract class CdiRepositoryBean implements Bean {
+
+ private static final Log LOG = LogFactory.getLog(CdiRepositoryBean.class);
+
+ private final Set qualifiers;
+ private final Class repositoryType;
+ private final BeanManager beanManager;
+
+ /**
+ * Creates a new {@link CdiRepositoryBean}.
+ *
+ * @param qualifiers must not be {@literal null}.
+ * @param repositoryType has to be an interface must not be {@literal null}.
+ */
+ public CdiRepositoryBean(Set qualifiers, Class repositoryType, BeanManager beanManager) {
+
+ Assert.notNull(qualifiers);
+ Assert.notNull(beanManager);
+ Assert.notNull(repositoryType);
+ Assert.isTrue(repositoryType.isInterface());
+
+ this.qualifiers = qualifiers;
+ this.repositoryType = repositoryType;
+ this.beanManager = beanManager;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.enterprise.inject.spi.Bean#getTypes()
+ */
+ @SuppressWarnings("rawtypes")
+ public Set getTypes() {
+
+ Set interfaces = new HashSet();
+ interfaces.add(repositoryType);
+ interfaces.addAll(Arrays.asList(repositoryType.getInterfaces()));
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(String.format("Declaring types '%s' for repository '%s'.", interfaces.toString(),
+ repositoryType.getName()));
+ }
+
+ return new HashSet(interfaces);
+ }
+
+ /**
+ * Returns an instance of an {@link EntityManager}.
+ *
+ * @param beanManager The BeanManager.
+ * @param bean The bean representing an EntityManager.
+ * @return The EntityManager instance.
+ */
+ @SuppressWarnings("unchecked")
+ protected S getDependencyInstance(Bean bean, Class type) {
+ CreationalContext creationalContext = beanManager.createCreationalContext(bean);
+ return (S) beanManager.getReference(bean, type, creationalContext);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.enterprise.context.spi.Contextual#create(javax.enterprise.context.spi.CreationalContext)
+ */
+ public final T create(CreationalContext creationalContext) {
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(String.format("Creating bean instance for repository type '%s'.", repositoryType.getName()));
+ }
+ return create(creationalContext, repositoryType);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.enterprise.context.spi.Contextual#destroy(java.lang.Object, javax.enterprise.context.spi.CreationalContext)
+ */
+ public void destroy(T instance, CreationalContext creationalContext) {
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(String.format("Destroying bean instance %s for repository type '%s'.", instance.toString(),
+ repositoryType.getName()));
+ }
+
+ creationalContext.release();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.enterprise.inject.spi.Bean#getQualifiers()
+ */
+ public Set getQualifiers() {
+ return qualifiers;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.enterprise.inject.spi.Bean#getName()
+ */
+ public String getName() {
+ return repositoryType.getName();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.enterprise.inject.spi.Bean#getStereotypes()
+ */
+ public Set> getStereotypes() {
+
+ Set> stereotypes = new HashSet>();
+
+ for (Annotation annotation : repositoryType.getAnnotations()) {
+ Class extends Annotation> annotationType = annotation.annotationType();
+ if (annotationType.isAnnotationPresent(Stereotype.class)) {
+ stereotypes.add(annotationType);
+ }
+ }
+
+ return stereotypes;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.enterprise.inject.spi.Bean#getBeanClass()
+ */
+ public Class> getBeanClass() {
+ return repositoryType;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.enterprise.inject.spi.Bean#isAlternative()
+ */
+ public boolean isAlternative() {
+ return repositoryType.isAnnotationPresent(Alternative.class);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.enterprise.inject.spi.Bean#isNullable()
+ */
+ public boolean isNullable() {
+ return false;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.enterprise.inject.spi.Bean#getInjectionPoints()
+ */
+ public Set getInjectionPoints() {
+ return Collections.emptySet();
+ }
+
+ /**
+ * @param creationalContext
+ * @param repositoryType
+ * @return
+ */
+ protected abstract T create(CreationalContext creationalContext, Class repositoryType);
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return String
+ .format("JpaRepositoryBean: type='%s', qualifiers=%s", repositoryType.getName(), qualifiers.toString());
+ }
+}
diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryExtensionSupport.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryExtensionSupport.java
new file mode 100644
index 000000000..bfa89698f
--- /dev/null
+++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryExtensionSupport.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2011 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.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.event.Observes;
+import javax.enterprise.inject.Any;
+import javax.enterprise.inject.Default;
+import javax.enterprise.inject.spi.AnnotatedType;
+import javax.enterprise.inject.spi.Extension;
+import javax.enterprise.inject.spi.ProcessAnnotatedType;
+import javax.enterprise.util.AnnotationLiteral;
+import javax.inject.Qualifier;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.data.repository.NoRepositoryBean;
+import org.springframework.data.repository.Repository;
+import org.springframework.data.repository.RepositoryDefinition;
+
+/**
+ * Base class for {@link Extension} implementations that create instances for Spring Data repositories.
+ *
+ * @author Dirk Mahler
+ * @author Oliver Gierke
+ */
+public abstract class CdiRepositoryExtensionSupport implements Extension {
+
+ private static final Log LOGGER = LogFactory.getLog(CdiRepositoryExtensionSupport.class);
+
+ private final Map, Set> repositoryTypes = new HashMap, Set>();
+
+ /**
+ * Implementation of a an observer which checks for Spring Data repository types and stores them in
+ * {@link #repositoryTypes} for later registration as bean type.
+ *
+ * @param The type.
+ * @param processAnnotatedType The annotated type as defined by CDI.
+ */
+ protected void processAnnotatedType(@Observes ProcessAnnotatedType processAnnotatedType) {
+
+ AnnotatedType annotatedType = processAnnotatedType.getAnnotatedType();
+ Class repositoryType = annotatedType.getJavaClass();
+
+ if (isRepository(repositoryType)) {
+
+ // Determine the qualifiers of the repository type.
+ Set qualifiers = getQualifiers(repositoryType);
+
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug(String.format("Discovered repository type '%s' with qualifiers %s.", repositoryType.getName(),
+ qualifiers));
+ }
+ // Store the repository type using its qualifiers.
+ repositoryTypes.put(repositoryType, qualifiers);
+ }
+ }
+
+ /**
+ * Returns whether the given type is a repository type.
+ *
+ * @param type must not be {@literal null}.
+ * @return
+ */
+ private boolean isRepository(Class> type) {
+
+ boolean isInterface = type.isInterface();
+ boolean extendsRepository = Repository.class.isAssignableFrom(type);
+ boolean isAnnotated = type.isAnnotationPresent(RepositoryDefinition.class);
+ boolean excludedByAnnotation = type.isAnnotationPresent(NoRepositoryBean.class);
+
+ return isInterface && (extendsRepository || isAnnotated) && !excludedByAnnotation;
+ }
+
+ /**
+ * Determines the qualifiers of the given type.
+ */
+ @SuppressWarnings("serial")
+ private Set getQualifiers(final Class> type) {
+
+ Set qualifiers = new HashSet();
+ Annotation[] annotations = type.getAnnotations();
+ for (Annotation annotation : annotations) {
+ Class extends Annotation> annotationType = annotation.annotationType();
+ if (annotationType.isAnnotationPresent(Qualifier.class)) {
+ qualifiers.add(annotation);
+ }
+ }
+ // Add @Default qualifier if no qualifier is specified.
+ if (qualifiers.isEmpty()) {
+ qualifiers.add(new AnnotationLiteral() {
+ });
+ }
+ // Add @Any qualifier.
+ qualifiers.add(new AnnotationLiteral() {
+ });
+ return qualifiers;
+ }
+
+ /**
+ * Provides access to all repository types as well as their qualifiers.
+ *
+ * @return
+ */
+ protected Iterable, Set>> getRepositoryTypes() {
+ return repositoryTypes.entrySet();
+ }
+}
diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/repository/core/support/RepositoryFactorySupport.java b/spring-data-commons-core/src/main/java/org/springframework/data/repository/core/support/RepositoryFactorySupport.java
index 31722b581..8e8ef5c39 100644
--- a/spring-data-commons-core/src/main/java/org/springframework/data/repository/core/support/RepositoryFactorySupport.java
+++ b/spring-data-commons-core/src/main/java/org/springframework/data/repository/core/support/RepositoryFactorySupport.java
@@ -105,7 +105,7 @@ public abstract class RepositoryFactorySupport {
* @param repositoryInterface
* @return
*/
- public > T getRepository(Class repositoryInterface) {
+ public T getRepository(Class repositoryInterface) {
return getRepository(repositoryInterface, null);
}
diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/repository/cdi/CdiRepositoryBeanUnitTests.java b/spring-data-commons-core/src/test/java/org/springframework/data/repository/cdi/CdiRepositoryBeanUnitTests.java
new file mode 100644
index 000000000..61709b55f
--- /dev/null
+++ b/spring-data-commons-core/src/test/java/org/springframework/data/repository/cdi/CdiRepositoryBeanUnitTests.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2011 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 static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Set;
+
+import javax.enterprise.context.spi.CreationalContext;
+import javax.enterprise.inject.spi.BeanManager;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.springframework.data.repository.Repository;
+
+/**
+ * Unit tests for {@link CdiRepositoryBean}.
+ *
+ * @author Oliver Gierke
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class CdiRepositoryBeanUnitTests {
+
+ static final Set NO_ANNOTATIONS = Collections.emptySet();
+
+ @Mock
+ BeanManager beanManager;
+
+ @Test(expected = IllegalArgumentException.class)
+ public void voidRejectsNullQualifiers() {
+ new DummyCdiRepositoryBean(null, SampleRepository.class, beanManager);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void voidRejectsNullRepositoryType() {
+ new DummyCdiRepositoryBean(NO_ANNOTATIONS, null, beanManager);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void voidRejectsNullBeanManager() {
+ new DummyCdiRepositoryBean(NO_ANNOTATIONS, SampleRepository.class, null);
+ }
+
+ @Test
+ public void returnsBasicMetadata() {
+
+ DummyCdiRepositoryBean bean = new DummyCdiRepositoryBean(
+ NO_ANNOTATIONS, SampleRepository.class, beanManager);
+
+ assertThat(bean.getBeanClass(), is(typeCompatibleWith(SampleRepository.class)));
+ assertThat(bean.getName(), is(SampleRepository.class.getName()));
+ assertThat(bean.isNullable(), is(false));
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void returnsAllImplementedTypes() {
+
+ DummyCdiRepositoryBean bean = new DummyCdiRepositoryBean(
+ NO_ANNOTATIONS, SampleRepository.class, beanManager);
+
+ Set types = bean.getTypes();
+ assertThat(types.size(), is(2));
+ assertThat(types.containsAll(Arrays.asList(SampleRepository.class, Repository.class)), is(true));
+ }
+
+ @Test
+ public void detectsStereotypes() {
+
+ DummyCdiRepositoryBean bean = new DummyCdiRepositoryBean(
+ NO_ANNOTATIONS, StereotypedSampleRepository.class, beanManager);
+
+ Set> stereotypes = bean.getStereotypes();
+ assertThat(stereotypes.size(), is(1));
+ assertThat(stereotypes, hasItem(StereotypeAnnotation.class));
+ }
+
+ static class DummyCdiRepositoryBean extends CdiRepositoryBean {
+
+ public DummyCdiRepositoryBean(Set qualifiers, Class repositoryType, BeanManager beanManager) {
+ super(qualifiers, repositoryType, beanManager);
+ }
+
+ public Class extends Annotation> getScope() {
+ return null;
+ }
+
+ @Override
+ protected T create(CreationalContext creationalContext, Class repositoryType) {
+ return null;
+ }
+ }
+
+ static interface SampleRepository extends Repository