Skip to content

Commit 6cc9e33

Browse files
mp911deodrotbohm
authored andcommitted
DATACMNS-565 - Improved detection of custom implementations for CDI repositories.
The detection of the custom implementation has now been moved into the Bean base class to avoid calls to BeanManager.getBeans(…) (which are necessary to pick up the configuration class defining postfixes and the like) happen to early in the CDI container lifecycle. Related ticket: DATACMNS-557.
1 parent 8bfa0bd commit 6cc9e33

File tree

3 files changed

+129
-118
lines changed

3 files changed

+129
-118
lines changed

src/main/java/org/springframework/data/repository/cdi/CdiRepositoryBean.java

Lines changed: 117 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2011-2013 the original author or authors.
2+
* Copyright 2011-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -28,14 +28,19 @@
2828
import javax.enterprise.context.spi.CreationalContext;
2929
import javax.enterprise.inject.Alternative;
3030
import javax.enterprise.inject.Stereotype;
31+
import javax.enterprise.inject.UnsatisfiedResolutionException;
3132
import javax.enterprise.inject.spi.Bean;
3233
import javax.enterprise.inject.spi.BeanManager;
3334
import javax.enterprise.inject.spi.InjectionPoint;
3435
import javax.enterprise.inject.spi.PassivationCapable;
3536

3637
import org.slf4j.Logger;
3738
import org.slf4j.LoggerFactory;
39+
import org.springframework.beans.factory.support.AbstractBeanDefinition;
40+
import org.springframework.data.repository.config.CustomRepositoryImplementationDetector;
41+
import org.springframework.data.repository.config.DefaultRepositoryConfiguration;
3842
import org.springframework.util.Assert;
43+
import org.springframework.util.ClassUtils;
3944
import org.springframework.util.StringUtils;
4045

4146
/**
@@ -48,10 +53,11 @@
4853
public abstract class CdiRepositoryBean<T> implements Bean<T>, PassivationCapable {
4954

5055
private static final Logger LOGGER = LoggerFactory.getLogger(CdiRepositoryBean.class);
56+
private static final CdiRepositoryConfiguration DEFAULT_CONFIGURATION = DefaultCdiRepositoryConfiguration.INSTANCE;
5157

5258
private final Set<Annotation> qualifiers;
5359
private final Class<T> repositoryType;
54-
private final Bean<?> customImplementationBean;
60+
private final CustomRepositoryImplementationDetector detector;
5561
private final BeanManager beanManager;
5662
private final String passivationId;
5763

@@ -74,11 +80,11 @@ public CdiRepositoryBean(Set<Annotation> qualifiers, Class<T> repositoryType, Be
7480
* @param qualifiers must not be {@literal null}.
7581
* @param repositoryType has to be an interface must not be {@literal null}.
7682
* @param beanManager the CDI {@link BeanManager}, must not be {@literal null}.
77-
* @param customImplementationBean the bean for the custom implementation of the
78-
* {@link org.springframework.data.repository.Repository}, can be {@literal null}.
83+
* @param detector detector for the custom repository implementations {@link CustomRepositoryImplementationDetector},
84+
* can be {@literal null}.
7985
*/
8086
public CdiRepositoryBean(Set<Annotation> qualifiers, Class<T> repositoryType, BeanManager beanManager,
81-
Bean<?> customImplementationBean) {
87+
CustomRepositoryImplementationDetector detector) {
8288

8389
Assert.notNull(qualifiers);
8490
Assert.notNull(beanManager);
@@ -88,7 +94,7 @@ public CdiRepositoryBean(Set<Annotation> qualifiers, Class<T> repositoryType, Be
8894
this.qualifiers = qualifiers;
8995
this.repositoryType = repositoryType;
9096
this.beanManager = beanManager;
91-
this.customImplementationBean = customImplementationBean;
97+
this.detector = detector;
9298
this.passivationId = createPassivationId(qualifiers, repositoryType);
9399
}
94100

@@ -179,7 +185,93 @@ public void destroy(T instance, CreationalContext<T> creationalContext) {
179185
creationalContext.release();
180186
}
181187

182-
/*
188+
/**
189+
* Looks up an instance of a {@link CdiRepositoryConfiguration}. In case the instance cannot be found within the CDI
190+
* scope, a default configuration is used.
191+
*
192+
* @return an available CdiRepositoryConfiguration instance or a default configuration.
193+
*/
194+
@SuppressWarnings("unchecked")
195+
protected CdiRepositoryConfiguration lookupConfiguration(BeanManager beanManager, Set<Annotation> qualifiers) {
196+
197+
Set<Bean<?>> beans = beanManager.getBeans(CdiRepositoryConfiguration.class, getQualifiersArray(qualifiers));
198+
199+
if (beans.isEmpty()) {
200+
return DEFAULT_CONFIGURATION;
201+
}
202+
203+
Bean<CdiRepositoryConfiguration> bean = (Bean<CdiRepositoryConfiguration>) beans.iterator().next();
204+
return getDependencyInstance(bean, (Class<CdiRepositoryConfiguration>) bean.getBeanClass());
205+
}
206+
207+
/**
208+
* Try to lookup a custom implementation for a {@link org.springframework.data.repository.Repository}. Can only be
209+
* used when a {@code CustomRepositoryImplementationDetector} is provided.
210+
*
211+
* @param repositoryType
212+
* @param beanManager
213+
* @param qualifiers
214+
* @return the custom implementation instance or null
215+
*/
216+
private Bean<?> getCustomImplementationBean(Class<?> repositoryType, BeanManager beanManager,
217+
Set<Annotation> qualifiers) {
218+
219+
if (detector == null) {
220+
return null;
221+
}
222+
223+
CdiRepositoryConfiguration cdiRepositoryConfiguration = lookupConfiguration(beanManager, qualifiers);
224+
Class<?> customImplementationClass = getCustomImplementationClass(repositoryType, cdiRepositoryConfiguration);
225+
226+
if (customImplementationClass == null) {
227+
return null;
228+
}
229+
230+
Set<Bean<?>> beans = beanManager.getBeans(customImplementationClass, getQualifiersArray(qualifiers));
231+
return beans.isEmpty() ? null : beans.iterator().next();
232+
}
233+
234+
/**
235+
* Retrieves a custom repository interfaces from a repository type. This works for the whole class hierarchy and can
236+
* find also a custom repo which is inherieted over many levels.
237+
*
238+
* @param repositoryType The class representing the repository.
239+
* @param cdiRepositoryConfiguration The configuration for CDI usage.
240+
* @return the interface class or {@literal null}.
241+
*/
242+
private Class<?> getCustomImplementationClass(Class<?> repositoryType,
243+
CdiRepositoryConfiguration cdiRepositoryConfiguration) {
244+
245+
String className = getCustomImplementationClassName(repositoryType, cdiRepositoryConfiguration);
246+
AbstractBeanDefinition beanDefinition = detector.detectCustomImplementation(className,
247+
Collections.singleton(repositoryType.getPackage().getName()));
248+
249+
if (beanDefinition == null) {
250+
return null;
251+
}
252+
253+
try {
254+
return Class.forName(beanDefinition.getBeanClassName());
255+
} catch (ClassNotFoundException e) {
256+
throw new UnsatisfiedResolutionException(String.format("Unable to resolve class for '%s'",
257+
beanDefinition.getBeanClassName()), e);
258+
}
259+
}
260+
261+
private String getCustomImplementationClassName(Class<?> repositoryType,
262+
CdiRepositoryConfiguration cdiRepositoryConfiguration) {
263+
264+
String configuredPostfix = cdiRepositoryConfiguration.getRepositoryImplementationPostfix();
265+
Assert.hasText(configuredPostfix, "Configured repository postfix must not be null or empty!");
266+
267+
return ClassUtils.getShortName(repositoryType) + configuredPostfix;
268+
}
269+
270+
private Annotation[] getQualifiersArray(Set<Annotation> qualifiers) {
271+
return qualifiers.toArray(new Annotation[qualifiers.size()]);
272+
}
273+
274+
/*
183275
* (non-Javadoc)
184276
* @see javax.enterprise.inject.spi.Bean#getQualifiers()
185277
*/
@@ -253,7 +345,7 @@ public Class<? extends Annotation> getScope() {
253345
return ApplicationScoped.class;
254346
}
255347

256-
/*
348+
/*
257349
* (non-Javadoc)
258350
* @see javax.enterprise.inject.spi.PassivationCapable#getId()
259351
*/
@@ -272,6 +364,7 @@ public String getId() {
272364
@Deprecated
273365
protected T create(CreationalContext<T> creationalContext, Class<T> repositoryType) {
274366

367+
Bean<?> customImplementationBean = getCustomImplementationBean(repositoryType, beanManager, qualifiers);
275368
Object customImplementation = customImplementationBean == null ? null : beanManager.getReference(
276369
customImplementationBean, customImplementationBean.getBeanClass(),
277370
beanManager.createCreationalContext(customImplementationBean));
@@ -292,13 +385,27 @@ protected T create(CreationalContext<T> creationalContext, Class<T> repositoryTy
292385
+ "in order to use custom repository implementations");
293386
}
294387

295-
/*
388+
/*
296389
* (non-Javadoc)
297390
* @see java.lang.Object#toString()
298391
*/
299392
@Override
300393
public String toString() {
301394
return String
302-
.format("JpaRepositoryBean: type='%s', qualifiers=%s", repositoryType.getName(), qualifiers.toString());
395+
.format("CdiRepositoryBean: type='%s', qualifiers=%s", repositoryType.getName(), qualifiers.toString());
396+
}
397+
398+
static enum DefaultCdiRepositoryConfiguration implements CdiRepositoryConfiguration {
399+
400+
INSTANCE;
401+
402+
/*
403+
* (non-Javadoc)
404+
* @see org.springframework.data.repository.cdi.CdiRepositoryConfiguration#getRepositoryImplementationPostfix()
405+
*/
406+
@Override
407+
public String getRepositoryImplementationPostfix() {
408+
return DefaultRepositoryConfiguration.DEFAULT_REPOSITORY_IMPLEMENTATION_POSTFIX;
409+
}
303410
}
304411
}

src/main/java/org/springframework/data/repository/cdi/CdiRepositoryExtensionSupport.java

Lines changed: 4 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2011-2013 the original author or authors.
2+
* Copyright 2011-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,21 +16,17 @@
1616
package org.springframework.data.repository.cdi;
1717

1818
import java.lang.annotation.Annotation;
19-
import java.util.Collections;
2019
import java.util.HashMap;
2120
import java.util.HashSet;
2221
import java.util.Map;
2322
import java.util.Map.Entry;
2423
import java.util.Set;
2524

26-
import javax.enterprise.context.spi.CreationalContext;
2725
import javax.enterprise.event.Observes;
2826
import javax.enterprise.inject.Any;
2927
import javax.enterprise.inject.Default;
30-
import javax.enterprise.inject.UnsatisfiedResolutionException;
3128
import javax.enterprise.inject.spi.AfterDeploymentValidation;
3229
import javax.enterprise.inject.spi.AnnotatedType;
33-
import javax.enterprise.inject.spi.Bean;
3430
import javax.enterprise.inject.spi.BeanManager;
3531
import javax.enterprise.inject.spi.Extension;
3632
import javax.enterprise.inject.spi.ProcessAnnotatedType;
@@ -39,7 +35,6 @@
3935

4036
import org.slf4j.Logger;
4137
import org.slf4j.LoggerFactory;
42-
import org.springframework.beans.factory.support.AbstractBeanDefinition;
4338
import org.springframework.core.annotation.AnnotationUtils;
4439
import org.springframework.core.env.Environment;
4540
import org.springframework.core.env.StandardEnvironment;
@@ -51,9 +46,6 @@
5146
import org.springframework.data.repository.Repository;
5247
import org.springframework.data.repository.RepositoryDefinition;
5348
import org.springframework.data.repository.config.CustomRepositoryImplementationDetector;
54-
import org.springframework.data.repository.config.DefaultRepositoryConfiguration;
55-
import org.springframework.util.Assert;
56-
import org.springframework.util.ClassUtils;
5749

5850
/**
5951
* Base class for {@link Extension} implementations that create instances for Spring Data repositories.
@@ -65,7 +57,6 @@
6557
public abstract class CdiRepositoryExtensionSupport implements Extension {
6658

6759
private static final Logger LOGGER = LoggerFactory.getLogger(CdiRepositoryExtensionSupport.class);
68-
private static final CdiRepositoryConfiguration DEFAULT_CONFIGURATION = DefaultCdiRepositoryConfiguration.INSTANCE;
6960

7061
private final Map<Class<?>, Set<Annotation>> repositoryTypes = new HashMap<Class<?>, Set<Annotation>>();
7162
private final Set<CdiRepositoryBean<?>> eagerRepositories = new HashSet<CdiRepositoryBean<?>>();
@@ -189,86 +180,10 @@ protected void registerBean(CdiRepositoryBean<?> bean) {
189180
}
190181

191182
/**
192-
* Looks up an instance of a {@link CdiRepositoryConfiguration}. In case the instance cannot be found within the CDI
193-
* scope, a default configuration is used.
194-
*
195-
* @return an available CdiRepositoryConfiguration instance or a default configuration.
196-
*/
197-
protected CdiRepositoryConfiguration lookupConfiguration(BeanManager beanManager, Set<Annotation> qualifiers) {
198-
199-
Set<Bean<?>> beans = beanManager.getBeans(CdiRepositoryConfiguration.class, getQualifiersArray(qualifiers));
200-
201-
if (beans.isEmpty()) {
202-
return DEFAULT_CONFIGURATION;
203-
}
204-
205-
Bean<?> bean = beans.iterator().next();
206-
CreationalContext<?> creationalContext = beanManager.createCreationalContext(bean);
207-
208-
return (CdiRepositoryConfiguration) beanManager.getReference(bean, CdiRepositoryConfiguration.class,
209-
creationalContext);
210-
}
211-
212-
private Annotation[] getQualifiersArray(Set<Annotation> qualifiers) {
213-
return qualifiers.toArray(new Annotation[qualifiers.size()]);
214-
}
215-
216-
/**
217-
* Try to lookup a custom implementation for a {@link org.springframework.data.repository.Repository}.
218-
*
219-
* @param repositoryType
220-
* @param beanManager
221-
* @param qualifiers
222-
* @return the custom implementation instance or null
183+
* @return the {@link CustomRepositoryImplementationDetector} to scan for the custom implementation
223184
*/
224-
protected Bean<?> getCustomImplementationBean(Class<?> repositoryType, BeanManager beanManager,
225-
Set<Annotation> qualifiers) {
226-
227-
CdiRepositoryConfiguration cdiRepositoryConfiguration = lookupConfiguration(beanManager, qualifiers);
228-
Class<?> customImplementationClass = getCustomImplementationClass(repositoryType, cdiRepositoryConfiguration);
229-
230-
if (customImplementationClass == null) {
231-
return null;
232-
}
233-
234-
Set<Bean<?>> beans = beanManager.getBeans(customImplementationClass, getQualifiersArray(qualifiers));
235-
return beans.isEmpty() ? null : beans.iterator().next();
236-
}
237-
238-
/**
239-
* Retrieves a custom repository interfaces from a repository type. This works for the whole class hierarchy and can
240-
* find also a custom repo which is inherieted over many levels.
241-
*
242-
* @param repositoryType The class representing the repository.
243-
* @param cdiRepositoryConfiguration The configuration for CDI usage.
244-
* @return the interface class or {@literal null}.
245-
*/
246-
private Class<?> getCustomImplementationClass(Class<?> repositoryType,
247-
CdiRepositoryConfiguration cdiRepositoryConfiguration) {
248-
249-
String className = getCustomImplementationClassName(repositoryType, cdiRepositoryConfiguration);
250-
AbstractBeanDefinition beanDefinition = customImplementationDetector.detectCustomImplementation(className,
251-
Collections.singleton(repositoryType.getPackage().getName()));
252-
253-
if (beanDefinition == null) {
254-
return null;
255-
}
256-
257-
try {
258-
return Class.forName(beanDefinition.getBeanClassName());
259-
} catch (ClassNotFoundException e) {
260-
throw new UnsatisfiedResolutionException(String.format("Unable to resolve class for '%s'",
261-
beanDefinition.getBeanClassName()), e);
262-
}
263-
}
264-
265-
private String getCustomImplementationClassName(Class<?> repositoryType,
266-
CdiRepositoryConfiguration cdiRepositoryConfiguration) {
267-
268-
String configuredPostfix = cdiRepositoryConfiguration.getRepositoryImplementationPostfix();
269-
Assert.hasText(configuredPostfix, "Configured repository postfix must not be null or empty!");
270-
271-
return ClassUtils.getShortName(repositoryType) + configuredPostfix;
185+
protected CustomRepositoryImplementationDetector getCustomImplementationDetector() {
186+
return customImplementationDetector;
272187
}
273188

274189
@SuppressWarnings("all")
@@ -285,17 +200,4 @@ static class AnyAnnotationLiteral extends AnnotationLiteral<Any> implements Any
285200
private static final AnyAnnotationLiteral INSTANCE = new AnyAnnotationLiteral();
286201
}
287202

288-
static enum DefaultCdiRepositoryConfiguration implements CdiRepositoryConfiguration {
289-
290-
INSTANCE;
291-
292-
/*
293-
* (non-Javadoc)
294-
* @see org.springframework.data.repository.cdi.CdiRepositoryConfiguration#getRepositoryImplementationPostfix()
295-
*/
296-
@Override
297-
public String getRepositoryImplementationPostfix() {
298-
return DefaultRepositoryConfiguration.DEFAULT_REPOSITORY_IMPLEMENTATION_POSTFIX;
299-
}
300-
}
301203
}

0 commit comments

Comments
 (0)