diff --git a/modules/oracle-xe/build.gradle b/modules/oracle-xe/build.gradle index 5a6040ac81f..38146d4dd01 100644 --- a/modules/oracle-xe/build.gradle +++ b/modules/oracle-xe/build.gradle @@ -5,4 +5,6 @@ dependencies { testImplementation project(':jdbc-test') testImplementation 'com.oracle.ojdbc:ojdbc8:19.3.0.0' + + compileOnly 'org.jetbrains:annotations:21.0.1' } diff --git a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java index ccd705f5af9..e67d578d235 100644 --- a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java +++ b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java @@ -2,19 +2,24 @@ import com.google.common.collect.Sets; import org.apache.commons.lang.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; import org.testcontainers.utility.DockerImageName; +import java.time.Duration; import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.concurrent.Future; -public class OracleContainer extends JdbcDatabaseContainer { +import static java.time.temporal.ChronoUnit.SECONDS; +import static java.util.Collections.singleton; + +public class OracleContainer> extends JdbcDatabaseContainer { public static final String NAME = "oracle"; private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("gvenzl/oracle-xe"); - static final String DEFAULT_TAG = "18.4.0-slim"; static final String IMAGE = DEFAULT_IMAGE_NAME.getUnversionedPart(); @@ -23,10 +28,21 @@ public class OracleContainer extends JdbcDatabaseContainer { private static final int DEFAULT_STARTUP_TIMEOUT_SECONDS = 240; private static final int DEFAULT_CONNECT_TIMEOUT_SECONDS = 120; - private static final List ORACLE_SYSTEM_USERS = Arrays.asList("system", "sys"); - private String username = "test"; - private String password = "test"; + static final String DEFAULT_DATABASE_NAME = "xepdb1"; //Container always starts with this database + static final String DEFAULT_APP_USER = "test"; + static final String DEFAULT_SYSTEM_USER = "system"; + static final String DEFAULT_SYS_USER = "sys"; + static final String DEFUALT_SHARED_PASSWORD = "test"; //System and App users will share a password + + private static final List ORACLE_SYSTEM_USERS = Arrays.asList(DEFAULT_SYSTEM_USER, DEFAULT_SYS_USER); + private static final List ORACLE_DEFAULT_DBS = Arrays.asList(DEFAULT_DATABASE_NAME); + + private String databaseName = DEFAULT_DATABASE_NAME; + private String username = DEFAULT_APP_USER; + private String password = DEFUALT_SHARED_PASSWORD; + + private boolean usingSid = false; /** * @deprecated use {@link OracleContainer(DockerImageName)} instead @@ -51,14 +67,33 @@ public OracleContainer(Future dockerImageName) { } private void preconfigure() { - withStartupTimeoutSeconds(DEFAULT_STARTUP_TIMEOUT_SECONDS); + this.waitStrategy = new LogMessageWaitStrategy() + .withRegEx(".*DATABASE IS READY TO USE!.*\\s") + .withTimes(1) + .withStartupTimeout(Duration.of(DEFAULT_STARTUP_TIMEOUT_SECONDS, SECONDS)); + withConnectTimeoutSeconds(DEFAULT_CONNECT_TIMEOUT_SECONDS); + addExposedPorts(ORACLE_PORT, APEX_HTTP_PORT); } + @NotNull @Override public Set getLivenessCheckPortNumbers() { - return Sets.newHashSet(ORACLE_PORT); + return singleton(getMappedPort(ORACLE_PORT)); + } + + @Override + protected void configure() { + withEnv("ORACLE_PASSWORD", password); + + //Only set ORACLE_DATABASE if different than the default + if(databaseName != DEFAULT_DATABASE_NAME) { + withEnv("ORACLE_DATABASE", databaseName); + } + + withEnv("APP_USER", username); + withEnv("APP_USER_PASSWORD", password); } @Override @@ -68,12 +103,29 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - return "jdbc:oracle:thin:" + getUsername() + "/" + getPassword() + "@" + getHost() + ":" + getOraclePort() + "/xepdb1"; + return usingSid ? + "jdbc:oracle:thin:" + "@" + getHost() + ":" + getOraclePort() + ":" + getSid() : + "jdbc:oracle:thin:" + "@" + getHost() + ":" + getOraclePort() + "/" + getDatabaseName(); + } + + @Override + public String getDatabaseName() { + return databaseName; + } + + @SuppressWarnings("SameReturnValue") + public String getSid() { + return "xe"; + } + + public boolean isUsingSid() { + return usingSid; } @Override public String getUsername() { - return username; + //An application user is tied to the database, and therefore not authenticated to connect to SID. + return isUsingSid() ? DEFAULT_SYSTEM_USER : username; } @Override @@ -82,7 +134,40 @@ public String getPassword() { } @Override - public OracleContainer withUsername(String username) { + public String getTestQueryString() { + return "SELECT 1 FROM DUAL"; + } + + public Integer getOraclePort() { + return getMappedPort(ORACLE_PORT); + } + + @SuppressWarnings("unused") + public Integer getWebPort() { + return getMappedPort(APEX_HTTP_PORT); + } + + @Override + public SELF withDatabaseName(String databaseName) { + if (StringUtils.isEmpty(databaseName)) { + throw new IllegalArgumentException("Database name cannot be null or empty"); + } + + if (ORACLE_DEFAULT_DBS.contains(databaseName.toLowerCase())) { + throw new IllegalArgumentException("Database name cannot be one of " + ORACLE_DEFAULT_DBS); + } + + this.databaseName = databaseName; + return self(); + } + + public SELF usingSid() { + this.usingSid = true; + return self(); + } + + @Override + public SELF withUsername(String username) { if (StringUtils.isEmpty(username)) { throw new IllegalArgumentException("Username cannot be null or empty"); } @@ -94,7 +179,7 @@ public OracleContainer withUsername(String username) { } @Override - public OracleContainer withPassword(String password) { + public SELF withPassword(String password) { if (StringUtils.isEmpty(password)) { throw new IllegalArgumentException("Password cannot be null or empty"); } @@ -103,33 +188,12 @@ public OracleContainer withPassword(String password) { } @Override - public OracleContainer withUrlParam(String paramName, String paramValue) { - throw new UnsupportedOperationException("The OracleDb does not support this"); - } - - @SuppressWarnings("SameReturnValue") - public String getSid() { - return "xe"; - } - - public Integer getOraclePort() { - return getMappedPort(ORACLE_PORT); - } - - @SuppressWarnings("unused") - public Integer getWebPort() { - return getMappedPort(APEX_HTTP_PORT); - } - - @Override - public String getTestQueryString() { - return "SELECT 1 FROM DUAL"; + public SELF withUrlParam(String paramName, String paramValue) { + throw new UnsupportedOperationException("The Oracle Database driver does not support this"); } @Override - protected void configure() { - withEnv("ORACLE_PASSWORD", password); - withEnv("APP_USER", username); - withEnv("APP_USER_PASSWORD", password); + protected void waitUntilContainerStarted() { + getWaitStrategy().waitUntilReady(this); } } diff --git a/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java b/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java index 7d026924442..7f1d5d310ed 100644 --- a/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java +++ b/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java @@ -15,12 +15,31 @@ public class SimpleOracleTest extends AbstractContainerDatabaseTest { public static final DockerImageName ORACLE_DOCKER_IMAGE_NAME = DockerImageName.parse("gvenzl/oracle-xe:18.4.0-slim"); @Test - public void testSimple() throws SQLException { + public void testPluggableDatabase() throws SQLException { try ( OracleContainer oracle = new OracleContainer(ORACLE_DOCKER_IMAGE_NAME) .withUsername("baz") .withPassword("bar") + .withDatabaseName("testDB") + ) { + oracle.start(); + ResultSet resultSet = performQuery(oracle, "SELECT 1 FROM dual"); + + int resultSetInt = resultSet.getInt(1); + + assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + } + } + + @Test + public void testSID() throws SQLException { + + try ( + OracleContainer oracle = new OracleContainer(ORACLE_DOCKER_IMAGE_NAME) + .withUsername("baz") + .withPassword("bar") + .usingSid(); ) { oracle.start(); ResultSet resultSet = performQuery(oracle, "SELECT 1 FROM dual");