Merge branch '4.1.x'

Closes gh-51629
This commit is contained in:
Stéphane Nicoll
2026-09-08 17:29:34 +02:00
135 changed files with 214 additions and 370 deletions
@@ -1,33 +0,0 @@
/*
* Copyright 2012-present 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.testsupport.container;
import org.testcontainers.activemq.ActiveMQContainer;
import org.testcontainers.utility.DockerImageName;
/**
* An {@link ActiveMQContainer} for ActiveMQ classic.
*
* @author Moritz Halbritter
*/
public final class ActiveMQClassicContainer extends ActiveMQContainer {
public ActiveMQClassicContainer(DockerImageName dockerImageName) {
super(dockerImageName);
}
}
@@ -1,32 +0,0 @@
/*
* Copyright 2012-present 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.testsupport.container;
import org.testcontainers.utility.DockerImageName;
/**
* An {@link org.testcontainers.activemq.ActiveMQContainer} for ActiveMQ.
*
* @author Moritz Halbritter
*/
public final class ActiveMQContainer extends org.testcontainers.activemq.ActiveMQContainer {
public ActiveMQContainer(DockerImageName dockerImageName) {
super(dockerImageName);
}
}
@@ -1,32 +0,0 @@
/*
* Copyright 2012-present 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.testsupport.container;
import org.testcontainers.utility.DockerImageName;
/**
* An {@link org.testcontainers.activemq.ArtemisContainer} for {@code apache/artemis}.
*
* @author Moritz Halbritter
*/
public final class ArtemisContainer extends org.testcontainers.activemq.ArtemisContainer {
public ArtemisContainer(DockerImageName dockerImageName) {
super(dockerImageName);
}
}
@@ -1,33 +0,0 @@
/*
* Copyright 2012-present 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.testsupport.container;
import org.testcontainers.activemq.ArtemisContainer;
import org.testcontainers.utility.DockerImageName;
/**
* An {@link ArtemisContainer} for {@code apache/activemq-artemis}.
*
* @author Moritz Halbritter
*/
public final class ArtemisLegacyContainer extends ArtemisContainer {
public ArtemisLegacyContainer(DockerImageName dockerImageName) {
super(dockerImageName);
}
}
@@ -1,34 +0,0 @@
/*
* Copyright 2012-present 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.testsupport.container;
import org.testcontainers.elasticsearch.ElasticsearchContainer;
/**
* A container suitable for testing Elasticsearch 9.
*
* @author Dmytro Nosan
*/
public class ElasticsearchContainer9 extends ElasticsearchContainer {
public ElasticsearchContainer9() {
super(TestImage.ELASTICSEARCH_9.toString());
addEnv("ES_JAVA_OPTS", "-Xms32m -Xmx512m");
addEnv("xpack.security.enabled", "false");
}
}
@@ -1,36 +0,0 @@
/*
* Copyright 2012-present 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.testsupport.container;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.utility.DockerImageName;
/**
* A {@link GenericContainer} for Symptoma/ActiveMQ.
*
* @author Stephane Nicoll
*/
public final class SymptomaActiveMQContainer extends GenericContainer<SymptomaActiveMQContainer> {
private static final int DEFAULT_PORT = 61616;
public SymptomaActiveMQContainer(DockerImageName dockerImageName) {
super(dockerImageName);
addExposedPorts(DEFAULT_PORT);
}
}
@@ -25,6 +25,8 @@ import java.util.function.Supplier;
import com.redis.testcontainers.RedisContainer;
import com.redis.testcontainers.RedisStackContainer;
import org.testcontainers.activemq.ActiveMQContainer;
import org.testcontainers.activemq.ArtemisContainer;
import org.testcontainers.cassandra.CassandraContainer;
import org.testcontainers.containers.Container;
import org.testcontainers.containers.GenericContainer;
@@ -48,8 +50,11 @@ import org.springframework.util.Assert;
/**
* References to container images used for integration tests. This class also acts a
* central location for tests to {@link #container(Class) create} a correctly configured
* {@link Container testcontainer}.
* central location for {@link #container(Class) creating} a correctly configured
* {@link Container testcontainer}. For uniquely defined containers (one image matching
* one container), {@link #forContainer(Class)} creates a container based on its type. For
* other cases, or to be explicit, use {@link #container(Class)} on the image the test
* requires.
*
* @author Stephane Nicoll
* @author Eddú Meléndez
@@ -62,7 +67,8 @@ public enum TestImage {
/**
* A container image suitable for testing ActiveMQ made by Symptoma.
*/
ACTIVE_MQ_SYMPTOMA("symptoma/activemq", "5.18.3", () -> SymptomaActiveMQContainer.class),
ACTIVE_MQ_SYMPTOMA("symptoma/activemq", "5.18.3",
(container) -> ((GenericContainer<?>) container).addExposedPorts(61616)),
/**
* A container image suitable for testing ActiveMQ.
@@ -72,7 +78,7 @@ public enum TestImage {
/**
* A container image suitable for testing ActiveMQ classic.
*/
ACTIVE_MQ_CLASSIC("apache/activemq-classic", "5.18.3", () -> ActiveMQClassicContainer.class),
ACTIVE_MQ_CLASSIC("apache/activemq-classic", "5.18.3", () -> ActiveMQContainer.class),
/**
* A container image suitable for testing Apache Kafka.
@@ -88,7 +94,7 @@ public enum TestImage {
* A container image suitable for testing Artemis using the legacy
* {@code apache/activemq-artemis} image.
*/
ARTEMIS_LEGACY("apache/activemq-artemis", "2.34.0", () -> ArtemisLegacyContainer.class),
ARTEMIS_LEGACY("apache/activemq-artemis", "2.34.0", () -> ArtemisContainer.class),
/**
* A container image suitable for testing Cassandra.
@@ -124,7 +130,11 @@ public enum TestImage {
/**
* A container image suitable for testing Elasticsearch 9.
*/
ELASTICSEARCH_9("elasticsearch", "9.0.2"),
ELASTICSEARCH_9("elasticsearch", "9.0.2", () -> ElasticsearchContainer.class, (container) -> {
ElasticsearchContainer elasticsearchContainer = (ElasticsearchContainer) container;
elasticsearchContainer.addEnv("ES_JAVA_OPTS", "-Xms32m -Xmx512m");
elasticsearchContainer.addEnv("xpack.security.enabled", "false");
}),
/**
* A container image from Elastic Registry suitable for testing Elasticsearch 9.
@@ -317,20 +327,45 @@ public enum TestImage {
}
}
private boolean matchesContainerClass(Class<?> containerClass) {
return this.containerClass != null && this.containerClass.isAssignableFrom(containerClass);
/**
* Create a container with the default container type.
* @param <C> the type of container this instance produces
* @return a container with default settings
*/
public <C extends Container<?>> C container() {
return container((Consumer<C>) null);
}
/**
* Create a {@link GenericContainer} for the given {@link TestImage}.
* @return a generic container for the test image
* Create a container with the default container type, and customize it before it is
* started.
* @param setup a consumer of the container
* @param <C> the type of container this instance produces
* @return a container with default settings, further customized by the given
* consumer.
*/
public GenericContainer<?> genericContainer() {
return createContainer(GenericContainer.class);
@SuppressWarnings({ "rawtypes", "unchecked" })
public <C extends Container<?>> C container(Consumer<C> setup) {
if (this.containerClass != null) {
return createContainer((Class<C>) this.containerClass, setup);
}
else {
return (C) createContainer(GenericContainer.class, (Consumer) setup);
}
}
/**
* Create a container for the given {@code containerClass}.
* @param containerClass the container type
* @param <C> the requested type of container
* @return a container with default settings
*/
public <C extends Container<?>> C container(Class<? extends C> containerClass) {
return createContainer(containerClass, null);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private <C extends Container<?>> C createContainer(Class<C> containerClass) {
private <C extends Container<?>> C createContainer(Class<C> containerClass, Consumer<C> userSetup) {
DockerImageName dockerImageName = DockerImageName.parse(toString());
try {
Constructor<C> constructor = containerClass.getDeclaredConstructor(DockerImageName.class);
@@ -339,6 +374,9 @@ public enum TestImage {
if (this.containerSetup != null) {
((Consumer) this.containerSetup).accept(container);
}
if (userSetup != null) {
userSetup.accept(container);
}
return container;
}
catch (Exception ex) {
@@ -357,13 +395,14 @@ public enum TestImage {
/**
* Factory method to create and configure a {@link Container} using a deduced
* {@link TestImage}.
* {@link TestImage}. At most one {@link TestImage} should match with the given
* {@code containerClass}.
* @param <C> the container type
* @param containerClass the container type
* @return a container instance
*/
public static <C extends Container<?>> C container(Class<C> containerClass) {
return forContainerClass(containerClass).createContainer(containerClass);
public static <C extends Container<?>> C forContainer(Class<C> containerClass) {
return forContainerClass(containerClass).createContainer(containerClass, null);
}
private static TestImage forContainerClass(Class<?> containerClass) {
@@ -375,4 +414,8 @@ public enum TestImage {
return images.get(0);
}
private boolean matchesContainerClass(Class<?> containerClass) {
return this.containerClass != null && this.containerClass.isAssignableFrom(containerClass);
}
}