Skip to content

Commit 6c3dec4

Browse files
committed
Add container support for Oracle Free which replaces Oracle XE
Update Docker Compose and Testcontainers support to work with `gvenzl/oracle-free` which replaces `gvenzl/oracle-xe`. Closes gh-38476
1 parent 0897d75 commit 6c3dec4

File tree

18 files changed

+343
-19
lines changed

18 files changed

+343
-19
lines changed

spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/ConnectionNamePredicate.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,28 +16,33 @@
1616

1717
package org.springframework.boot.docker.compose.service.connection;
1818

19+
import java.util.Arrays;
20+
import java.util.Set;
1921
import java.util.function.Predicate;
22+
import java.util.stream.Collectors;
2023

2124
import org.springframework.boot.docker.compose.core.ImageReference;
2225
import org.springframework.boot.docker.compose.core.RunningService;
26+
import org.springframework.util.Assert;
2327

2428
/**
25-
* {@link Predicate} that matches against connection names.
29+
* {@link Predicate} that matches against connection name.
2630
*
2731
* @author Phillip Webb
2832
*/
2933
class ConnectionNamePredicate implements Predicate<DockerComposeConnectionSource> {
3034

31-
private final String required;
35+
private final Set<String> required;
3236

33-
ConnectionNamePredicate(String required) {
34-
this.required = asCanonicalName(required);
37+
ConnectionNamePredicate(String... required) {
38+
Assert.notEmpty(required, "Required must not be empty");
39+
this.required = Arrays.stream(required).map(this::asCanonicalName).collect(Collectors.toSet());
3540
}
3641

3742
@Override
3843
public boolean test(DockerComposeConnectionSource source) {
3944
String actual = getActual(source.getRunningService());
40-
return this.required.equals(actual);
45+
return this.required.contains(actual);
4146
}
4247

4348
private String getActual(RunningService service) {

spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/DockerComposeConnectionDetailsFactory.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,16 @@ protected DockerComposeConnectionDetailsFactory(String connectionName, String...
5454
this(new ConnectionNamePredicate(connectionName), requiredClassNames);
5555
}
5656

57+
/**
58+
* Create a new {@link DockerComposeConnectionDetailsFactory} instance.
59+
* @param connectionNames the required connection name
60+
* @param requiredClassNames the names of classes that must be present
61+
* @since 3.2.0
62+
*/
63+
protected DockerComposeConnectionDetailsFactory(String[] connectionNames, String... requiredClassNames) {
64+
this(new ConnectionNamePredicate(connectionNames), requiredClassNames);
65+
}
66+
5767
/**
5868
* Create a new {@link DockerComposeConnectionDetailsFactory} instance.
5969
* @param predicate a predicate used to check when a service is accepted

spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/oracle/OracleEnvironment.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
*/
2929
class OracleEnvironment {
3030

31+
static final String[] CONTAINER_NAMES = { "gvenzl/oracle-xe", "gvenzl/oracle-free" };
32+
3133
private final String username;
3234

3335
private final String password;

spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/oracle/OracleJdbcDockerComposeConnectionDetailsFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class OracleJdbcDockerComposeConnectionDetailsFactory
3434
extends DockerComposeConnectionDetailsFactory<JdbcConnectionDetails> {
3535

3636
protected OracleJdbcDockerComposeConnectionDetailsFactory() {
37-
super("gvenzl/oracle-xe");
37+
super(OracleEnvironment.CONTAINER_NAMES);
3838
}
3939

4040
@Override

spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/oracle/OracleR2dbcDockerComposeConnectionDetailsFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class OracleR2dbcDockerComposeConnectionDetailsFactory
3636
extends DockerComposeConnectionDetailsFactory<R2dbcConnectionDetails> {
3737

3838
OracleR2dbcDockerComposeConnectionDetailsFactory() {
39-
super("gvenzl/oracle-xe", "io.r2dbc.spi.ConnectionFactoryOptions");
39+
super(OracleEnvironment.CONTAINER_NAMES, "io.r2dbc.spi.ConnectionFactoryOptions");
4040
}
4141

4242
@Override

spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/service/connection/ConnectionNamePredicateTests.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,14 @@ void labeled() {
7474
.accepts(sourceOf("internalhost:8080/libs/libs/mzipkin", "openzipkin/zipkin"));
7575
}
7676

77-
private Predicate<DockerComposeConnectionSource> predicateOf(String required) {
77+
@Test
78+
void multiple() {
79+
assertThat(predicateOf("elasticsearch1", "elasticsearch2")).accepts(sourceOf("elasticsearch1"))
80+
.accepts(sourceOf("elasticsearch2"));
81+
82+
}
83+
84+
private Predicate<DockerComposeConnectionSource> predicateOf(String... required) {
7885
return new ConnectionNamePredicate(required);
7986
}
8087

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright 2012-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.docker.compose.service.connection.oracle;
18+
19+
import java.sql.Driver;
20+
import java.time.Duration;
21+
22+
import org.awaitility.Awaitility;
23+
import org.junit.jupiter.api.Test;
24+
import org.junit.jupiter.api.condition.OS;
25+
26+
import org.springframework.boot.autoconfigure.jdbc.JdbcConnectionDetails;
27+
import org.springframework.boot.docker.compose.service.connection.test.AbstractDockerComposeIntegrationTests;
28+
import org.springframework.boot.jdbc.DatabaseDriver;
29+
import org.springframework.boot.testsupport.junit.DisabledOnOs;
30+
import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
31+
import org.springframework.jdbc.core.JdbcTemplate;
32+
import org.springframework.jdbc.datasource.SimpleDriverDataSource;
33+
import org.springframework.util.ClassUtils;
34+
35+
import static org.assertj.core.api.Assertions.assertThat;
36+
37+
/**
38+
* Integration tests for {@link OracleJdbcDockerComposeConnectionDetailsFactory}
39+
*
40+
* @author Andy Wilkinson
41+
*/
42+
@DisabledOnOs(os = { OS.LINUX, OS.MAC }, architecture = "aarch64",
43+
disabledReason = "The Oracle image has no ARM support")
44+
class OracleFreeJdbcDockerComposeConnectionDetailsFactoryIntegrationTests
45+
extends AbstractDockerComposeIntegrationTests {
46+
47+
OracleFreeJdbcDockerComposeConnectionDetailsFactoryIntegrationTests() {
48+
super("oracle-compose.yaml", DockerImageNames.oracleFree());
49+
}
50+
51+
@Test
52+
@SuppressWarnings("unchecked")
53+
void runCreatesConnectionDetailsThatCanBeUsedToAccessDatabase() throws Exception {
54+
JdbcConnectionDetails connectionDetails = run(JdbcConnectionDetails.class);
55+
assertThat(connectionDetails.getUsername()).isEqualTo("app_user");
56+
assertThat(connectionDetails.getPassword()).isEqualTo("app_user_secret");
57+
assertThat(connectionDetails.getJdbcUrl()).startsWith("jdbc:oracle:thin:@").endsWith("/xepdb1");
58+
SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
59+
dataSource.setUrl(connectionDetails.getJdbcUrl());
60+
dataSource.setUsername(connectionDetails.getUsername());
61+
dataSource.setPassword(connectionDetails.getPassword());
62+
dataSource.setDriverClass((Class<? extends Driver>) ClassUtils.forName(connectionDetails.getDriverClassName(),
63+
getClass().getClassLoader()));
64+
Awaitility.await().atMost(Duration.ofMinutes(1)).ignoreExceptions().untilAsserted(() -> {
65+
JdbcTemplate template = new JdbcTemplate(dataSource);
66+
assertThat(template.queryForObject(DatabaseDriver.ORACLE.getValidationQuery(), String.class))
67+
.isEqualTo("Hello");
68+
});
69+
}
70+
71+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright 2012-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.docker.compose.service.connection.oracle;
18+
19+
import java.time.Duration;
20+
21+
import io.r2dbc.spi.ConnectionFactories;
22+
import io.r2dbc.spi.ConnectionFactoryOptions;
23+
import org.awaitility.Awaitility;
24+
import org.junit.jupiter.api.Test;
25+
import org.junit.jupiter.api.condition.OS;
26+
27+
import org.springframework.boot.autoconfigure.r2dbc.R2dbcConnectionDetails;
28+
import org.springframework.boot.docker.compose.service.connection.test.AbstractDockerComposeIntegrationTests;
29+
import org.springframework.boot.jdbc.DatabaseDriver;
30+
import org.springframework.boot.testsupport.junit.DisabledOnOs;
31+
import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
32+
import org.springframework.r2dbc.core.DatabaseClient;
33+
34+
import static org.assertj.core.api.Assertions.assertThat;
35+
36+
/**
37+
* Integration tests for {@link OracleR2dbcDockerComposeConnectionDetailsFactory}
38+
*
39+
* @author Andy Wilkinson
40+
*/
41+
@DisabledOnOs(os = { OS.LINUX, OS.MAC }, architecture = "aarch64",
42+
disabledReason = "The Oracle image has no ARM support")
43+
class OracleFreeR2dbcDockerComposeConnectionDetailsFactoryIntegrationTests
44+
extends AbstractDockerComposeIntegrationTests {
45+
46+
OracleFreeR2dbcDockerComposeConnectionDetailsFactoryIntegrationTests() {
47+
super("oracle-compose.yaml", DockerImageNames.oracleFree());
48+
}
49+
50+
@Test
51+
void runCreatesConnectionDetailsThatCanBeUsedToAccessDatabase() {
52+
R2dbcConnectionDetails connectionDetails = run(R2dbcConnectionDetails.class);
53+
ConnectionFactoryOptions connectionFactoryOptions = connectionDetails.getConnectionFactoryOptions();
54+
assertThat(connectionFactoryOptions.toString()).contains("database=xepdb1", "driver=oracle",
55+
"password=REDACTED", "user=app_user");
56+
assertThat(connectionFactoryOptions.getRequiredValue(ConnectionFactoryOptions.PASSWORD))
57+
.isEqualTo("app_user_secret");
58+
Awaitility.await().atMost(Duration.ofMinutes(1)).ignoreExceptions().untilAsserted(() -> {
59+
Object result = DatabaseClient.create(ConnectionFactories.get(connectionFactoryOptions))
60+
.sql(DatabaseDriver.ORACLE.getValidationQuery())
61+
.map((row, metadata) -> row.get(0))
62+
.first()
63+
.block(Duration.ofSeconds(30));
64+
assertThat(result).isEqualTo("Hello");
65+
});
66+
}
67+
68+
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,9 @@
4141
*/
4242
@DisabledOnOs(os = { OS.LINUX, OS.MAC }, architecture = "aarch64",
4343
disabledReason = "The Oracle image has no ARM support")
44-
class OracleJdbcDockerComposeConnectionDetailsFactoryIntegrationTests extends AbstractDockerComposeIntegrationTests {
44+
class OracleXeJdbcDockerComposeConnectionDetailsFactoryIntegrationTests extends AbstractDockerComposeIntegrationTests {
4545

46-
OracleJdbcDockerComposeConnectionDetailsFactoryIntegrationTests() {
46+
OracleXeJdbcDockerComposeConnectionDetailsFactoryIntegrationTests() {
4747
super("oracle-compose.yaml", DockerImageNames.oracleXe());
4848
}
4949

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@
4040
*/
4141
@DisabledOnOs(os = { OS.LINUX, OS.MAC }, architecture = "aarch64",
4242
disabledReason = "The Oracle image has no ARM support")
43-
class OracleR2dbcDockerComposeConnectionDetailsFactoryIntegrationTests extends AbstractDockerComposeIntegrationTests {
43+
class OracleXeR2dbcDockerComposeConnectionDetailsFactoryIntegrationTests extends AbstractDockerComposeIntegrationTests {
4444

45-
OracleR2dbcDockerComposeConnectionDetailsFactoryIntegrationTests() {
45+
OracleXeR2dbcDockerComposeConnectionDetailsFactoryIntegrationTests() {
4646
super("oracle-compose.yaml", DockerImageNames.oracleXe());
4747
}
4848

spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/docker-compose.adoc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ The following service connections are currently supported:
7474
| Containers named "elasticsearch"
7575

7676
| `JdbcConnectionDetails`
77-
| Containers named "gvenzl/oracle-xe", "mariadb", "mssql/server", "mysql", or "postgres"
77+
| Containers named "gvenzl/oracle-free", "gvenzl/oracle-xe", "mariadb", "mssql/server", "mysql", or "postgres"
7878

7979
| `MongoConnectionDetails`
8080
| Containers named "mongo"
@@ -92,7 +92,7 @@ The following service connections are currently supported:
9292
| Containers named "apachepulsar/pulsar"
9393

9494
| `R2dbcConnectionDetails`
95-
| Containers named "gvenzl/oracle-xe", "mariadb", "mssql/server", "mysql", or "postgres"
95+
| Containers named "gvenzl/oracle-free", "gvenzl/oracle-xe", "mariadb", "mssql/server", "mysql", or "postgres"
9696

9797
| `RabbitConnectionDetails`
9898
| Containers named "rabbitmq"

spring-boot-project/spring-boot-testcontainers/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ dependencies {
2929
optional("org.testcontainers:mysql")
3030
optional("org.testcontainers:neo4j")
3131
optional("org.testcontainers:oracle-xe")
32+
optional("org.testcontainers:oracle-free")
3233
optional("org.testcontainers:postgresql")
3334
optional("org.testcontainers:pulsar")
3435
optional("org.testcontainers:rabbitmq")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2012-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.testcontainers.service.connection.r2dbc;
18+
19+
import io.r2dbc.spi.ConnectionFactoryOptions;
20+
import org.testcontainers.oracle.OracleContainer;
21+
import org.testcontainers.oracle.OracleR2DBCDatabaseContainer;
22+
23+
import org.springframework.boot.autoconfigure.r2dbc.R2dbcConnectionDetails;
24+
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
25+
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
26+
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
27+
28+
/**
29+
* {@link ContainerConnectionDetailsFactory} to create {@link R2dbcConnectionDetails} from
30+
* a {@link ServiceConnection @ServiceConnection}-annotated {@link OracleContainer}.
31+
*
32+
* @author Eddú Meléndez
33+
*/
34+
class OracleFreeR2dbcContainerConnectionDetailsFactory
35+
extends ContainerConnectionDetailsFactory<OracleContainer, R2dbcConnectionDetails> {
36+
37+
OracleFreeR2dbcContainerConnectionDetailsFactory() {
38+
super(ANY_CONNECTION_NAME, "io.r2dbc.spi.ConnectionFactoryOptions");
39+
}
40+
41+
@Override
42+
public R2dbcConnectionDetails getContainerConnectionDetails(ContainerConnectionSource<OracleContainer> source) {
43+
return new R2dbcDatabaseContainerConnectionDetails(source);
44+
}
45+
46+
/**
47+
* {@link R2dbcConnectionDetails} backed by a {@link ContainerConnectionSource}.
48+
*/
49+
private static final class R2dbcDatabaseContainerConnectionDetails
50+
extends ContainerConnectionDetails<OracleContainer> implements R2dbcConnectionDetails {
51+
52+
private R2dbcDatabaseContainerConnectionDetails(ContainerConnectionSource<OracleContainer> source) {
53+
super(source);
54+
}
55+
56+
@Override
57+
public ConnectionFactoryOptions getConnectionFactoryOptions() {
58+
return OracleR2DBCDatabaseContainer.getOptions(getContainer());
59+
}
60+
61+
}
62+
63+
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@
3131
*
3232
* @author Eddú Meléndez
3333
*/
34-
class OracleR2dbcContainerConnectionDetailsFactory
34+
class OracleXeR2dbcContainerConnectionDetailsFactory
3535
extends ContainerConnectionDetailsFactory<OracleContainer, R2dbcConnectionDetails> {
3636

37-
OracleR2dbcContainerConnectionDetailsFactory() {
37+
OracleXeR2dbcContainerConnectionDetailsFactory() {
3838
super(ANY_CONNECTION_NAME, "io.r2dbc.spi.ConnectionFactoryOptions");
3939
}
4040

spring-boot-project/spring-boot-testcontainers/src/main/resources/META-INF/spring.factories

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ org.springframework.boot.testcontainers.service.connection.otlp.OpenTelemetryTra
2424
org.springframework.boot.testcontainers.service.connection.pulsar.PulsarContainerConnectionDetailsFactory,\
2525
org.springframework.boot.testcontainers.service.connection.r2dbc.MariaDbR2dbcContainerConnectionDetailsFactory,\
2626
org.springframework.boot.testcontainers.service.connection.r2dbc.MySqlR2dbcContainerConnectionDetailsFactory,\
27-
org.springframework.boot.testcontainers.service.connection.r2dbc.OracleR2dbcContainerConnectionDetailsFactory,\
27+
org.springframework.boot.testcontainers.service.connection.r2dbc.OracleFreeR2dbcContainerConnectionDetailsFactory,\
28+
org.springframework.boot.testcontainers.service.connection.r2dbc.OracleXeR2dbcContainerConnectionDetailsFactory,\
2829
org.springframework.boot.testcontainers.service.connection.r2dbc.PostgresR2dbcContainerConnectionDetailsFactory,\
2930
org.springframework.boot.testcontainers.service.connection.r2dbc.SqlServerR2dbcContainerConnectionDetailsFactory,\
3031
org.springframework.boot.testcontainers.service.connection.redis.RedisContainerConnectionDetailsFactory,\

0 commit comments

Comments
 (0)