pull/35165/head
Phillip Webb 2 years ago
parent c13041201d
commit fc5339f81c

@ -27,8 +27,16 @@ package org.springframework.boot.autoconfigure.service.connection;
*/ */
public class ConnectionDetailsFactoryNotFoundException extends RuntimeException { public class ConnectionDetailsFactoryNotFoundException extends RuntimeException {
public <S> ConnectionDetailsFactoryNotFoundException(S source) { <S> ConnectionDetailsFactoryNotFoundException(S source) {
super("No ConnectionDetailsFactory found for source '%s'".formatted(source)); this("No ConnectionDetailsFactory found for source '%s'".formatted(source));
}
public ConnectionDetailsFactoryNotFoundException(String message) {
super(message);
}
public ConnectionDetailsFactoryNotFoundException(String message, Throwable cause) {
super(message, cause);
} }
} }

@ -27,8 +27,16 @@ package org.springframework.boot.autoconfigure.service.connection;
*/ */
public class ConnectionDetailsNotFoundException extends RuntimeException { public class ConnectionDetailsNotFoundException extends RuntimeException {
public <S> ConnectionDetailsNotFoundException(S source) { <S> ConnectionDetailsNotFoundException(S source) {
super("No ConnectionDetails found for source '%s'".formatted(source)); this("No ConnectionDetails found for source '%s'".formatted(source));
}
public ConnectionDetailsNotFoundException(String message) {
super(message);
}
public ConnectionDetailsNotFoundException(String message, Throwable cause) {
super(message, cause);
} }
} }

@ -30,11 +30,11 @@ class BeanOrigin implements Origin {
private final String beanName; private final String beanName;
private final BeanDefinition beanDefinition; private final String resourceDescription;
BeanOrigin(String beanName, BeanDefinition beanDefinition) { BeanOrigin(String beanName, BeanDefinition beanDefinition) {
this.beanName = beanName; this.beanName = beanName;
this.beanDefinition = beanDefinition; this.resourceDescription = (beanDefinition != null) ? beanDefinition.getResourceDescription() : null;
} }
@Override @Override
@ -46,8 +46,7 @@ class BeanOrigin implements Origin {
return false; return false;
} }
BeanOrigin other = (BeanOrigin) obj; BeanOrigin other = (BeanOrigin) obj;
return Objects.equals(this.beanName, other.beanName) && Objects return Objects.equals(this.beanName, other.beanName);
.equals(this.beanDefinition.getResourceDescription(), other.beanDefinition.getResourceDescription());
} }
@Override @Override
@ -57,14 +56,13 @@ class BeanOrigin implements Origin {
@Override @Override
public String toString() { public String toString() {
String resourceDescription = this.beanDefinition.getResourceDescription();
StringBuilder result = new StringBuilder(); StringBuilder result = new StringBuilder();
result.append("Bean '"); result.append("Bean '");
result.append(this.beanName); result.append(this.beanName);
result.append("'"); result.append("'");
if (resourceDescription != null) { if (this.resourceDescription != null) {
result.append(" defined in "); result.append(" defined in ");
result.append(resourceDescription); result.append(this.resourceDescription);
} }
return result.toString(); return result.toString();
} }

@ -18,6 +18,7 @@ package org.springframework.boot.testcontainers.service.connection;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -36,35 +37,31 @@ import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
* Class used to register bean definitions from a list of * Class used to register {@link ConnectionDetails} bean definitions from
* {@link ContainerConnectionSource} instances. * {@link ContainerConnectionSource} instances.
* *
* @author Moritz Halbritter * @author Moritz Halbritter
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Phillip Webb * @author Phillip Webb
*/ */
class ContainerConnectionSourcesRegistrar { class ConnectionDetailsRegistrar {
private static final Log logger = LogFactory.getLog(ContainerConnectionSourcesRegistrar.class); private static final Log logger = LogFactory.getLog(ConnectionDetailsRegistrar.class);
private final ListableBeanFactory beanFactory; private final ListableBeanFactory beanFactory;
private final ConnectionDetailsFactories connectionDetailsFactories; private final ConnectionDetailsFactories connectionDetailsFactories;
private final List<ContainerConnectionSource<?>> sources; ConnectionDetailsRegistrar(ListableBeanFactory beanFactory, ConnectionDetailsFactories connectionDetailsFactories) {
ContainerConnectionSourcesRegistrar(ListableBeanFactory beanFactory,
ConnectionDetailsFactories connectionDetailsFactories, List<ContainerConnectionSource<?>> sources) {
this.beanFactory = beanFactory; this.beanFactory = beanFactory;
this.connectionDetailsFactories = connectionDetailsFactories; this.connectionDetailsFactories = connectionDetailsFactories;
this.sources = sources;
} }
void registerBeanDefinitions(BeanDefinitionRegistry registry) { void registerBeanDefinitions(BeanDefinitionRegistry registry, Collection<ContainerConnectionSource<?>> sources) {
this.sources.forEach((source) -> registerBeanDefinition(registry, source)); sources.forEach((source) -> registerBeanDefinitions(registry, source));
} }
private void registerBeanDefinition(BeanDefinitionRegistry registry, ContainerConnectionSource<?> source) { private void registerBeanDefinitions(BeanDefinitionRegistry registry, ContainerConnectionSource<?> source) {
this.connectionDetailsFactories.getConnectionDetails(source, true) this.connectionDetailsFactories.getConnectionDetails(source, true)
.forEach((connectionDetailsType, connectionDetails) -> registerBeanDefinition(registry, source, .forEach((connectionDetailsType, connectionDetails) -> registerBeanDefinition(registry, source,
connectionDetailsType, connectionDetails)); connectionDetailsType, connectionDetails));

@ -16,24 +16,12 @@
package org.springframework.boot.testcontainers.service.connection; package org.springframework.boot.testcontainers.service.connection;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.testcontainers.containers.Container; import org.testcontainers.containers.Container;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureOrder; import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactories;
import org.springframework.boot.origin.Origin;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.type.AnnotationMetadata;
/** /**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
@ -45,53 +33,10 @@ import org.springframework.core.type.AnnotationMetadata;
*/ */
@AutoConfiguration @AutoConfiguration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Import(ServiceConnectionAutoConfiguration.Registrar.class) @Import(ServiceConnectionAutoConfigurationRegistrar.class)
public class ServiceConnectionAutoConfiguration { public class ServiceConnectionAutoConfiguration {
ServiceConnectionAutoConfiguration() { ServiceConnectionAutoConfiguration() {
} }
static class Registrar implements ImportBeanDefinitionRegistrar {
private final BeanFactory beanFactory;
Registrar(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory listableBeanFactory) {
ConnectionDetailsFactories connectionDetailsFactories = new ConnectionDetailsFactories();
List<ContainerConnectionSource<?>> sources = getSources(listableBeanFactory);
new ContainerConnectionSourcesRegistrar(listableBeanFactory, connectionDetailsFactories, sources)
.registerBeanDefinitions(registry);
}
}
private List<ContainerConnectionSource<?>> getSources(ConfigurableListableBeanFactory beanFactory) {
List<ContainerConnectionSource<?>> sources = new ArrayList<>();
for (String candidate : beanFactory.getBeanNamesForType(Container.class)) {
Set<ServiceConnection> annotations = beanFactory.findAllAnnotationsOnBean(candidate,
ServiceConnection.class, false);
if (!annotations.isEmpty()) {
addSources(sources, beanFactory, candidate, annotations);
}
}
return sources;
}
private void addSources(List<ContainerConnectionSource<?>> sources, ConfigurableListableBeanFactory beanFactory,
String beanName, Set<ServiceConnection> annotations) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
Origin origin = new BeanOrigin(beanName, beanDefinition);
Container<?> container = beanFactory.getBean(beanName, Container.class);
for (ServiceConnection annotation : annotations) {
sources.add(new ContainerConnectionSource<>(beanName, origin, container, annotation));
}
}
}
} }

@ -0,0 +1,80 @@
/*
* Copyright 2012-2023 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
*
* https://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.boot.testcontainers.service.connection;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.testcontainers.containers.Container;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactories;
import org.springframework.boot.origin.Origin;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
/**
* {@link ImportBeanDefinitionRegistrar} used by
* {@link ServiceConnectionAutoConfiguration}.
*
* @author Phillip Webb
*/
class ServiceConnectionAutoConfigurationRegistrar implements ImportBeanDefinitionRegistrar {
private final BeanFactory beanFactory;
ServiceConnectionAutoConfigurationRegistrar(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory listableBeanFactory) {
ConnectionDetailsFactories connectionDetailsFactories = new ConnectionDetailsFactories();
List<ContainerConnectionSource<?>> sources = getSources(listableBeanFactory);
new ConnectionDetailsRegistrar(listableBeanFactory, connectionDetailsFactories)
.registerBeanDefinitions(registry, sources);
}
}
private List<ContainerConnectionSource<?>> getSources(ConfigurableListableBeanFactory beanFactory) {
List<ContainerConnectionSource<?>> sources = new ArrayList<>();
for (String candidate : beanFactory.getBeanNamesForType(Container.class)) {
Set<ServiceConnection> annotations = beanFactory.findAllAnnotationsOnBean(candidate,
ServiceConnection.class, false);
if (!annotations.isEmpty()) {
addSources(sources, beanFactory, candidate, annotations);
}
}
return sources;
}
private void addSources(List<ContainerConnectionSource<?>> sources, ConfigurableListableBeanFactory beanFactory,
String beanName, Set<ServiceConnection> annotations) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
Origin origin = new BeanOrigin(beanName, beanDefinition);
Container<?> container = beanFactory.getBean(beanName, Container.class);
for (ServiceConnection annotation : annotations) {
sources.add(new ContainerConnectionSource<>(beanName, origin, container, annotation));
}
}
}

@ -53,8 +53,8 @@ class ServiceConnectionContextCustomizer implements ContextCustomizer {
public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) { public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
if (beanFactory instanceof BeanDefinitionRegistry registry) { if (beanFactory instanceof BeanDefinitionRegistry registry) {
new ContainerConnectionSourcesRegistrar(beanFactory, this.connectionDetailsFactories, this.sources) new ConnectionDetailsRegistrar(beanFactory, this.connectionDetailsFactories)
.registerBeanDefinitions(registry); .registerBeanDefinitions(registry, this.sources);
} }
} }

@ -33,7 +33,8 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
/** /**
* Tests for {@link ServiceConnectionAutoConfiguration}. * Tests for {@link ServiceConnectionAutoConfiguration} and
* {@link ServiceConnectionAutoConfigurationRegistrar}.
* *
* @author Phillip Webb * @author Phillip Webb
*/ */

Loading…
Cancel
Save