Skip to content

Commit

Permalink
DATACMNS-1734 - Defer initialization of Repositories in DomainClassCo…
Browse files Browse the repository at this point in the history
…nverter.

DomainClassConverter now delays the initialization of the Repositories instances used to avoid the premature initialization of repository instances as that can cause deadlocks if the infrastructure backing the repositories is initialized on a separate thread.

Refactored nested converter classes to allow them to be static ones.

Related issues: spring-projects/spring-boot#16230, spring-projects/spring-framework#25131.
  • Loading branch information
odrotbohm committed May 26, 2020
1 parent 0399381 commit 4e8c731
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 10 deletions.
Expand Up @@ -30,6 +30,7 @@
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.core.EntityInformation;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.util.Lazy;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
Expand All @@ -47,7 +48,7 @@ public class DomainClassConverter<T extends ConversionService & ConverterRegistr
implements ConditionalGenericConverter, ApplicationContextAware {

private final T conversionService;
private Repositories repositories = Repositories.NONE;
private Lazy<Repositories> repositories = Lazy.of(Repositories.NONE);
private Optional<ToEntityConverter> toEntityConverter = Optional.empty();
private Optional<ToIdConverter> toIdConverter = Optional.empty();

Expand Down Expand Up @@ -97,7 +98,7 @@ public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
* @return
*/
private Optional<? extends ConditionalGenericConverter> getConverter(TypeDescriptor targetType) {
return repositories.hasRepositoryFor(targetType.getType()) ? toEntityConverter : toIdConverter;
return repositories.get().hasRepositoryFor(targetType.getType()) ? toEntityConverter : toIdConverter;
}

/*
Expand All @@ -106,13 +107,18 @@ private Optional<? extends ConditionalGenericConverter> getConverter(TypeDescrip
*/
public void setApplicationContext(ApplicationContext context) {

this.repositories = new Repositories(context);
this.repositories = Lazy.of(() -> {

this.toEntityConverter = Optional.of(new ToEntityConverter(this.repositories, this.conversionService));
this.toEntityConverter.ifPresent(it -> this.conversionService.addConverter(it));
Repositories repositories = new Repositories(context);

this.toIdConverter = Optional.of(new ToIdConverter());
this.toIdConverter.ifPresent(it -> this.conversionService.addConverter(it));
this.toEntityConverter = Optional.of(new ToEntityConverter(repositories, conversionService));
this.toEntityConverter.ifPresent(it -> conversionService.addConverter(it));

this.toIdConverter = Optional.of(new ToIdConverter(repositories, conversionService));
this.toIdConverter.ifPresent(it -> conversionService.addConverter(it));

return repositories;
});
}

/**
Expand All @@ -121,9 +127,11 @@ public void setApplicationContext(ApplicationContext context) {
* @author Oliver Gierke
* @since 1.10
*/
private class ToEntityConverter implements ConditionalGenericConverter {
private static class ToEntityConverter implements ConditionalGenericConverter {

private final RepositoryInvokerFactory repositoryInvokerFactory;
private final Repositories repositories;
private final ConversionService conversionService;

/**
* Creates a new {@link ToEntityConverter} for the given {@link Repositories} and {@link ConversionService}.
Expand All @@ -132,7 +140,10 @@ private class ToEntityConverter implements ConditionalGenericConverter {
* @param conversionService must not be {@literal null}.
*/
public ToEntityConverter(Repositories repositories, ConversionService conversionService) {

this.repositoryInvokerFactory = new DefaultRepositoryInvokerFactory(repositories, conversionService);
this.repositories = repositories;
this.conversionService = conversionService;
}

/*
Expand Down Expand Up @@ -206,7 +217,16 @@ public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
* @author Oliver Gierke
* @since 1.10
*/
class ToIdConverter implements ConditionalGenericConverter {
static class ToIdConverter implements ConditionalGenericConverter {

private final Repositories repositories;
private final ConversionService conversionService;

public ToIdConverter(Repositories repositories, ConversionService conversionService) {

this.repositories = repositories;
this.conversionService = conversionService;
}

/*
* (non-Javadoc)
Expand Down
Expand Up @@ -29,7 +29,6 @@
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;

import org.springframework.aop.framework.Advised;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
Expand Down Expand Up @@ -181,6 +180,7 @@ void supportsConversionFromEntityToString() {
void toIdConverterDoesNotMatchIfTargetTypeIsAssignableFromSource() throws NoSuchMethodException {

converter.setApplicationContext(initContextWithRepo());
assertMatches(false);

@SuppressWarnings("rawtypes")
Optional<ToIdConverter> toIdConverter = (Optional<ToIdConverter>) ReflectionTestUtils.getField(converter,
Expand Down

0 comments on commit 4e8c731

Please sign in to comment.