Refactor testcontainers service connections

Update restcontainers service connections support so that
technology specific `@ServiceConnector` annotations are not longer
required.

A single `@ServiceConnector` annotation can now be used to create
all `ConnectionDetail` beans.

Closes gh-35017
This commit is contained in:
Phillip Webb
2023-04-12 22:36:59 -07:00
parent 11dac5b5b7
commit 81a972af8d
78 changed files with 1297 additions and 903 deletions

View File

@@ -16,9 +16,9 @@
package org.springframework.boot.testcontainers.service.connection;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Container;
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactory;
@@ -26,13 +26,13 @@ import org.springframework.boot.origin.Origin;
import org.springframework.boot.origin.OriginProvider;
import org.springframework.core.ResolvableType;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
/**
* Base class for {@link ConnectionDetailsFactory} implementations that provide
* {@link ConnectionDetails} from a {@link ContainerConnectionSource}.
*
* @param <A> the source annotation type. The annotation will be mergable to a
* {@link ServiceConnection @ServiceConnection}.
* @param <D> the connection details type
* @param <C> the generic container type
* @author Moritz Halbritter
@@ -40,17 +40,58 @@ import org.springframework.util.Assert;
* @author Phillip Webb
* @since 3.1.0
*/
public abstract class ContainerConnectionDetailsFactory<A extends Annotation, D extends ConnectionDetails, C extends GenericContainer<?>>
implements ConnectionDetailsFactory<ContainerConnectionSource<A, D, C>, D> {
public abstract class ContainerConnectionDetailsFactory<D extends ConnectionDetails, C extends Container<?>>
implements ConnectionDetailsFactory<ContainerConnectionSource<D, C>, D> {
/**
* Constant passed to the constructor when any connection name is accepted.
*/
protected static final String ANY_CONNECTION_NAME = null;
private final String connectionName;
private final String[] requiredClassNames;
/**
* Create a new {@link ContainerConnectionDetailsFactory} instance that accepts
* {@link #ANY_CONNECTION_NAME any connection name}.
*/
protected ContainerConnectionDetailsFactory() {
this(ANY_CONNECTION_NAME);
}
/**
* Create a new {@link ContainerConnectionDetailsFactory} instance with the given
* connection name restriction.
* @param connectionName the required connection name or {@link #ANY_CONNECTION_NAME}
* @param requiredClassNames the names of classes that must be present
*/
protected ContainerConnectionDetailsFactory(String connectionName, String... requiredClassNames) {
this.connectionName = connectionName;
this.requiredClassNames = requiredClassNames;
}
@Override
public final D getConnectionDetails(ContainerConnectionSource<A, D, C> source) {
Class<?>[] generics = resolveGenerics();
Class<?> annotationType = generics[0];
Class<?> connectionDetailsType = generics[1];
Class<?> containerType = generics[2];
return (!source.accepts(annotationType, connectionDetailsType, containerType)) ? null
: getContainerConnectionDetails(source);
public final D getConnectionDetails(ContainerConnectionSource<D, C> source) {
if (!hasRequiredClasses()) {
return null;
}
try {
Class<?>[] generics = resolveGenerics();
Class<?> connectionDetailsType = generics[0];
Class<?> containerType = generics[1];
if (source.accepts(this.connectionName, connectionDetailsType, containerType)) {
return getContainerConnectionDetails(source);
}
}
catch (NoClassDefFoundError ex) {
}
return null;
}
private boolean hasRequiredClasses() {
return ObjectUtils.isEmpty(this.requiredClassNames) || Arrays.stream(this.requiredClassNames)
.allMatch((requiredClassName) -> ClassUtils.isPresent(requiredClassName, null));
}
private Class<?>[] resolveGenerics() {
@@ -64,7 +105,7 @@ public abstract class ContainerConnectionDetailsFactory<A extends Annotation, D
* @param source the source
* @return the service connection or {@code null}.
*/
protected abstract D getContainerConnectionDetails(ContainerConnectionSource<A, D, C> source);
protected abstract D getContainerConnectionDetails(ContainerConnectionSource<D, C> source);
/**
* Convenient base class for {@link ConnectionDetails} results that are backed by a
@@ -78,7 +119,7 @@ public abstract class ContainerConnectionDetailsFactory<A extends Annotation, D
* Create a new {@link ContainerConnectionDetails} instance.
* @param source the source {@link ContainerConnectionSource}
*/
protected ContainerConnectionDetails(ContainerConnectionSource<?, ?, ?> source) {
protected ContainerConnectionDetails(ContainerConnectionSource<?, ?> source) {
Assert.notNull(source, "Source must not be null");
this.origin = source.getOrigin();
}

View File

@@ -16,23 +16,25 @@
package org.springframework.boot.testcontainers.service.connection;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Set;
import org.testcontainers.containers.GenericContainer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.testcontainers.containers.Container;
import org.testcontainers.utility.DockerImageName;
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
import org.springframework.boot.origin.Origin;
import org.springframework.boot.origin.OriginProvider;
import org.springframework.core.annotation.MergedAnnotation;
import org.springframework.core.log.LogMessage;
import org.springframework.util.StringUtils;
/**
* Passed to {@link ContainerConnectionDetailsFactory} to provide details of the
* {@link ServiceConnection @ServiceConnection} annotation {@link GenericContainer} field
* that provides the service.
* {@link ServiceConnection @ServiceConnection} annotated {@link Container} that provides
* the service.
*
* @param <A> the source annotation type. The annotation will mergable to a
* {@link ServiceConnection @ServiceConnection}
* @param <D> the connection details type
* @param <C> the generic container type
* @author Moritz Halbritter
@@ -41,58 +43,77 @@ import org.springframework.core.annotation.MergedAnnotation;
* @since 3.1.0
* @see ContainerConnectionDetailsFactory
*/
public final class ContainerConnectionSource<A extends Annotation, D extends ConnectionDetails, C extends GenericContainer<?>>
public final class ContainerConnectionSource<D extends ConnectionDetails, C extends Container<?>>
implements OriginProvider {
private final Class<D> connectionDetailsType;
private static final Log logger = LogFactory.getLog(ContainerConnectionSource.class);
private final Field field;
private final String beanNameSuffix;
private final A annotation;
private final Origin origin;
private final C container;
private final AnnotatedFieldOrigin origin;
private String acceptedConnectionName;
@SuppressWarnings("unchecked")
ContainerConnectionSource(Class<D> connectionDetailsType, Field field,
MergedAnnotation<ServiceConnection> annotation, C container) {
this(connectionDetailsType, field, (A) annotation.getRoot().synthesize(), container);
}
private Set<Class<?>> acceptedConnectionDetailsTypes;
ContainerConnectionSource(Class<D> connectionDetailsType, Field field, A annotation, C container) {
this.connectionDetailsType = connectionDetailsType;
this.field = field;
this.annotation = annotation;
ContainerConnectionSource(String beanNameSuffix, Origin origin, C container,
MergedAnnotation<ServiceConnection> annotation) {
this.beanNameSuffix = beanNameSuffix;
this.origin = origin;
this.container = container;
this.origin = new AnnotatedFieldOrigin(field, annotation);
this.acceptedConnectionName = getConnectionName(container, annotation.getString("name"));
this.acceptedConnectionDetailsTypes = Set.of(annotation.getClassArray("type"));
}
boolean accepts(Class<?> annotationType, Class<?> connectionDetailsType, Class<?> containerType) {
return annotationType.isInstance(this.annotation)
&& connectionDetailsType.isAssignableFrom(this.connectionDetailsType)
&& containerType.isInstance(this.container);
ContainerConnectionSource(String beanNameSuffix, Origin origin, C container, ServiceConnection annotation) {
this.beanNameSuffix = beanNameSuffix;
this.origin = origin;
this.container = container;
this.acceptedConnectionName = getConnectionName(container, annotation.name());
this.acceptedConnectionDetailsTypes = Set.of(annotation.type());
}
String getBeanName() {
return this.field.getName() + this.connectionDetailsType.getSimpleName() + "ConnectedContainer";
private static String getConnectionName(Container<?> container, String connectionName) {
if (StringUtils.hasLength(connectionName)) {
return connectionName;
}
try {
DockerImageName imageName = DockerImageName.parse(container.getDockerImageName());
imageName.assertValid();
return imageName.getRepository();
}
catch (IllegalArgumentException ex) {
return container.getDockerImageName();
}
}
/**
* Return the source annotation that provided the connection to the container. This
* annotation will be mergable to {@link ServiceConnection @ServiceConnection}.
* @return the source annotation
*/
public A getAnnotation() {
return this.annotation;
boolean accepts(String connectionName, Class<?> connectionDetailsType, Class<?> containerType) {
if (!containerType.isInstance(this.container)) {
logger.trace(LogMessage.of(() -> "%s not accepted as %s is not an instance of %s".formatted(this,
this.container.getClass().getName(), containerType.getName())));
return false;
}
if (StringUtils.hasLength(connectionName) && !connectionName.equalsIgnoreCase(this.acceptedConnectionName)) {
logger.trace(LogMessage.of(() -> "%s not accepted as connection names '%s' and '%s' do not match"
.formatted(this, connectionName, this.acceptedConnectionName)));
return false;
}
if (!this.acceptedConnectionDetailsTypes.isEmpty() && !this.acceptedConnectionDetailsTypes.stream()
.anyMatch((candidate) -> candidate.isAssignableFrom(connectionDetailsType))) {
logger.trace(LogMessage.of(() -> "%s not accepted as connection details type %s not in %s".formatted(this,
connectionDetailsType, this.acceptedConnectionDetailsTypes)));
return false;
}
logger.trace(LogMessage
.of(() -> "%s accepted for connection name '%s', connection details type %s, container type %s"
.formatted(this, connectionName, connectionDetailsType.getName(), containerType.getName())));
return true;
}
/**
* Return the {@link GenericContainer} that implements the service being connected to.
* @return the {@link GenericContainer} providing the service
*/
public C getContainer() {
return this.container;
String getBeanNameSuffix() {
return this.beanNameSuffix;
}
@Override
@@ -100,9 +121,17 @@ public final class ContainerConnectionSource<A extends Annotation, D extends Con
return this.origin;
}
/**
* Return the {@link Container} that implements the service being connected to.
* @return the {@link Container} providing the service
*/
public C getContainer() {
return this.container;
}
@Override
public String toString() {
return "ServiceConnectedContainer for %s".formatted(this.origin);
return "@ServiceConnection source for %s".formatted(this.origin);
}
}

View File

@@ -0,0 +1,106 @@
/*
* 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.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactories;
import org.springframework.core.log.LogMessage;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Class used to register bean definitions from a list of
* {@link ContainerConnectionSource} instances.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/
class ContainerConnectionSourcesRegistrar {
private static final Log logger = LogFactory.getLog(ContainerConnectionSourcesRegistrar.class);
private final ListableBeanFactory beanFactory;
private final ConnectionDetailsFactories connectionDetailsFactories;
private final List<ContainerConnectionSource<?, ?>> sources;
ContainerConnectionSourcesRegistrar(ListableBeanFactory beanFactory,
ConnectionDetailsFactories connectionDetailsFactories, List<ContainerConnectionSource<?, ?>> sources) {
this.beanFactory = beanFactory;
this.connectionDetailsFactories = connectionDetailsFactories;
this.sources = sources;
}
void registerBeanDefinitions(BeanDefinitionRegistry registry) {
this.sources.forEach((source) -> registerBeanDefinition(registry, source));
}
private void registerBeanDefinition(BeanDefinitionRegistry registry, ContainerConnectionSource<?, ?> source) {
getConnectionDetails(source)
.forEach((connectionDetailsType, connectionDetails) -> registerBeanDefinition(registry, source,
connectionDetailsType, connectionDetails));
}
private <S> Map<Class<?>, ConnectionDetails> getConnectionDetails(S source) {
Map<Class<?>, ConnectionDetails> connectionDetails = this.connectionDetailsFactories
.getConnectionDetails(source);
Assert.state(!connectionDetails.isEmpty(), () -> "No connection details created for %s".formatted(source));
return connectionDetails;
}
@SuppressWarnings("unchecked")
private <T> void registerBeanDefinition(BeanDefinitionRegistry registry, ContainerConnectionSource<?, ?> source,
Class<?> connectionDetailsType, ConnectionDetails connectionDetails) {
String[] existingBeans = this.beanFactory.getBeanNamesForType(connectionDetailsType);
if (!ObjectUtils.isEmpty(existingBeans)) {
logger.debug(LogMessage.of(() -> "Skipping registration of %s due to existing beans %s".formatted(source,
Arrays.asList(existingBeans))));
return;
}
String beanName = getBeanName(source, connectionDetails);
Class<T> beanType = (Class<T>) connectionDetails.getClass();
Supplier<T> beanSupplier = () -> (T) connectionDetails;
logger.debug(LogMessage.of(() -> "Registering '%s' for %s".formatted(beanName, source)));
registry.registerBeanDefinition(beanName, new RootBeanDefinition(beanType, beanSupplier));
}
private String getBeanName(ContainerConnectionSource<?, ?> source, ConnectionDetails connectionDetails) {
List<String> parts = new ArrayList<>();
parts.add(ClassUtils.getShortNameAsProperty(connectionDetails.getClass()));
parts.add("for");
parts.add(source.getBeanNameSuffix());
return StringUtils.uncapitalize(parts.stream().map(StringUtils::capitalize).collect(Collectors.joining()));
}
}

View File

@@ -16,31 +16,26 @@
package org.springframework.boot.testcontainers.service.connection;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Objects;
import org.springframework.boot.origin.Origin;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* {@link Origin} backed by a {@link Field} and an {@link Annotation}.
* {@link Origin} backed by a {@link Field}.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/
class AnnotatedFieldOrigin implements Origin {
class FieldOrigin implements Origin {
private final Field field;
private final Annotation annotation;
AnnotatedFieldOrigin(Field field, Annotation annotation) {
FieldOrigin(Field field) {
Assert.notNull(field, "Field must not be null");
Assert.notNull(annotation, "Annotation must not be null");
this.field = field;
this.annotation = annotation;
}
@Override
@@ -51,18 +46,18 @@ class AnnotatedFieldOrigin implements Origin {
if (obj == null || getClass() != obj.getClass()) {
return false;
}
AnnotatedFieldOrigin other = (AnnotatedFieldOrigin) obj;
return this.field.equals(other.field) && this.annotation.equals(other.annotation);
FieldOrigin other = (FieldOrigin) obj;
return this.field.equals(other.field);
}
@Override
public int hashCode() {
return Objects.hash(this.field, this.annotation);
return this.field.hashCode();
}
@Override
public String toString() {
return this.annotation + " " + this.field;
return ClassUtils.getShortName(this.field.getDeclaringClass()) + "." + this.field.getName();
}
}

View File

@@ -21,15 +21,14 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.testcontainers.containers.Container;
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactory;
import org.springframework.core.annotation.AliasFor;
/**
* Annotation used to indicate that a field provides a service that can be connected to.
* Typically used to meta-annotate a higher-level annotation.
* <p>
* When used, a {@link ConnectionDetailsFactory} must be registered in
* {@code spring.factories} to provide {@link ConnectionDetails} for the field value.
* Annotation used to indicate that a field or method is a
* {@link ContainerConnectionSource} which provides a service that can be connected to.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
@@ -37,14 +36,35 @@ import org.springframework.boot.autoconfigure.service.connection.ConnectionDetai
* @since 3.1.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.ANNOTATION_TYPE })
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE })
public @interface ServiceConnection {
/**
* The type of {@link ConnectionDetails} that can describe how to connect to the
* service.
* @return the connection type
* The name of the service being connected to. If not specified, the image name will
* be used. Container names are used to determine the connection details that should
* be created when a technology-specific {@link Container} subclass is not available.
* This attribute is an alias for {@link #name()}.
* @return the name of the service
* @see #name()
*/
Class<? extends ConnectionDetails> value();
@AliasFor("name")
String value() default "";
/**
* The name of the service being connected to. If not specified, the image name will
* be used. Container names are used to determine the connection details that should
* be created when a technology-specific {@link Container} subclass is not available.
* @return the name of the service
* @see #value()
*/
@AliasFor("value")
String name() default "";
/**
* A restriction to types of {@link ConnectionDetails} that can be created from this
* connection. The default value does not restrict the types that can be created.
* @return the connection detail types that can be created to establish the connection
*/
Class<? extends ConnectionDetails>[] type() default {};
}

View File

@@ -17,21 +17,17 @@
package org.springframework.boot.testcontainers.service.connection;
import java.util.List;
import java.util.function.Supplier;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactories;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.util.Assert;
/**
* {@link ContextCustomizer} to support registering {@link ConnectionDetails}.
* Spring Test {@link ContextCustomizer} to support registering {@link ConnectionDetails}.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
@@ -39,46 +35,30 @@ import org.springframework.util.Assert;
*/
class ServiceConnectionContextCustomizer implements ContextCustomizer {
private final ConnectionDetailsFactories factories = new ConnectionDetailsFactories();
private final List<ContainerConnectionSource<?, ?>> sources;
private final List<ContainerConnectionSource<?, ?, ?>> sources;
private final ConnectionDetailsFactories connectionDetailsFactories;
ServiceConnectionContextCustomizer(List<ContainerConnectionSource<?, ?, ?>> sources) {
ServiceConnectionContextCustomizer(List<ContainerConnectionSource<?, ?>> sources) {
this(sources, new ConnectionDetailsFactories());
}
ServiceConnectionContextCustomizer(List<ContainerConnectionSource<?, ?>> sources,
ConnectionDetailsFactories connectionDetailsFactories) {
this.sources = sources;
this.connectionDetailsFactories = connectionDetailsFactories;
}
@Override
public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
if (beanFactory instanceof BeanDefinitionRegistry registry) {
registerServiceConnections(registry);
new ContainerConnectionSourcesRegistrar(beanFactory, this.connectionDetailsFactories, this.sources)
.registerBeanDefinitions(registry);
}
}
private void registerServiceConnections(BeanDefinitionRegistry registry) {
this.sources.forEach((source) -> registerServiceConnection(registry, source));
}
private void registerServiceConnection(BeanDefinitionRegistry registry, ContainerConnectionSource<?, ?, ?> source) {
ConnectionDetails connectionDetails = getConnectionDetails(source);
register(connectionDetails, registry, source.getBeanName());
}
@SuppressWarnings("unchecked")
private <T> void register(ConnectionDetails connectionDetails, BeanDefinitionRegistry registry, String beanName) {
Class<T> beanType = (Class<T>) connectionDetails.getClass();
Supplier<T> beanSupplier = () -> (T) connectionDetails;
BeanDefinition beanDefinition = new RootBeanDefinition(beanType, beanSupplier);
registry.registerBeanDefinition(beanName, beanDefinition);
}
private <S> ConnectionDetails getConnectionDetails(S source) {
ConnectionDetails connectionDetails = this.factories.getConnectionDetails(source);
Assert.state(connectionDetails != null, () -> "No connection details created for %s".formatted(source));
return connectionDetails;
}
List<ContainerConnectionSource<?, ?, ?>> getSources() {
List<ContainerConnectionSource<?, ?>> getSources() {
return this.sources;
}

View File

@@ -21,9 +21,9 @@ import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Container;
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
import org.springframework.boot.origin.Origin;
import org.springframework.core.annotation.MergedAnnotation;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.test.context.ContextConfigurationAttributes;
@@ -31,10 +31,14 @@ import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.ContextCustomizerFactory;
import org.springframework.test.context.TestContextAnnotationUtils;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
* {@link ContextCustomizerFactory} to support service connections in tests.
* Spring Test {@link ContextCustomizerFactory} to support
* {@link ServiceConnection @ServiceConnection} annotated {@link Container} fields in
* tests.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
@@ -45,12 +49,12 @@ class ServiceConnectionContextCustomizerFactory implements ContextCustomizerFact
@Override
public ContextCustomizer createContextCustomizer(Class<?> testClass,
List<ContextConfigurationAttributes> configAttributes) {
List<ContainerConnectionSource<?, ?, ?>> sources = new ArrayList<>();
List<ContainerConnectionSource<?, ?>> sources = new ArrayList<>();
findSources(testClass, sources);
return (sources.isEmpty()) ? null : new ServiceConnectionContextCustomizer(sources);
}
private void findSources(Class<?> clazz, List<ContainerConnectionSource<?, ?, ?>> sources) {
private void findSources(Class<?> clazz, List<ContainerConnectionSource<?, ?>> sources) {
ReflectionUtils.doWithFields(clazz, (field) -> {
MergedAnnotations annotations = MergedAnnotations.from(field);
annotations.stream(ServiceConnection.class)
@@ -61,23 +65,17 @@ class ServiceConnectionContextCustomizerFactory implements ContextCustomizerFact
}
}
private ContainerConnectionSource<?, ?, ?> createSource(Field field,
MergedAnnotation<ServiceConnection> annotation) {
if (!Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@ServiceConnection field '%s' must be static".formatted(field.getName()));
}
Class<? extends ConnectionDetails> connectionDetailsType = getConnectionDetailsType(annotation);
private ContainerConnectionSource<?, ?> createSource(Field field, MergedAnnotation<ServiceConnection> annotation) {
Assert.state(Modifier.isStatic(field.getModifiers()),
() -> "@ServiceConnection field '%s' must be static".formatted(field.getName()));
String beanNameSuffix = StringUtils.capitalize(ClassUtils.getShortNameAsProperty(field.getDeclaringClass()))
+ StringUtils.capitalize(field.getName());
Origin origin = new FieldOrigin(field);
Object fieldValue = getFieldValue(field);
Assert.isInstanceOf(GenericContainer.class, fieldValue,
"Field %s must be a %s".formatted(field.getName(), GenericContainer.class.getName()));
GenericContainer<?> container = (GenericContainer<?>) fieldValue;
return new ContainerConnectionSource<>(connectionDetailsType, field, annotation, container);
}
@SuppressWarnings("unchecked")
private Class<? extends ConnectionDetails> getConnectionDetailsType(
MergedAnnotation<ServiceConnection> annotation) {
return (Class<? extends ConnectionDetails>) annotation.getClass(MergedAnnotation.VALUE);
Assert.state(Container.class.isInstance(fieldValue), () -> "Field '%s' in %s must be a %s"
.formatted(field.getName(), field.getDeclaringClass().getName(), Container.class.getName()));
Container<?> container = (Container<?>) fieldValue;
return new ContainerConnectionSource<>(beanNameSuffix, origin, container, annotation);
}
private Object getFieldValue(Field field) {

View File

@@ -24,22 +24,23 @@ import org.testcontainers.containers.RabbitMQContainer;
import org.springframework.boot.autoconfigure.amqp.RabbitConnectionDetails;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
/**
* {@link ContainerConnectionDetailsFactory} for
* {@link RabbitServiceConnection @RabbitServiceConnection}-annotated
* {@link RabbitMQContainer} fields.
* {@link ContainerConnectionDetailsFactory} to create {@link RabbitConnectionDetails}
* from a {@link ServiceConnection @ServiceConnection}-annotated
* {@link RabbitMQContainer}.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/
class RabbitContainerConnectionDetailsFactory
extends ContainerConnectionDetailsFactory<RabbitServiceConnection, RabbitConnectionDetails, RabbitMQContainer> {
extends ContainerConnectionDetailsFactory<RabbitConnectionDetails, RabbitMQContainer> {
@Override
protected RabbitConnectionDetails getContainerConnectionDetails(
ContainerConnectionSource<RabbitServiceConnection, RabbitConnectionDetails, RabbitMQContainer> source) {
ContainerConnectionSource<RabbitConnectionDetails, RabbitMQContainer> source) {
return new RabbitMqContainerConnectionDetails(source);
}
@@ -52,7 +53,7 @@ class RabbitContainerConnectionDetailsFactory
private final RabbitMQContainer container;
private RabbitMqContainerConnectionDetails(
ContainerConnectionSource<RabbitServiceConnection, RabbitConnectionDetails, RabbitMQContainer> source) {
ContainerConnectionSource<RabbitConnectionDetails, RabbitMQContainer> source) {
super(source);
this.container = source.getContainer();
}

View File

@@ -1,44 +0,0 @@
/*
* 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.amqp;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.amqp.RabbitConnectionDetails;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
/**
* Annotation that indicates that a field provides a RabbitMQ service connection.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
* @since 3.1.0
* @see RabbitConnectionDetails
* @see ServiceConnection
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.TYPE })
@ServiceConnection(RabbitConnectionDetails.class)
public @interface RabbitServiceConnection {
}

View File

@@ -23,22 +23,23 @@ import org.testcontainers.containers.CassandraContainer;
import org.springframework.boot.autoconfigure.cassandra.CassandraConnectionDetails;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
/**
* {@link ContainerConnectionDetailsFactory} for
* {@link CassandraServiceConnection @CassandraServiceConnection}-annotated
* {@link CassandraContainer} fields.
* {@link ContainerConnectionDetailsFactory} to create {@link CassandraConnectionDetails}
* from a {@link ServiceConnection @ServiceConnection}-annotated
* {@link CassandraContainer}.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/
class CassandraContainerConnectionDetailsFactory extends
ContainerConnectionDetailsFactory<CassandraServiceConnection, CassandraConnectionDetails, CassandraContainer<?>> {
class CassandraContainerConnectionDetailsFactory
extends ContainerConnectionDetailsFactory<CassandraConnectionDetails, CassandraContainer<?>> {
@Override
protected CassandraConnectionDetails getContainerConnectionDetails(
ContainerConnectionSource<CassandraServiceConnection, CassandraConnectionDetails, CassandraContainer<?>> source) {
ContainerConnectionSource<CassandraConnectionDetails, CassandraContainer<?>> source) {
return new CassandraContainerConnectionDetails(source);
}
@@ -51,7 +52,7 @@ class CassandraContainerConnectionDetailsFactory extends
private final CassandraContainer<?> container;
private CassandraContainerConnectionDetails(
ContainerConnectionSource<CassandraServiceConnection, CassandraConnectionDetails, CassandraContainer<?>> source) {
ContainerConnectionSource<CassandraConnectionDetails, CassandraContainer<?>> source) {
super(source);
this.container = source.getContainer();
}

View File

@@ -1,44 +0,0 @@
/*
* 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.cassandra;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.cassandra.CassandraConnectionDetails;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
/**
* Annotation that indicates that a field provides a Cassandra service connection.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
* @since 3.1.0
* @see CassandraConnectionDetails
* @see ServiceConnection
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.TYPE })
@ServiceConnection(CassandraConnectionDetails.class)
public @interface CassandraServiceConnection {
}

View File

@@ -21,22 +21,23 @@ import org.testcontainers.couchbase.CouchbaseContainer;
import org.springframework.boot.autoconfigure.couchbase.CouchbaseConnectionDetails;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
/**
* {@link ContainerConnectionDetailsFactory} for
* {@link CouchbaseServiceConnection @CouchbaseServiceConnection}-annotated
* {@link CouchbaseContainer} fields.
* {@link ContainerConnectionDetailsFactory} to create {@link CouchbaseConnectionDetails}
* from a {@link ServiceConnection @ServiceConnection}-annotated
* {@link CouchbaseContainer}.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/
class CouchbaseContainerConnectionDetailsFactory extends
ContainerConnectionDetailsFactory<CouchbaseServiceConnection, CouchbaseConnectionDetails, CouchbaseContainer> {
class CouchbaseContainerConnectionDetailsFactory
extends ContainerConnectionDetailsFactory<CouchbaseConnectionDetails, CouchbaseContainer> {
@Override
protected CouchbaseConnectionDetails getContainerConnectionDetails(
ContainerConnectionSource<CouchbaseServiceConnection, CouchbaseConnectionDetails, CouchbaseContainer> source) {
ContainerConnectionSource<CouchbaseConnectionDetails, CouchbaseContainer> source) {
return new CouchbaseContainerConnectionDetails(source);
}
@@ -49,7 +50,7 @@ class CouchbaseContainerConnectionDetailsFactory extends
private final CouchbaseContainer container;
private CouchbaseContainerConnectionDetails(
ContainerConnectionSource<CouchbaseServiceConnection, CouchbaseConnectionDetails, CouchbaseContainer> source) {
ContainerConnectionSource<CouchbaseConnectionDetails, CouchbaseContainer> source) {
super(source);
this.container = source.getContainer();
}

View File

@@ -1,44 +0,0 @@
/*
* 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.couchbase;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.couchbase.CouchbaseConnectionDetails;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
/**
* Annotation that indicates that a field provides a Couchbase service connection.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
* @since 3.1.0
* @see CouchbaseConnectionDetails
* @see ServiceConnection
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.TYPE })
@ServiceConnection(CouchbaseConnectionDetails.class)
public @interface CouchbaseServiceConnection {
}

View File

@@ -18,30 +18,31 @@ package org.springframework.boot.testcontainers.service.connection.elasticsearch
import java.util.List;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.elasticsearch.ElasticsearchContainer;
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchConnectionDetails;
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchConnectionDetails.Node.Protocol;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
/**
* {@link ContainerConnectionDetailsFactory} for
* {@link ElasticsearchServiceConnection @ElasticsearchServiceConnection}-annotated
* {@link GenericContainer} fields.
* {@link ContainerConnectionDetailsFactory} to create
* {@link ElasticsearchConnectionDetails} from a
* {@link ServiceConnection @ServiceConnection}-annotated {@link ElasticsearchContainer}.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/
class ElasticsearchContainerConnectionDetailsFactory extends
ContainerConnectionDetailsFactory<ElasticsearchServiceConnection, ElasticsearchConnectionDetails, GenericContainer<?>> {
class ElasticsearchContainerConnectionDetailsFactory
extends ContainerConnectionDetailsFactory<ElasticsearchConnectionDetails, ElasticsearchContainer> {
private static final int DEFAULT_PORT = 9200;
@Override
protected ElasticsearchConnectionDetails getContainerConnectionDetails(
ContainerConnectionSource<ElasticsearchServiceConnection, ElasticsearchConnectionDetails, GenericContainer<?>> source) {
ContainerConnectionSource<ElasticsearchConnectionDetails, ElasticsearchContainer> source) {
return new ElasticsearchContainerConnectionDetails(source);
}
@@ -55,7 +56,7 @@ class ElasticsearchContainerConnectionDetailsFactory extends
private final List<Node> nodes;
private ElasticsearchContainerConnectionDetails(
ContainerConnectionSource<ElasticsearchServiceConnection, ElasticsearchConnectionDetails, GenericContainer<?>> source) {
ContainerConnectionSource<ElasticsearchConnectionDetails, ElasticsearchContainer> source) {
super(source);
this.nodes = List.of(new Node(source.getContainer().getHost(),
source.getContainer().getMappedPort(DEFAULT_PORT), Protocol.HTTP, null, null));

View File

@@ -1,44 +0,0 @@
/*
* 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.elasticsearch;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchConnectionDetails;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
/**
* Annotation that indicates that a field provides a Elasticsearch service connection.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
* @since 3.1.0
* @see ElasticsearchConnectionDetails
* @see ServiceConnection
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.TYPE })
@ServiceConnection(ElasticsearchConnectionDetails.class)
public @interface ElasticsearchServiceConnection {
}

View File

@@ -24,18 +24,18 @@ import org.springframework.boot.testcontainers.service.connection.ContainerConne
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
/**
* {@link ContainerConnectionDetailsFactory} for
* {@link ServiceConnection @ServiceConnection}-annotated {@link JdbcDatabaseContainer}
* fields that should produce {@link FlywayConnectionDetails}.
* {@link ContainerConnectionDetailsFactory} to create {@link FlywayConnectionDetails}
* from a {@link ServiceConnection @ServiceConnection}-annotated
* {@link JdbcDatabaseContainer}.
*
* @author Andy Wilkinson
*/
class FlywayContainerConnectionDetailsFactory extends
ContainerConnectionDetailsFactory<ServiceConnection, FlywayConnectionDetails, JdbcDatabaseContainer<?>> {
class FlywayContainerConnectionDetailsFactory
extends ContainerConnectionDetailsFactory<FlywayConnectionDetails, JdbcDatabaseContainer<?>> {
@Override
protected FlywayConnectionDetails getContainerConnectionDetails(
ContainerConnectionSource<ServiceConnection, FlywayConnectionDetails, JdbcDatabaseContainer<?>> source) {
ContainerConnectionSource<FlywayConnectionDetails, JdbcDatabaseContainer<?>> source) {
return new FlywayContainerConnectionDetails(source);
}
@@ -48,7 +48,7 @@ class FlywayContainerConnectionDetailsFactory extends
private final JdbcDatabaseContainer<?> container;
private FlywayContainerConnectionDetails(
ContainerConnectionSource<ServiceConnection, FlywayConnectionDetails, JdbcDatabaseContainer<?>> source) {
ContainerConnectionSource<FlywayConnectionDetails, JdbcDatabaseContainer<?>> source) {
super(source);
this.container = source.getContainer();
}

View File

@@ -23,22 +23,23 @@ import org.testcontainers.containers.InfluxDBContainer;
import org.springframework.boot.autoconfigure.influx.InfluxDbConnectionDetails;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
/**
* {@link ContainerConnectionDetailsFactory} for
* {@link InfluxDbServiceConnection @InfluxDbServiceConnection}-annotated
* {@link InfluxDBContainer} fields.
* {@link ContainerConnectionDetailsFactory} to create {@link InfluxDbConnectionDetails}
* from a {@link ServiceConnection @ServiceConnection}-annotated
* {@link InfluxDBContainer}.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/
class InfluxDbContainerConnectionDetailsFactory extends
ContainerConnectionDetailsFactory<InfluxDbServiceConnection, InfluxDbConnectionDetails, InfluxDBContainer<?>> {
class InfluxDbContainerConnectionDetailsFactory
extends ContainerConnectionDetailsFactory<InfluxDbConnectionDetails, InfluxDBContainer<?>> {
@Override
protected InfluxDbConnectionDetails getContainerConnectionDetails(
ContainerConnectionSource<InfluxDbServiceConnection, InfluxDbConnectionDetails, InfluxDBContainer<?>> source) {
ContainerConnectionSource<InfluxDbConnectionDetails, InfluxDBContainer<?>> source) {
return new InfluxDbContainerConnectionDetails(source);
}
@@ -51,7 +52,7 @@ class InfluxDbContainerConnectionDetailsFactory extends
private final InfluxDBContainer<?> container;
private InfluxDbContainerConnectionDetails(
ContainerConnectionSource<InfluxDbServiceConnection, InfluxDbConnectionDetails, InfluxDBContainer<?>> source) {
ContainerConnectionSource<InfluxDbConnectionDetails, InfluxDBContainer<?>> source) {
super(source);
this.container = source.getContainer();
}

View File

@@ -1,44 +0,0 @@
/*
* 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.influx;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.influx.InfluxDbConnectionDetails;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
/**
* Annotation that indicates that a field provides an InfluxDB service connection.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
* @since 3.1.0
* @see InfluxDbConnectionDetails
* @see ServiceConnection
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.TYPE })
@ServiceConnection(InfluxDbConnectionDetails.class)
public @interface InfluxDbServiceConnection {
}

View File

@@ -21,22 +21,22 @@ import org.testcontainers.containers.JdbcDatabaseContainer;
import org.springframework.boot.autoconfigure.jdbc.JdbcConnectionDetails;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
/**
* {@link ContainerConnectionDetailsFactory} for
* {@link JdbcServiceConnection @JdbcServiceConnection}-annotated
* {@link JdbcDatabaseContainer} fields.
* {@link ContainerConnectionDetailsFactory} to create {@link JdbcConnectionDetails} from
* a {@link ServiceConnection @ServiceConnection}-annotated {@link JdbcDatabaseContainer}.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/
class JdbcContainerConnectionDetailsFactory extends
ContainerConnectionDetailsFactory<JdbcServiceConnection, JdbcConnectionDetails, JdbcDatabaseContainer<?>> {
class JdbcContainerConnectionDetailsFactory
extends ContainerConnectionDetailsFactory<JdbcConnectionDetails, JdbcDatabaseContainer<?>> {
@Override
protected JdbcConnectionDetails getContainerConnectionDetails(
ContainerConnectionSource<JdbcServiceConnection, JdbcConnectionDetails, JdbcDatabaseContainer<?>> source) {
ContainerConnectionSource<JdbcConnectionDetails, JdbcDatabaseContainer<?>> source) {
return new JdbcContainerConnectionDetails(source);
}
@@ -49,7 +49,7 @@ class JdbcContainerConnectionDetailsFactory extends
private final JdbcDatabaseContainer<?> container;
private JdbcContainerConnectionDetails(
ContainerConnectionSource<JdbcServiceConnection, JdbcConnectionDetails, JdbcDatabaseContainer<?>> source) {
ContainerConnectionSource<JdbcConnectionDetails, JdbcDatabaseContainer<?>> source) {
super(source);
this.container = source.getContainer();
}

View File

@@ -1,43 +0,0 @@
/*
* 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.jdbc;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.jdbc.JdbcConnectionDetails;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
/**
* Annotation that indicates that a field provides a JDBC database service connection.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @since 3.1.0
* @see JdbcConnectionDetails
* @see ServiceConnection
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.TYPE })
@ServiceConnection(JdbcConnectionDetails.class)
public @interface JdbcServiceConnection {
}

View File

@@ -24,22 +24,22 @@ import org.testcontainers.containers.KafkaContainer;
import org.springframework.boot.autoconfigure.kafka.KafkaConnectionDetails;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
/**
* {@link ContainerConnectionDetailsFactory} for
* {@link KafkaServiceConnection @KafkaServiceConnection}-annotated {@link KafkaContainer}
* fields.
* {@link ContainerConnectionDetailsFactory} to create {@link KafkaConnectionDetails} from
* a {@link ServiceConnection @ServiceConnection}-annotated {@link KafkaContainer}.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/
class KafkaContainerConnectionDetailsFactory
extends ContainerConnectionDetailsFactory<KafkaServiceConnection, KafkaConnectionDetails, KafkaContainer> {
extends ContainerConnectionDetailsFactory<KafkaConnectionDetails, KafkaContainer> {
@Override
protected KafkaConnectionDetails getContainerConnectionDetails(
ContainerConnectionSource<KafkaServiceConnection, KafkaConnectionDetails, KafkaContainer> source) {
ContainerConnectionSource<KafkaConnectionDetails, KafkaContainer> source) {
return new KafkaContainerConnectionDetails(source);
}
@@ -52,7 +52,7 @@ class KafkaContainerConnectionDetailsFactory
private final KafkaContainer container;
private KafkaContainerConnectionDetails(
ContainerConnectionSource<KafkaServiceConnection, KafkaConnectionDetails, KafkaContainer> source) {
ContainerConnectionSource<KafkaConnectionDetails, KafkaContainer> source) {
super(source);
this.container = source.getContainer();
}

View File

@@ -1,43 +0,0 @@
/*
* 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.kafka;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.kafka.KafkaConnectionDetails;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
/**
* Annotation that indicates that a field provides a Kafka service connection.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @since 3.1.0
* @see KafkaConnectionDetails
* @see ServiceConnection
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.TYPE })
@ServiceConnection(KafkaConnectionDetails.class)
public @interface KafkaServiceConnection {
}

View File

@@ -24,18 +24,18 @@ import org.springframework.boot.testcontainers.service.connection.ContainerConne
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
/**
* {@link ContainerConnectionDetailsFactory} for
* {@link ServiceConnection @ServiceConnection}-annotated {@link JdbcDatabaseContainer}
* fields that should produce {@link LiquibaseConnectionDetails}.
* {@link ContainerConnectionDetailsFactory} to create {@link LiquibaseConnectionDetails}
* from a {@link ServiceConnection @ServiceConnection}-annotated
* {@link JdbcDatabaseContainer}.
*
* @author Andy Wilkinson
*/
class LiquibaseContainerConnectionDetailsFactory extends
ContainerConnectionDetailsFactory<ServiceConnection, LiquibaseConnectionDetails, JdbcDatabaseContainer<?>> {
class LiquibaseContainerConnectionDetailsFactory
extends ContainerConnectionDetailsFactory<LiquibaseConnectionDetails, JdbcDatabaseContainer<?>> {
@Override
protected LiquibaseConnectionDetails getContainerConnectionDetails(
ContainerConnectionSource<ServiceConnection, LiquibaseConnectionDetails, JdbcDatabaseContainer<?>> source) {
ContainerConnectionSource<LiquibaseConnectionDetails, JdbcDatabaseContainer<?>> source) {
return new LiquibaseContainerConnectionDetails(source);
}
@@ -48,7 +48,7 @@ class LiquibaseContainerConnectionDetailsFactory extends
private final JdbcDatabaseContainer<?> container;
private LiquibaseContainerConnectionDetails(
ContainerConnectionSource<ServiceConnection, LiquibaseConnectionDetails, JdbcDatabaseContainer<?>> source) {
ContainerConnectionSource<LiquibaseConnectionDetails, JdbcDatabaseContainer<?>> source) {
super(source);
this.container = source.getContainer();
}

View File

@@ -22,22 +22,26 @@ import org.testcontainers.containers.MongoDBContainer;
import org.springframework.boot.autoconfigure.mongo.MongoConnectionDetails;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
/**
* {@link ContainerConnectionDetailsFactory} for
* {@link MongoServiceConnection @MongoServiceConnection}-annotated
* {@link MongoDBContainer} fields.
* {@link ContainerConnectionDetailsFactory} to create {@link MongoConnectionDetails} from
* a {@link ServiceConnection @ServiceConnection}-annotated {@link MongoDBContainer}.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/
class MongoContainerConnectionDetailsFactory
extends ContainerConnectionDetailsFactory<MongoServiceConnection, MongoConnectionDetails, MongoDBContainer> {
extends ContainerConnectionDetailsFactory<MongoConnectionDetails, MongoDBContainer> {
MongoContainerConnectionDetailsFactory() {
super(ANY_CONNECTION_NAME, "com.mongodb.ConnectionString");
}
@Override
protected MongoConnectionDetails getContainerConnectionDetails(
ContainerConnectionSource<MongoServiceConnection, MongoConnectionDetails, MongoDBContainer> source) {
ContainerConnectionSource<MongoConnectionDetails, MongoDBContainer> source) {
return new MongoContainerConnectionDetails(source);
}
@@ -50,7 +54,7 @@ class MongoContainerConnectionDetailsFactory
private final ConnectionString connectionString;
private MongoContainerConnectionDetails(
ContainerConnectionSource<MongoServiceConnection, MongoConnectionDetails, MongoDBContainer> source) {
ContainerConnectionSource<MongoConnectionDetails, MongoDBContainer> source) {
super(source);
this.connectionString = new ConnectionString(source.getContainer().getReplicaSetUrl());
}

View File

@@ -1,43 +0,0 @@
/*
* 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.mongo;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.mongo.MongoConnectionDetails;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
/**
* Annotation that indicates that a field provides a Mongo service connection.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @since 3.1.0
* @see MongoConnectionDetails
* @see ServiceConnection
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.TYPE })
@ServiceConnection(MongoConnectionDetails.class)
public @interface MongoServiceConnection {
}

View File

@@ -25,22 +25,26 @@ import org.testcontainers.containers.Neo4jContainer;
import org.springframework.boot.autoconfigure.neo4j.Neo4jConnectionDetails;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
/**
* {@link ContainerConnectionDetailsFactory} for
* {@link Neo4jServiceConnection @Neo4jServiceConnection}-annotated {@link Neo4jContainer}
* fields.
* {@link ContainerConnectionDetailsFactory} to create {@link Neo4jConnectionDetails} from
* a {@link ServiceConnection @ServiceConnection}-annotated {@link Neo4jContainer}.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/
class Neo4jContainerConnectionDetailsFactory
extends ContainerConnectionDetailsFactory<Neo4jServiceConnection, Neo4jConnectionDetails, Neo4jContainer<?>> {
extends ContainerConnectionDetailsFactory<Neo4jConnectionDetails, Neo4jContainer<?>> {
Neo4jContainerConnectionDetailsFactory() {
super(ANY_CONNECTION_NAME, "org.neo4j.driver.AuthToken");
}
@Override
protected Neo4jConnectionDetails getContainerConnectionDetails(
ContainerConnectionSource<Neo4jServiceConnection, Neo4jConnectionDetails, Neo4jContainer<?>> source) {
ContainerConnectionSource<Neo4jConnectionDetails, Neo4jContainer<?>> source) {
return new Neo4jContainerConnectionDetails(source);
}
@@ -53,7 +57,7 @@ class Neo4jContainerConnectionDetailsFactory
private final Neo4jContainer<?> container;
private Neo4jContainerConnectionDetails(
ContainerConnectionSource<Neo4jServiceConnection, Neo4jConnectionDetails, Neo4jContainer<?>> source) {
ContainerConnectionSource<Neo4jConnectionDetails, Neo4jContainer<?>> source) {
super(source);
this.container = source.getContainer();
}

View File

@@ -1,43 +0,0 @@
/*
* 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.neo4j;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.neo4j.Neo4jConnectionDetails;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
/**
* Annotation that indicates a field provides a Neo4j service connection.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @since 3.1.0
* @see Neo4jConnectionDetails
* @see ServiceConnection
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.TYPE })
@ServiceConnection(Neo4jConnectionDetails.class)
public @interface Neo4jServiceConnection {
}

View File

@@ -15,6 +15,6 @@
*/
/**
* General support for auto-configuration of service connections in tests.
* General support for service connections in tests.
*/
package org.springframework.boot.testcontainers.service.connection;

View File

@@ -21,25 +21,28 @@ import org.testcontainers.containers.MariaDBContainer;
import org.testcontainers.containers.MariaDBR2DBCDatabaseContainer;
import org.springframework.boot.autoconfigure.r2dbc.R2dbcConnectionDetails;
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactory;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
/**
* {@link ConnectionDetailsFactory} for
* {@link R2dbcServiceConnection @R2dbcServiceConnection}- annotated
* {@link MariaDBContainer} fields.
* {@link ContainerConnectionDetailsFactory} to create {@link R2dbcConnectionDetails} from
* a {@link ServiceConnection @ServiceConnection}-annotated {@link MariaDBContainer}.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/
class MariaDbR2dbcContainerConnectionDetailsFactory
extends ContainerConnectionDetailsFactory<R2dbcServiceConnection, R2dbcConnectionDetails, MariaDBContainer<?>> {
extends ContainerConnectionDetailsFactory<R2dbcConnectionDetails, MariaDBContainer<?>> {
MariaDbR2dbcContainerConnectionDetailsFactory() {
super(ANY_CONNECTION_NAME, "io.r2dbc.spi.ConnectionFactoryOptions");
}
@Override
public R2dbcConnectionDetails getContainerConnectionDetails(
ContainerConnectionSource<R2dbcServiceConnection, R2dbcConnectionDetails, MariaDBContainer<?>> source) {
ContainerConnectionSource<R2dbcConnectionDetails, MariaDBContainer<?>> source) {
return new R2dbcDatabaseContainerConnectionDetails(source.getContainer());
}

View File

@@ -21,25 +21,28 @@ import org.testcontainers.containers.MSSQLR2DBCDatabaseContainer;
import org.testcontainers.containers.MSSQLServerContainer;
import org.springframework.boot.autoconfigure.r2dbc.R2dbcConnectionDetails;
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactory;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
/**
* {@link ConnectionDetailsFactory} for
* {@link R2dbcServiceConnection @R2dbcServiceConnection}- annotated
* {@link MSSQLServerContainer} fields.
* {@link ContainerConnectionDetailsFactory} to create {@link R2dbcConnectionDetails} from
* a {@link ServiceConnection @ServiceConnection}-annotated {@link MSSQLServerContainer}.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/
class SqlServerR2dbcContainerConnectionDetailsFactory extends
ContainerConnectionDetailsFactory<R2dbcServiceConnection, R2dbcConnectionDetails, MSSQLServerContainer<?>> {
class MsSqlServerR2dbcContainerConnectionDetailsFactory
extends ContainerConnectionDetailsFactory<R2dbcConnectionDetails, MSSQLServerContainer<?>> {
MsSqlServerR2dbcContainerConnectionDetailsFactory() {
super(ANY_CONNECTION_NAME, "io.r2dbc.spi.ConnectionFactoryOptions");
}
@Override
public R2dbcConnectionDetails getContainerConnectionDetails(
ContainerConnectionSource<R2dbcServiceConnection, R2dbcConnectionDetails, MSSQLServerContainer<?>> source) {
ContainerConnectionSource<R2dbcConnectionDetails, MSSQLServerContainer<?>> source) {
return new R2dbcDatabaseContainerConnectionDetails(source.getContainer());
}

View File

@@ -21,25 +21,28 @@ import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.containers.MySQLR2DBCDatabaseContainer;
import org.springframework.boot.autoconfigure.r2dbc.R2dbcConnectionDetails;
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactory;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
/**
* {@link ConnectionDetailsFactory} for
* {@link R2dbcServiceConnection @R2dbcServiceConnection}- annotated
* {@link MySQLContainer} fields.
* {@link ContainerConnectionDetailsFactory} to create {@link R2dbcConnectionDetails} from
* a {@link ServiceConnection @ServiceConnection}-annotated {@link MySQLContainer}.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/
class MySqlR2dbcContainerConnectionDetailsFactory
extends ContainerConnectionDetailsFactory<R2dbcServiceConnection, R2dbcConnectionDetails, MySQLContainer<?>> {
extends ContainerConnectionDetailsFactory<R2dbcConnectionDetails, MySQLContainer<?>> {
MySqlR2dbcContainerConnectionDetailsFactory() {
super(ANY_CONNECTION_NAME, "io.r2dbc.spi.ConnectionFactoryOptions");
}
@Override
public R2dbcConnectionDetails getContainerConnectionDetails(
ContainerConnectionSource<R2dbcServiceConnection, R2dbcConnectionDetails, MySQLContainer<?>> source) {
ContainerConnectionSource<R2dbcConnectionDetails, MySQLContainer<?>> source) {
return new R2dbcDatabaseContainerConnectionDetails(source.getContainer());
}

View File

@@ -21,24 +21,28 @@ import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.containers.PostgreSQLR2DBCDatabaseContainer;
import org.springframework.boot.autoconfigure.r2dbc.R2dbcConnectionDetails;
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactory;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
/**
* {@link ConnectionDetailsFactory} for {@link R2dbcServiceConnection @R2dbcConnection}
* annotated {@link PostgreSQLContainer} fields.
* {@link ContainerConnectionDetailsFactory} to create {@link R2dbcConnectionDetails} from
* a {@link ServiceConnection @ServiceConnection}-annotated {@link PostgreSQLContainer}.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/
class PostgresR2dbcContainerConnectionDetailsFactory extends
ContainerConnectionDetailsFactory<R2dbcServiceConnection, R2dbcConnectionDetails, PostgreSQLContainer<?>> {
class PostgresR2dbcContainerConnectionDetailsFactory
extends ContainerConnectionDetailsFactory<R2dbcConnectionDetails, PostgreSQLContainer<?>> {
PostgresR2dbcContainerConnectionDetailsFactory() {
super(ANY_CONNECTION_NAME, "io.r2dbc.spi.ConnectionFactoryOptions");
}
@Override
public R2dbcConnectionDetails getContainerConnectionDetails(
ContainerConnectionSource<R2dbcServiceConnection, R2dbcConnectionDetails, PostgreSQLContainer<?>> source) {
ContainerConnectionSource<R2dbcConnectionDetails, PostgreSQLContainer<?>> source) {
return new R2dbcDatabaseContainerConnectionDetails(source.getContainer());
}

View File

@@ -1,43 +0,0 @@
/*
* 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.r2dbc;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.r2dbc.R2dbcConnectionDetails;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
/**
* Annotation that indicates a field provides a R2DBC database service connection.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @since 3.1.0
* @see ServiceConnection
* @see R2dbcConnectionDetails
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.TYPE })
@ServiceConnection(R2dbcConnectionDetails.class)
public @interface R2dbcServiceConnection {
}

View File

@@ -16,27 +16,33 @@
package org.springframework.boot.testcontainers.service.connection.redis;
import org.testcontainers.containers.Container;
import org.testcontainers.containers.GenericContainer;
import org.springframework.boot.autoconfigure.data.redis.RedisConnectionDetails;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
/**
* {@link ContainerConnectionDetailsFactory} for
* {@link RedisServiceConnection @RedisServiceConnection}-annotated
* {@link GenericContainer} fields.
* {@link ContainerConnectionDetailsFactory} to create {@link RedisConnectionDetails} from
* a {@link ServiceConnection @ServiceConnection}-annotated {@link GenericContainer} using
* the {@code "redis"} image.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/
class RedisContainerConnectionDetailsFactory
extends ContainerConnectionDetailsFactory<RedisServiceConnection, RedisConnectionDetails, GenericContainer<?>> {
extends ContainerConnectionDetailsFactory<RedisConnectionDetails, Container<?>> {
RedisContainerConnectionDetailsFactory() {
super("redis");
}
@Override
public RedisConnectionDetails getContainerConnectionDetails(
ContainerConnectionSource<RedisServiceConnection, RedisConnectionDetails, GenericContainer<?>> source) {
ContainerConnectionSource<RedisConnectionDetails, Container<?>> source) {
return new RedisContainerConnectionDetails(source);
}
@@ -49,7 +55,7 @@ class RedisContainerConnectionDetailsFactory
private final Standalone standalone;
private RedisContainerConnectionDetails(
ContainerConnectionSource<RedisServiceConnection, RedisConnectionDetails, GenericContainer<?>> source) {
ContainerConnectionSource<RedisConnectionDetails, Container<?>> source) {
super(source);
this.standalone = Standalone.of(source.getContainer().getHost(),
source.getContainer().getFirstMappedPort());

View File

@@ -1,43 +0,0 @@
/*
* 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.redis;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.data.redis.RedisConnectionDetails;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
/**
* Annotation that indicates that a field provides a Redis service connection.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @since 3.1.0
* @see RedisConnectionDetails
* @see ServiceConnection
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.TYPE })
@ServiceConnection(RedisConnectionDetails.class)
public @interface RedisServiceConnection {
}

View File

@@ -8,7 +8,6 @@ org.springframework.boot.testcontainers.service.connection.amqp.RabbitContainerC
org.springframework.boot.testcontainers.service.connection.cassandra.CassandraContainerConnectionDetailsFactory,\
org.springframework.boot.testcontainers.service.connection.couchbase.CouchbaseContainerConnectionDetailsFactory,\
org.springframework.boot.testcontainers.service.connection.flyway.FlywayContainerConnectionDetailsFactory,\
org.springframework.boot.testcontainers.service.connection.redis.RedisContainerConnectionDetailsFactory,\
org.springframework.boot.testcontainers.service.connection.elasticsearch.ElasticsearchContainerConnectionDetailsFactory,\
org.springframework.boot.testcontainers.service.connection.influx.InfluxDbContainerConnectionDetailsFactory,\
org.springframework.boot.testcontainers.service.connection.jdbc.JdbcContainerConnectionDetailsFactory,\
@@ -17,6 +16,7 @@ org.springframework.boot.testcontainers.service.connection.liquibase.LiquibaseCo
org.springframework.boot.testcontainers.service.connection.mongo.MongoContainerConnectionDetailsFactory,\
org.springframework.boot.testcontainers.service.connection.neo4j.Neo4jContainerConnectionDetailsFactory,\
org.springframework.boot.testcontainers.service.connection.r2dbc.MariaDbR2dbcContainerConnectionDetailsFactory,\
org.springframework.boot.testcontainers.service.connection.r2dbc.MsSqlServerR2dbcContainerConnectionDetailsFactory,\
org.springframework.boot.testcontainers.service.connection.r2dbc.MySqlR2dbcContainerConnectionDetailsFactory,\
org.springframework.boot.testcontainers.service.connection.r2dbc.PostgresR2dbcContainerConnectionDetailsFactory,\
org.springframework.boot.testcontainers.service.connection.r2dbc.SqlServerR2dbcContainerConnectionDetailsFactory
org.springframework.boot.testcontainers.service.connection.redis.RedisContainerConnectionDetailsFactory