From 03ed205bb8b15f14c1426c5b76ff74d7b5dc18ea Mon Sep 17 00:00:00 2001 From: Michael-A-McMahon Date: Wed, 6 Jul 2022 16:47:34 -0700 Subject: [PATCH 1/5] Support ConnectionFactoryOptions.PROTOCOL --- .../r2dbc/impl/OracleReactiveJdbcAdapter.java | 41 ++++++++++- .../impl/OracleConnectionFactoryImplTest.java | 27 ++------ .../impl/OracleReactiveJdbcAdapterTest.java | 34 +++++---- .../r2dbc/impl/OracleStatementImplTest.java | 16 ++--- .../oracle/r2dbc/test/DatabaseConfig.java | 69 ++++++++++++------- .../java/oracle/r2dbc/test/OracleTestKit.java | 22 +++--- 6 files changed, 121 insertions(+), 88 deletions(-) diff --git a/src/main/java/oracle/r2dbc/impl/OracleReactiveJdbcAdapter.java b/src/main/java/oracle/r2dbc/impl/OracleReactiveJdbcAdapter.java index e740d74..164ea6f 100755 --- a/src/main/java/oracle/r2dbc/impl/OracleReactiveJdbcAdapter.java +++ b/src/main/java/oracle/r2dbc/impl/OracleReactiveJdbcAdapter.java @@ -66,6 +66,7 @@ import static io.r2dbc.spi.ConnectionFactoryOptions.HOST; import static io.r2dbc.spi.ConnectionFactoryOptions.PASSWORD; import static io.r2dbc.spi.ConnectionFactoryOptions.PORT; +import static io.r2dbc.spi.ConnectionFactoryOptions.PROTOCOL; import static io.r2dbc.spi.ConnectionFactoryOptions.SSL; import static io.r2dbc.spi.ConnectionFactoryOptions.USER; import static oracle.r2dbc.impl.OracleR2dbcExceptions.fromJdbc; @@ -431,9 +432,40 @@ public DataSource createDataSource(ConnectionFactoryOptions options) { } /** + *

* Composes an Oracle JDBC URL from {@code ConnectionFactoryOptions}, as * specified in the javadoc of * {@link #createDataSource(ConnectionFactoryOptions)} + *

+ * If the {@link ConnectionFactoryOptions#SSL} option is set, then the JDBC + * URL is composed with the tcps protocol, as in: + * {@code jdbc:oracle:thins:@tcps:...}. The {@code SSL} option is interpreted + * as a strict directive to use TLS, and so it takes precedence over any value + * that may otherwise be specified by the {@code PROTOCOL} option. + *

+ * If the {@code SSL} option is not set, then URL is composed with the any + * value set for {@link ConnectionFactoryOptions#PROTOCOL} option. For + * instance, if the {@code PROTOCOL} option is set to "ldap" then the URL + * is composed as: {@code jdbc:oracle:thins:@ldap://...}. + *

+ * For consistency with the Oracle JDBC URL, an Oracle R2DBC URL might include + * multiple space separated LDAP addresses, like this: + *

+   * r2dbc:oracle:ldap://example.com:3500/cn=salesdept,cn=OracleContext,dc=com/salesdb%20ldap://example.com:3500/cn=salesdept,cn=OracleContext,dc=com/salesdb
+   * 
+ * The %20 encoding of the space character must be used in order for + * {@link ConnectionFactoryOptions#parse(CharSequence)} to recognize the URL + * syntax. When multiple address are specified this way, the {@code DATABASE} + * option will have the value of: + *
+   * cn=salesdept,cn=OracleContext,dc=com/salesdb ldap://example.com:3500/cn=salesdept,cn=OracleContext,dc=com/salesdb
+   * 
+ * This is unusual, but it is what Oracle JDBC expects to see in the path + * element of a + * + * multi-address LDAP URL. + * + *

* @param options R2DBC options. Not null. * @return An Oracle JDBC URL composed from R2DBC options * @throws IllegalArgumentException If the {@code oracleNetDescriptor} @@ -448,15 +480,18 @@ private static String composeJdbcUrl(ConnectionFactoryOptions options) { return "jdbc:oracle:thin:@" + descriptor.toString(); } else { + Object protocol = + Boolean.TRUE.equals(parseOptionValue( + SSL, options, Boolean.class, Boolean::valueOf)) + ? "tcps" + : options.getValue(PROTOCOL); Object host = options.getRequiredValue(HOST); Integer port = parseOptionValue( PORT, options, Integer.class, Integer::valueOf); Object serviceName = options.getValue(DATABASE); - Boolean isTcps = parseOptionValue( - SSL, options, Boolean.class, Boolean::valueOf); return String.format("jdbc:oracle:thin:@%s%s%s%s", - Boolean.TRUE.equals(isTcps) ? "tcps:" : "", + protocol == null ? "" : protocol + "://", host, port != null ? (":" + port) : "", serviceName != null ? ("/" + serviceName) : ""); diff --git a/src/test/java/oracle/r2dbc/impl/OracleConnectionFactoryImplTest.java b/src/test/java/oracle/r2dbc/impl/OracleConnectionFactoryImplTest.java index e7cd707..0dd9c0f 100644 --- a/src/test/java/oracle/r2dbc/impl/OracleConnectionFactoryImplTest.java +++ b/src/test/java/oracle/r2dbc/impl/OracleConnectionFactoryImplTest.java @@ -36,6 +36,7 @@ import java.util.HashSet; import java.util.Set; +import static oracle.r2dbc.test.DatabaseConfig.connectionFactoryOptions; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -105,16 +106,7 @@ public void testDiscovery() { @Test public void testCreate() { Publisher connectionPublisher = - new OracleConnectionFactoryImpl( - ConnectionFactoryOptions.builder() - .option(ConnectionFactoryOptions.DRIVER, "oracle") - .option(ConnectionFactoryOptions.HOST, DatabaseConfig.host()) - .option(ConnectionFactoryOptions.PORT, DatabaseConfig.port()) - .option(ConnectionFactoryOptions.DATABASE, DatabaseConfig.serviceName()) - .option(ConnectionFactoryOptions.USER, DatabaseConfig.user()) - .option(ConnectionFactoryOptions.PASSWORD, DatabaseConfig.password()) - .build()) - .create(); + new OracleConnectionFactoryImpl(connectionFactoryOptions()).create(); // Expect publisher to emit one connection to each subscriber Set connections = new HashSet<>(); @@ -145,16 +137,11 @@ public void testCreate() { public void testCreateFailure() { // Connect with the wrong username Publisher connectionPublisher = - new OracleConnectionFactoryImpl( - ConnectionFactoryOptions.builder() - .option(ConnectionFactoryOptions.DRIVER, "oracle") - .option(ConnectionFactoryOptions.HOST, DatabaseConfig.host()) - .option(ConnectionFactoryOptions.PORT, DatabaseConfig.port()) - .option(ConnectionFactoryOptions.DATABASE, DatabaseConfig.serviceName()) - .option(ConnectionFactoryOptions.USER, - "Wrong" + DatabaseConfig.user()) - .option(ConnectionFactoryOptions.PASSWORD, DatabaseConfig.password()) - .build()) + new OracleConnectionFactoryImpl(connectionFactoryOptions() + .mutate() + .option(ConnectionFactoryOptions.USER, + "Wrong" + DatabaseConfig.user()) + .build()) .create(); // Expect publisher to signal onError with an R2DBCException diff --git a/src/test/java/oracle/r2dbc/impl/OracleReactiveJdbcAdapterTest.java b/src/test/java/oracle/r2dbc/impl/OracleReactiveJdbcAdapterTest.java index bdd2b55..0255d54 100644 --- a/src/test/java/oracle/r2dbc/impl/OracleReactiveJdbcAdapterTest.java +++ b/src/test/java/oracle/r2dbc/impl/OracleReactiveJdbcAdapterTest.java @@ -43,6 +43,8 @@ import java.nio.file.StandardOpenOption; import java.sql.SQLException; import java.time.Duration; +import java.util.Objects; +import java.util.Optional; import java.util.Properties; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -66,9 +68,11 @@ import static io.r2dbc.spi.ConnectionFactoryOptions.USER; import static java.lang.String.format; import static oracle.r2dbc.test.DatabaseConfig.connectTimeout; +import static oracle.r2dbc.test.DatabaseConfig.connectionFactoryOptions; import static oracle.r2dbc.test.DatabaseConfig.host; import static oracle.r2dbc.test.DatabaseConfig.password; import static oracle.r2dbc.test.DatabaseConfig.port; +import static oracle.r2dbc.test.DatabaseConfig.protocol; import static oracle.r2dbc.test.DatabaseConfig.serviceName; import static oracle.r2dbc.test.DatabaseConfig.sharedConnection; import static oracle.r2dbc.test.DatabaseConfig.sqlTimeout; @@ -213,9 +217,11 @@ public void testTnsAdmin() throws IOException { // Create an Oracle Net Descriptor String descriptor = format( - "(DESCRIPTION=(ADDRESS=(HOST=%s)(PORT=%d)(PROTOCOL=tcp))" + + "(DESCRIPTION=(ADDRESS=(HOST=%s)(PORT=%d)(PROTOCOL=%s))" + "(CONNECT_DATA=(SERVICE_NAME=%s)))", - host(), port(), serviceName()); + host(), port(), + Objects.requireNonNullElse(protocol(), "tcp"), + serviceName()); // Create a tnsnames.ora file with an alias for the descriptor Files.writeString(Path.of("tnsnames.ora"), @@ -365,14 +371,8 @@ public void testConnectTimeout() @Test public void testStatementTimeout() { Connection connection0 = - Mono.from(ConnectionFactories.get(ConnectionFactoryOptions - .builder() - .option(DRIVER, "oracle") - .option(HOST, host()) - .option(PORT, port()) - .option(DATABASE, serviceName()) - .option(USER, user()) - .option(PASSWORD, password()) + Mono.from(ConnectionFactories.get(connectionFactoryOptions() + .mutate() .option(STATEMENT_TIMEOUT, Duration.ofSeconds(2)) // Disable OOB to support testing with an 18.x database .option(Option.valueOf( @@ -442,14 +442,9 @@ public void testExecutorOption() { // Create a connection that is configured to use the custom executor Connection connection = awaitOne(ConnectionFactories.get( - ConnectionFactoryOptions.builder() + connectionFactoryOptions() + .mutate() .option(OracleR2dbcOptions.EXECUTOR, testExecutor) - .option(DRIVER, "oracle") - .option(HOST, host()) - .option(PORT, port()) - .option(DATABASE, serviceName()) - .option(USER, user()) - .option(PASSWORD, password()) .build()) .create()); @@ -490,12 +485,15 @@ public void testVSessionOptions() { // Verify configuration with URL parameters Connection connection = awaitOne(ConnectionFactories.get( ConnectionFactoryOptions.parse( - format("r2dbc:oracle://%s:%d/%s" + + format("r2dbc:oracle:%s//%s:%d/%s" + "?v$session.osuser=%s" + "&v$session.terminal=%s" + "&v$session.process=%s" + "&v$session.program=%s" + "&v$session.machine=%s", + Optional.ofNullable(protocol()) + .map(protocol -> protocol + ":") + .orElse(""), host(), port(), serviceName(), osuser, terminal, process, program, machine)) .mutate() diff --git a/src/test/java/oracle/r2dbc/impl/OracleStatementImplTest.java b/src/test/java/oracle/r2dbc/impl/OracleStatementImplTest.java index 09723b8..e9f5578 100644 --- a/src/test/java/oracle/r2dbc/impl/OracleStatementImplTest.java +++ b/src/test/java/oracle/r2dbc/impl/OracleStatementImplTest.java @@ -61,6 +61,7 @@ import static java.lang.String.format; import static java.util.Arrays.asList; import static oracle.r2dbc.test.DatabaseConfig.connectTimeout; +import static oracle.r2dbc.test.DatabaseConfig.connectionFactoryOptions; import static oracle.r2dbc.test.DatabaseConfig.host; import static oracle.r2dbc.test.DatabaseConfig.newConnection; import static oracle.r2dbc.test.DatabaseConfig.password; @@ -2280,17 +2281,10 @@ public Object getValue() { * @return Connection that uses the {@code executor} */ private static Publisher connect(Executor executor) { - return ConnectionFactories.get( - ConnectionFactoryOptions.parse(format( - "r2dbc:oracle://%s:%d/%s", host(), port(), serviceName())) - .mutate() - .option( - ConnectionFactoryOptions.USER, user()) - .option( - ConnectionFactoryOptions.PASSWORD, password()) - .option( - OracleR2dbcOptions.EXECUTOR, executor) - .build()) + return ConnectionFactories.get(connectionFactoryOptions() + .mutate() + .option(OracleR2dbcOptions.EXECUTOR, executor) + .build()) .create(); } diff --git a/src/test/java/oracle/r2dbc/test/DatabaseConfig.java b/src/test/java/oracle/r2dbc/test/DatabaseConfig.java index e7e4729..133833e 100644 --- a/src/test/java/oracle/r2dbc/test/DatabaseConfig.java +++ b/src/test/java/oracle/r2dbc/test/DatabaseConfig.java @@ -49,6 +49,16 @@ public final class DatabaseConfig { private DatabaseConfig() {} + /** + * Returns the protocol used to connect with a database, specified as + * {@code PROTOCOL} in the "config.properties" file. + * @return Connection protocol for the test database. May be {@code null} if + * no protocol is configured. + */ + public static String protocol() { + return PROTOCOL; + } + /** * Returns the hostname of the server where a test database listens for * connections, specified as {@code HOST} in the "config.properties" file. @@ -203,6 +213,40 @@ public static void showErrors(Connection connection) { .forEach(System.err::println); } + /** + * Returns the options parsed from the "config.properties" resource. + */ + public static ConnectionFactoryOptions connectionFactoryOptions() { + + ConnectionFactoryOptions.Builder optionsBuilder = + ConnectionFactoryOptions.builder() + .option(ConnectionFactoryOptions.DRIVER, "oracle") + .option(ConnectionFactoryOptions.HOST, HOST) + .option(ConnectionFactoryOptions.PORT, PORT) + .option(ConnectionFactoryOptions.DATABASE, SERVICE_NAME) + .option(ConnectionFactoryOptions.USER, USER) + .option(ConnectionFactoryOptions.PASSWORD, PASSWORD) + // Disable statement caching in order to verify cursor closing; + // Cached statements don't close their cursors + .option(Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_IMPLICIT_STATEMENT_CACHE_SIZE), + 0) + // Disable out-of-band breaks to support testing with the 18.x + // database. The 19.x database will automatically detect when it's + // running on a system where OOB is not supported, but the 18.x + // database does not do this and so statement timeout tests will + // hang if the database system does not support OOB + .option(Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_NET_DISABLE_OUT_OF_BAND_BREAK), + "true"); + + if (PROTOCOL != null) + optionsBuilder.option(ConnectionFactoryOptions.PROTOCOL, PROTOCOL); + + return optionsBuilder.build(); + } + + private static final String PROTOCOL; private static final String HOST; private static final int PORT; private static final String SERVICE_NAME; @@ -237,30 +281,9 @@ public static void showErrors(Connection connection) { Long.parseLong(prop.getProperty("CONNECT_TIMEOUT"))); SQL_TIMEOUT = Duration.ofSeconds( Long.parseLong(prop.getProperty("SQL_TIMEOUT"))); + PROTOCOL = prop.getProperty("PROTOCOL"); - CONNECTION_FACTORY = ConnectionFactories.get( - ConnectionFactoryOptions.builder() - .option(ConnectionFactoryOptions.DRIVER, "oracle") - .option(ConnectionFactoryOptions.HOST, HOST) - .option(ConnectionFactoryOptions.PORT, PORT) - .option(ConnectionFactoryOptions.DATABASE, SERVICE_NAME) - .option(ConnectionFactoryOptions.USER, USER) - .option(ConnectionFactoryOptions.PASSWORD, PASSWORD) - // Disable statement caching in order to verify cursor closing; - // Cached statements don't close their cursors - .option(Option.valueOf( - OracleConnection.CONNECTION_PROPERTY_IMPLICIT_STATEMENT_CACHE_SIZE), - 0) - // Disable out-of-band breaks to support testing with the 18.x - // database. The 19.x database will automatically detect when it's - // running on a system where OOB is not supported, but the 18.x - // database does not do this and so statement timeout tests will - // hang if the database system does not support OOB - .option(Option.valueOf( - OracleConnection.CONNECTION_PROPERTY_THIN_NET_DISABLE_OUT_OF_BAND_BREAK), - "true") - .build()); - + CONNECTION_FACTORY = ConnectionFactories.get(connectionFactoryOptions()); SHARED_CONNECTION_FACTORY = new SharedConnectionFactory( CONNECTION_FACTORY.create(), CONNECTION_FACTORY.getMetadata()); diff --git a/src/test/java/oracle/r2dbc/test/OracleTestKit.java b/src/test/java/oracle/r2dbc/test/OracleTestKit.java index c526fff..c2fc5a2 100755 --- a/src/test/java/oracle/r2dbc/test/OracleTestKit.java +++ b/src/test/java/oracle/r2dbc/test/OracleTestKit.java @@ -45,6 +45,7 @@ import java.math.BigDecimal; import java.sql.SQLException; import java.util.Arrays; +import java.util.Optional; import java.util.function.Function; import java.util.stream.IntStream; @@ -54,9 +55,11 @@ import static io.r2dbc.spi.ConnectionFactoryOptions.PASSWORD; import static io.r2dbc.spi.ConnectionFactoryOptions.PORT; import static io.r2dbc.spi.ConnectionFactoryOptions.USER; +import static oracle.r2dbc.test.DatabaseConfig.connectionFactoryOptions; import static oracle.r2dbc.test.DatabaseConfig.host; import static oracle.r2dbc.test.DatabaseConfig.password; import static oracle.r2dbc.test.DatabaseConfig.port; +import static oracle.r2dbc.test.DatabaseConfig.protocol; import static oracle.r2dbc.test.DatabaseConfig.serviceName; import static oracle.r2dbc.test.DatabaseConfig.user; @@ -91,7 +94,10 @@ public class OracleTestKit implements TestKit { { try { OracleDataSource dataSource = new oracle.jdbc.pool.OracleDataSource(); - dataSource.setURL(String.format("jdbc:oracle:thin:@%s:%d/%s", + dataSource.setURL(String.format("jdbc:oracle:thin:@%s%s:%d/%s", + Optional.ofNullable(protocol()) + .map(protocol -> protocol + ":") + .orElse(""), host(), port(), serviceName())); dataSource.setUser(user()); dataSource.setPassword(password()); @@ -102,18 +108,8 @@ public class OracleTestKit implements TestKit { } } - private final ConnectionFactory connectionFactory; - { - connectionFactory = ConnectionFactories.get( - ConnectionFactoryOptions.builder() - .option(DRIVER, "oracle") - .option(DATABASE, serviceName()) - .option(HOST, host()) - .option(PORT, port()) - .option(PASSWORD, password()) - .option(USER, user()) - .build()); - } + private final ConnectionFactory connectionFactory = + ConnectionFactories.get(connectionFactoryOptions()); public JdbcOperations getJdbcOperations() { return jdbcOperations; From 6fb27068261ca301933a118e7379f9af09e4ddf2 Mon Sep 17 00:00:00 2001 From: Michael-A-McMahon Date: Mon, 11 Jul 2022 13:08:00 -0700 Subject: [PATCH 2/5] Documenting LDAP URLs --- README.md | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index eb1aa45..d8df2ab 100644 --- a/README.md +++ b/README.md @@ -191,22 +191,22 @@ are supported by Oracle R2DBC: - `HOST` - `PORT` - `DATABASE` + - The database option is interpreted as the + [service name](https://docs.oracle.com/en/database/oracle/oracle-database/21/netag/identifying-and-accessing-database.html#GUID-153861C1-16AD-41EC-A179-074146B722E6) + of an Oracle Database instance. _System Identifiers (SID) are not recognized_. - `USER` - `PASSWORD` - `SSL` - `CONNECT_TIMEOUT` - `STATEMENT_TIMEOUT`. - -> Oracle R2DBC interprets the `DATABASE` option as the -> [service name](https://docs.oracle.com/en/database/oracle/oracle-database/21/netag/identifying-and-accessing-database.html#GUID-153861C1-16AD-41EC-A179-074146B722E6) -> of an Oracle Database instance. _System Identifiers (SID) are not recognized_. + - `PROTOCOL` + - (For inclusion in the next release) Accepted protocol values are "tcps", "ldap", and "ldaps" #### Support for Extended R2DBC Options Oracle R2DBC extends the standard set of R2DBC options to offer functionality that is specific to Oracle Database and the Oracle JDBC Driver. Extended options are declared in the -[OracleR2dbcOptions](src/main/java/oracle/r2dbc/OracleR2dbcOptions.java) -class. +[OracleR2dbcOptions](src/main/java/oracle/r2dbc/OracleR2dbcOptions.java) class. #### Configuring an Oracle Net Descriptor The `oracle.r2dbc.OracleR2dbcOptions.DESCRIPTOR` option may be used to configure @@ -234,6 +234,22 @@ located: r2dbc:oracle://?oracle.r2dbc.descriptor=myAlias&TNS_ADMIN=/path/to/tnsnames/ ``` +#### (For inclusion in the next release) Configuring an LDAP URL +Use `ldap` or `ldaps` as the URL protocol to have an Oracle Net Descriptor +retrieved from an LDAP server: +``` +r2dbc:oracle:ldap://ldap.example.com:7777/sales,cn=OracleContext,dc=com +r2dbc:oracle:ldaps://ldap.example.com:7778/sales,cn=OracleContext,dc=com +``` +Use a space separated list of LDAP URIs for fail over and load balancing: +``` +r2dbc:oracle:ldap://ldap1.example.com:7777/sales,cn=OracleContext,dc=com%20ldap://ldap2.example.com:7777/sales,cn=OracleContext,dc=com%20ldap://ldap3.example.com:7777/sales,cn=OracleContext,dc=com +``` +> Space characters in a URL must be percent encoded as `%20` + +An LDAP server request will **block a thread for network I/O** when Oracle R2DBC +creates a new connection. + #### Configuring a java.util.concurrent.Executor The `oracle.r2dbc.OracleR2dbcOptions.EXECUTOR` option configures a `java.util.concurrent.Executor` for executing asynchronous callbacks. The From 8cf89fc825514b48828ffd860eb880ed85590ba8 Mon Sep 17 00:00:00 2001 From: Michael-A-McMahon Date: Wed, 13 Jul 2022 12:43:23 -0700 Subject: [PATCH 3/5] Add LDAP tests --- pom.xml | 13 +- src/main/java/module-info.java | 1 + .../impl/OracleReactiveJdbcAdapterTest.java | 106 +++- .../oracle/r2dbc/util/TestContextFactory.java | 468 ++++++++++++++++++ 4 files changed, 581 insertions(+), 7 deletions(-) create mode 100644 src/test/java/oracle/r2dbc/util/TestContextFactory.java diff --git a/pom.xml b/pom.xml index 9f54c67..77260e6 100755 --- a/pom.xml +++ b/pom.xml @@ -87,6 +87,17 @@ -Xlint:-processing -Xlint:-serial + + true ${java.version} @@ -150,6 +161,7 @@ maven-surefire-plugin 3.0.0-M5 + **/*Test.java **/*TestKit.java @@ -278,7 +290,6 @@ reactor-test test - diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 796c99e..ce9c4bc 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -30,6 +30,7 @@ with oracle.r2dbc.impl.OracleConnectionFactoryProviderImpl; requires java.sql; + requires java.naming; requires com.oracle.database.jdbc; requires reactor.core; requires transitive org.reactivestreams; diff --git a/src/test/java/oracle/r2dbc/impl/OracleReactiveJdbcAdapterTest.java b/src/test/java/oracle/r2dbc/impl/OracleReactiveJdbcAdapterTest.java index 0255d54..4a01fe6 100644 --- a/src/test/java/oracle/r2dbc/impl/OracleReactiveJdbcAdapterTest.java +++ b/src/test/java/oracle/r2dbc/impl/OracleReactiveJdbcAdapterTest.java @@ -23,6 +23,7 @@ import io.r2dbc.spi.Connection; import io.r2dbc.spi.ConnectionFactories; +import io.r2dbc.spi.ConnectionFactory; import io.r2dbc.spi.ConnectionFactoryOptions; import io.r2dbc.spi.Option; import io.r2dbc.spi.R2dbcTimeoutException; @@ -31,10 +32,12 @@ import oracle.jdbc.datasource.OracleDataSource; import oracle.r2dbc.OracleR2dbcOptions; import oracle.r2dbc.test.DatabaseConfig; +import oracle.r2dbc.util.TestContextFactory; import org.junit.jupiter.api.Test; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import javax.naming.spi.NamingManager; import java.io.IOException; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; @@ -216,12 +219,7 @@ public void testCreateDataSource() throws SQLException { public void testTnsAdmin() throws IOException { // Create an Oracle Net Descriptor - String descriptor = format( - "(DESCRIPTION=(ADDRESS=(HOST=%s)(PORT=%d)(PROTOCOL=%s))" + - "(CONNECT_DATA=(SERVICE_NAME=%s)))", - host(), port(), - Objects.requireNonNullElse(protocol(), "tcp"), - serviceName()); + String descriptor = createDescriptor(); // Create a tnsnames.ora file with an alias for the descriptor Files.writeString(Path.of("tnsnames.ora"), @@ -525,6 +523,102 @@ public void testVSessionOptions() { tryAwaitNone(connection.close()); } } + /** + * Verifies the use of the LDAP protocol in an r2dbc:oracle URL. + */ + @Test + public void testLdapUrl() throws Exception { + + // Configure Oracle R2DBC with an R2DBC URL having the LDAP protocol and the + // given path. + String ldapPath = "sales,cn=OracleContext,dc=com"; + ConnectionFactory ldapConnectionFactory = ConnectionFactories.get( + ConnectionFactoryOptions.parse(format( + "r2dbc:oracle:ldap://ldap.example.com:9999/%s", ldapPath)) + .mutate() + .option(ConnectionFactoryOptions.USER, DatabaseConfig.user()) + .option(ConnectionFactoryOptions.PASSWORD, DatabaseConfig.password()) + .build()); + + // Set up the mock LDAP context factory. See JavaDoc of TestContextFactory + // for details about this. + NamingManager.setInitialContextFactoryBuilder(environment -> + new TestContextFactory()); + TestContextFactory.bind(ldapPath, createDescriptor()); + + // Now verify that the LDAP URL is resolved to the descriptor + Connection ldapConnection = awaitOne(ldapConnectionFactory.create()); + try { + assertEquals( + "Hello, LDAP", + awaitOne( + awaitOne(ldapConnection.createStatement( + "SELECT 'Hello, LDAP' FROM sys.dual") + .execute()) + .map(row -> row.get(0)))); + } + finally { + tryAwaitNone(ldapConnection.close()); + } + } + + /** + * Verifies the use of the LDAP protocol in an r2dbc:oracle URL having + * multiple LDAP endpoints + */ + @Test + public void testMultiLdapUrl() throws Exception { + + // Configure Oracle R2DBC with an R2DBC URL having the LDAP protocol and + // multiple LDAP endpoints. Only the last endpoint will contain the given + // path, and so the previous endpoints are invalid. + String ldapPath = "cn=salesdept,cn=OracleContext,dc=com/salesdb"; + ConnectionFactory ldapConnectionFactory = ConnectionFactories.get( + ConnectionFactoryOptions.parse(format( + "r2dbc:oracle:" + + "ldap://ldap1.example.com:7777/cn=salesdept0,cn=OracleContext,dc=com/salesdb" + + "%%20ldap://ldap1.example.com:7777/cn=salesdept1,cn=OracleContext,dc=com/salesdb" + + "%%20ldap://ldap3.example.com:7777/%s", ldapPath)) + .mutate() + .option(ConnectionFactoryOptions.USER, DatabaseConfig.user()) + .option(ConnectionFactoryOptions.PASSWORD, DatabaseConfig.password()) + .build()); + + // Set up the mock LDAP context factory. A descriptor is bound to the last + // endpoint only. See JavaDoc of TestContextFactory for details about this. + NamingManager.setInitialContextFactoryBuilder(environment -> + new TestContextFactory()); + TestContextFactory.bind("salesdb", createDescriptor()); + + // Now verify that the LDAP URL is resolved to the descriptor + Connection ldapConnection = awaitOne(ldapConnectionFactory.create()); + try { + assertEquals( + "Hello, LDAP", + awaitOne( + awaitOne(ldapConnection.createStatement( + "SELECT 'Hello, LDAP' FROM sys.dual") + .execute()) + .map(row -> row.get(0)))); + } + finally { + tryAwaitNone(ldapConnection.close()); + } + } + + /** + * Returns an Oracle Net Descriptor having the values configured by + * {@link DatabaseConfig} + * @return An Oracle Net Descriptor for the test database. + */ + private static String createDescriptor() { + return format( + "(DESCRIPTION=(ADDRESS=(HOST=%s)(PORT=%d)(PROTOCOL=%s))" + + "(CONNECT_DATA=(SERVICE_NAME=%s)))", + host(), port(), + Objects.requireNonNullElse(protocol(), "tcp"), + serviceName()); + } /** * Verifies that an attempt to connect with a {@code listeningChannel} diff --git a/src/test/java/oracle/r2dbc/util/TestContextFactory.java b/src/test/java/oracle/r2dbc/util/TestContextFactory.java new file mode 100644 index 0000000..e0c2f1e --- /dev/null +++ b/src/test/java/oracle/r2dbc/util/TestContextFactory.java @@ -0,0 +1,468 @@ +/* + Copyright (c) 2020, 2022, Oracle and/or its affiliates. + + This software is dual-licensed to you under the Universal Permissive License + (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License + 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose + either license. + + 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 oracle.r2dbc.util; + +import javax.naming.Binding; +import javax.naming.Context; +import javax.naming.Name; +import javax.naming.NameClassPair; +import javax.naming.NameParser; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attributes; +import javax.naming.directory.BasicAttributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.ModificationItem; +import javax.naming.directory.SearchControls; +import javax.naming.directory.SearchResult; +import java.util.HashMap; +import java.util.Hashtable; + +/** + *

+ * A mock implementation of the JNDI + * {@link javax.naming.spi.InitialContextFactory} SPI. This class is used for + * testing LDAP URLs with Oracle R2DBC. + * + *

Registering this Factory

+ * When an LDAP URL is configured, the underlying Oracle JDBC Driver will do + * something like this: + *

{@code
+ *   var properties = new Properties()
+ *   properties.setProperty(
+ *     Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+ *   DirContext dirContext = new InitialDirContext(properties);
+ * }
+ * Note how JDBC has hardcoded the Sun LDAP factory. Test code needs to override + * this, such that the DirContext object seen above will delegate to an instance + * created by this test factory. This can be accomplished by registering a + * factory builder that outputs an instance of this class: + *
{@code
+ *   NamingManager.setInitialContextFactoryBuilder(environment ->
+ *     new TestContextFactory());
+ * }
+ * When a factory builder is registered, the InitialDirContext constructor + * ignores JDBC's configuration. Instead of using the Sun factory, the + * constructor will use this test factory. + * + *

Binding Oracle Net Descriptors

+ * After Oracle JDBC gets the DirContext object (see previous section), it will + * query it for an Oracle Net Descriptor. Oracle JDBC then uses this descriptor + * to connect to Oracle Database (just as if the descriptor had been given as + * a URL or as a tnsnames.ora entry). The name that JDBC queries for is + * extracted from the path component of the LDAP URL. For example, if JDBC is + * given an LDAP URL of: + *

+ *   ldap://ldap1.example.com:3500/cn=salesdept,cn=OracleContext,dc=com/salesdb
+ * 
+ * Then Oracle JDBC queries the DirContext like this: + *
{@code
+ * Attributes attributes = dirContext.getAttributes(
+ *   "cn=salesdept,cn=OracleContext,dc=com/salesdb",
+ *   new String[]{"orclnetdescstring"});
+ * }
+ * In the returned Attributes object, Oracle JDBC reads the descriptor + * from the first value of the first attribute. An invocation of + * {@link TestContextFactory#bind(String, String)} will bind a single attribute + * with a single value to a given name. For example: + *
{@code
+ * TestContextFactory.bind(
+ *   "cn=salesdept,cn=OracleContext,dc=com/salesdb",
+ *   "(DESCRIPTION=...)");
+ * }
+ * The code above would have JDBC connect to the given descriptor. + *

+ */ +public final class TestContextFactory + implements javax.naming.spi.InitialContextFactory { + + /** + * The {@code DirContext} object constructed by Oracle JDBC will delegate to + * this instance of {@code TestDirContext}. + */ + private static final TestDirContext DIR_CONTEXT = new TestDirContext(); + + /** + * {@inheritDoc} + * Ignores the {@code environment} configured by Oracle JDBC, and just returns + * the fixed instance of {@code TestDirContext} + */ + @Override + public Context getInitialContext(Hashtable environment) + throws NamingException { + TestDirContext.environment = (Hashtable) environment.clone(); + return DIR_CONTEXT; + } + + /** + * {@inheritDoc} + * Binds the given name to the given value. This can be called to map an + * Oracle Net Descriptor to the path of an LDAP URL. + */ + public static void bind(String name, String value) { + TestDirContext.ATTRIBUTES.put(name, new BasicAttributes(name, value)); + } + + /** + * Implements the methods of the {@code DirContext} SPI which are called by + * Oracle JDBC. All other method will throw an exception. This implementation + * is simply backed by a mapping between names and attributes. + */ + private static final class TestDirContext implements DirContext { + + /** Maps names to attributes */ + private static final HashMap ATTRIBUTES = + new HashMap<>(); + + /** + * The environment of this context. It is passed to + * {@link TestDirContext#getInitialContext(Hashtable)} when returning this + * context. + */ + private static Hashtable environment = new Hashtable<>(); + + /** + * {@inheritDoc} + * Returns the attribute that has been mapped to a given name. Oracle JDBC + * calls this method to get an Oracle Net Descriptor. + */ + @Override + public Attributes getAttributes(String name, String[] attrIds) + throws NamingException { + Attributes attributes = ATTRIBUTES.get(name); + + // It is noted that JDBC will prefix "cn=" on the path component of an + // LDAP URL. Check if this is why the look up fails. + if (attributes == null && name.startsWith("cn=")) + attributes = ATTRIBUTES.get(name.substring("cn=".length())); + + if (attributes == null) + throw new NamingException("No attribute found for name: " + name); + + return attributes; + } + + /** + * {@inheritDoc} + * JDBC calls this method when resolving a multi-endpoint LDAP URL. + */ + @Override + public Hashtable getEnvironment() throws NamingException { + + String providerUrl = (String)environment.get(Context.PROVIDER_URL); + if (providerUrl != null) { + + // Replicating com.sun.jndi.ldap.LdapCtxFactory. When the provider URL is + // a space-separated list, it returns the first working URL. JDBC then + // calls getAttributes with the last path element that was mapped to this + // URL. So if the working URL was: ldap://host:port/cn=.../db, then JDBC + // calls getAttributes with "cn=db" + String[] urls = providerUrl.split(" "); + + if (urls.length == 1) + return environment; + + // Return the first URL. Don't include the ldap: scheme. + var url = urls[0].substring(urls[0].indexOf('/')); + @SuppressWarnings("unchecked") + Hashtable result = + (Hashtable)environment.clone(); + result.put(Context.PROVIDER_URL, url); + return result; + } + + return environment; + } + + /** + * {@inheritDoc} + * Oracle JDBC calls this method after retrieving an Oracle Net Descriptor. + * There are no resources to clean up. + */ + @Override + public void close() throws NamingException { + // Do nothing + } + + // Every method beyond this point is not called by Oracle JDBC. These + // methods will all throw NamingException. + + @Override + public Attributes getAttributes(Name name, String[] attrIds) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public Attributes getAttributes(Name name) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public Attributes getAttributes(String name) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public void modifyAttributes(Name name, int mod_op, Attributes attrs) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public void modifyAttributes(String name, int mod_op, Attributes attrs) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public void modifyAttributes(Name name, ModificationItem[] mods) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public void modifyAttributes(String name, ModificationItem[] mods) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public void bind(Name name, Object obj, Attributes attrs) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public void bind(String name, Object obj, Attributes attrs) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public void rebind(Name name, Object obj, Attributes attrs) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public void rebind(String name, Object obj, Attributes attrs) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public DirContext createSubcontext(Name name, Attributes attrs) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public DirContext createSubcontext(String name, Attributes attrs) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public DirContext getSchema(Name name) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public DirContext getSchema(String name) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public DirContext getSchemaClassDefinition(Name name) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public DirContext getSchemaClassDefinition(String name) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public NamingEnumeration search(Name name, Attributes matchingAttributes, String[] attributesToReturn) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public NamingEnumeration search(String name, Attributes matchingAttributes, String[] attributesToReturn) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public NamingEnumeration search(Name name, Attributes matchingAttributes) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public NamingEnumeration search(String name, Attributes matchingAttributes) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public NamingEnumeration search(Name name, String filter, SearchControls cons) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public NamingEnumeration search(String name, String filter, SearchControls cons) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public NamingEnumeration search(Name name, String filterExpr, Object[] filterArgs, SearchControls cons) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public NamingEnumeration search(String name, String filterExpr, Object[] filterArgs, SearchControls cons) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public Object lookup(Name name) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public Object lookup(String name) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public void bind(Name name, Object obj) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public void bind(String name, Object obj) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public void rebind(Name name, Object obj) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public void rebind(String name, Object obj) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public void unbind(Name name) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public void unbind(String name) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public void rename(Name oldName, Name newName) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public void rename(String oldName, String newName) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public NamingEnumeration list(Name name) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public NamingEnumeration list(String name) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public NamingEnumeration listBindings(Name name) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public NamingEnumeration listBindings(String name) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public void destroySubcontext(Name name) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public void destroySubcontext(String name) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public Context createSubcontext(Name name) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public Context createSubcontext(String name) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public Object lookupLink(Name name) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public Object lookupLink(String name) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public NameParser getNameParser(Name name) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public NameParser getNameParser(String name) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public Name composeName(Name name, Name prefix) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public String composeName(String name, String prefix) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public Object addToEnvironment(String propName, Object propVal) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public Object removeFromEnvironment(String propName) throws NamingException { + throw new NamingException("Not implemented"); + } + + @Override + public String getNameInNamespace() throws NamingException { + throw new NamingException("Not implemented"); + } + } +} From e526745fc1119e76eba176074796eb7b72046c20 Mon Sep 17 00:00:00 2001 From: Michael-A-McMahon Date: Wed, 13 Jul 2022 13:30:24 -0700 Subject: [PATCH 4/5] Add LDAP properties --- .../java/oracle/r2dbc/OracleR2dbcOptions.java | 307 ++++++++++++++---- .../r2dbc/impl/OracleReactiveJdbcAdapter.java | 94 +----- .../impl/OracleReactiveJdbcAdapterTest.java | 4 - .../oracle/r2dbc/util/TestContextFactory.java | 12 + 4 files changed, 266 insertions(+), 151 deletions(-) diff --git a/src/main/java/oracle/r2dbc/OracleR2dbcOptions.java b/src/main/java/oracle/r2dbc/OracleR2dbcOptions.java index 9acad2a..67cabb2 100644 --- a/src/main/java/oracle/r2dbc/OracleR2dbcOptions.java +++ b/src/main/java/oracle/r2dbc/OracleR2dbcOptions.java @@ -23,6 +23,7 @@ import io.r2dbc.spi.Option; import oracle.jdbc.OracleConnection; +import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.ForkJoinPool; @@ -39,8 +40,7 @@ private OracleR2dbcOptions() {} * then the value of this {@code Option} may be set to a tnsnames.ora * alias. */ - public static final Option DESCRIPTOR = - Option.valueOf("oracle.r2dbc.descriptor"); + public static final Option DESCRIPTOR; /** * Extended {@code Option} that specifies an {@link Executor} for executing @@ -60,203 +60,390 @@ private OracleR2dbcOptions() {} * {@code ForkJoinPool}'s * {@linkplain ForkJoinPool#commonPool() common pool} by default. */ - public static final Option EXECUTOR = - Option.valueOf("oracle.r2dbc.executor"); + public static final Option EXECUTOR; /** * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: * {@link OracleConnection#CONNECTION_PROPERTY_TNS_ADMIN} */ - public static final Option TNS_ADMIN = - Option.valueOf(OracleConnection.CONNECTION_PROPERTY_TNS_ADMIN); + public static final Option TNS_ADMIN; /** * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: * {@link OracleConnection#CONNECTION_PROPERTY_WALLET_LOCATION} */ - public static final Option TLS_WALLET_LOCATION = - Option.valueOf(OracleConnection.CONNECTION_PROPERTY_WALLET_LOCATION); + public static final Option TLS_WALLET_LOCATION; /** * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: * {@link OracleConnection#CONNECTION_PROPERTY_WALLET_PASSWORD} */ - public static final Option TLS_WALLET_PASSWORD = - Option.sensitiveValueOf(OracleConnection.CONNECTION_PROPERTY_WALLET_PASSWORD); + public static final Option TLS_WALLET_PASSWORD; /** * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: * {@link OracleConnection#CONNECTION_PROPERTY_THIN_JAVAX_NET_SSL_KEYSTORE} */ - public static final Option TLS_KEYSTORE = - Option.valueOf(OracleConnection.CONNECTION_PROPERTY_THIN_JAVAX_NET_SSL_KEYSTORE); + public static final Option TLS_KEYSTORE; /** * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: * {@link OracleConnection#CONNECTION_PROPERTY_THIN_JAVAX_NET_SSL_KEYSTORETYPE} */ - public static final Option TLS_KEYSTORE_TYPE = - Option.valueOf(OracleConnection.CONNECTION_PROPERTY_THIN_JAVAX_NET_SSL_KEYSTORETYPE); + public static final Option TLS_KEYSTORE_TYPE; /** * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: * {@link OracleConnection#CONNECTION_PROPERTY_THIN_JAVAX_NET_SSL_KEYSTOREPASSWORD} */ - public static final Option TLS_KEYSTORE_PASSWORD = - Option.sensitiveValueOf(OracleConnection.CONNECTION_PROPERTY_THIN_JAVAX_NET_SSL_KEYSTOREPASSWORD); + public static final Option TLS_KEYSTORE_PASSWORD; /** * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: * {@link OracleConnection#CONNECTION_PROPERTY_THIN_JAVAX_NET_SSL_TRUSTSTORE} */ - public static final Option TLS_TRUSTSTORE = - Option.valueOf(OracleConnection.CONNECTION_PROPERTY_THIN_JAVAX_NET_SSL_TRUSTSTORE); + public static final Option TLS_TRUSTSTORE; /** * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: * {@link OracleConnection#CONNECTION_PROPERTY_THIN_JAVAX_NET_SSL_TRUSTSTORETYPE} */ - public static final Option TLS_TRUSTSTORE_TYPE = - Option.valueOf(OracleConnection.CONNECTION_PROPERTY_THIN_JAVAX_NET_SSL_TRUSTSTORETYPE); + public static final Option TLS_TRUSTSTORE_TYPE; /** * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: * {@link OracleConnection#CONNECTION_PROPERTY_THIN_JAVAX_NET_SSL_TRUSTSTOREPASSWORD} */ - public static final Option TLS_TRUSTSTORE_PASSWORD = - Option.sensitiveValueOf(OracleConnection.CONNECTION_PROPERTY_THIN_JAVAX_NET_SSL_TRUSTSTOREPASSWORD); + public static final Option TLS_TRUSTSTORE_PASSWORD; /** * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: * {@link OracleConnection#CONNECTION_PROPERTY_THIN_NET_AUTHENTICATION_SERVICES} */ - public static final Option AUTHENTICATION_SERVICES = - Option.valueOf(OracleConnection.CONNECTION_PROPERTY_THIN_NET_AUTHENTICATION_SERVICES); + public static final Option AUTHENTICATION_SERVICES; /** * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: * {@link OracleConnection#CONNECTION_PROPERTY_THIN_SSL_CERTIFICATE_ALIAS} */ - public static final Option TLS_CERTIFICATE_ALIAS = - Option.valueOf(OracleConnection.CONNECTION_PROPERTY_THIN_SSL_CERTIFICATE_ALIAS); + public static final Option TLS_CERTIFICATE_ALIAS; /** * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: * {@link OracleConnection#CONNECTION_PROPERTY_THIN_SSL_SERVER_DN_MATCH} */ - public static final Option TLS_SERVER_DN_MATCH = - Option.valueOf(OracleConnection.CONNECTION_PROPERTY_THIN_SSL_SERVER_DN_MATCH); + public static final Option TLS_SERVER_DN_MATCH; /** * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: * {@link OracleConnection#CONNECTION_PROPERTY_THIN_SSL_SERVER_CERT_DN} */ - public static final Option TLS_SERVER_CERT_DN = - Option.valueOf(OracleConnection.CONNECTION_PROPERTY_THIN_SSL_SERVER_CERT_DN); + public static final Option TLS_SERVER_CERT_DN; /** * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: * {@link OracleConnection#CONNECTION_PROPERTY_THIN_SSL_VERSION} */ - public static final Option TLS_VERSION = - Option.valueOf(OracleConnection.CONNECTION_PROPERTY_THIN_SSL_VERSION); + public static final Option TLS_VERSION; /** * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: * {@link OracleConnection#CONNECTION_PROPERTY_THIN_SSL_CIPHER_SUITES} */ - public static final Option TLS_CIPHER_SUITES = - Option.valueOf(OracleConnection.CONNECTION_PROPERTY_THIN_SSL_CIPHER_SUITES); + public static final Option TLS_CIPHER_SUITES; /** * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: * {@link OracleConnection#CONNECTION_PROPERTY_THIN_SSL_KEYMANAGERFACTORY_ALGORITHM} */ - public static final Option TLS_KEYMANAGERFACTORY_ALGORITHM = - Option.valueOf(OracleConnection.CONNECTION_PROPERTY_THIN_SSL_KEYMANAGERFACTORY_ALGORITHM); + public static final Option TLS_KEYMANAGERFACTORY_ALGORITHM; /** * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: * {@link OracleConnection#CONNECTION_PROPERTY_THIN_SSL_TRUSTMANAGERFACTORY_ALGORITHM} */ - public static final Option TLS_TRUSTMANAGERFACTORY_ALGORITHM = - Option.valueOf(OracleConnection.CONNECTION_PROPERTY_THIN_SSL_TRUSTMANAGERFACTORY_ALGORITHM); + public static final Option TLS_TRUSTMANAGERFACTORY_ALGORITHM; /** * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: * {@link OracleConnection#CONNECTION_PROPERTY_SSL_CONTEXT_PROTOCOL} */ - public static final Option SSL_CONTEXT_PROTOCOL = - Option.valueOf(OracleConnection.CONNECTION_PROPERTY_SSL_CONTEXT_PROTOCOL); + public static final Option SSL_CONTEXT_PROTOCOL; /** * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: * {@link OracleConnection#CONNECTION_PROPERTY_FAN_ENABLED} */ - public static final Option FAN_ENABLED = - Option.valueOf(OracleConnection.CONNECTION_PROPERTY_FAN_ENABLED); + public static final Option FAN_ENABLED; /** * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: * {@link OracleConnection#CONNECTION_PROPERTY_IMPLICIT_STATEMENT_CACHE_SIZE} */ - public static final Option IMPLICIT_STATEMENT_CACHE_SIZE = - Option.valueOf(OracleConnection.CONNECTION_PROPERTY_IMPLICIT_STATEMENT_CACHE_SIZE); + public static final Option IMPLICIT_STATEMENT_CACHE_SIZE; /** * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: * {@link OracleConnection#CONNECTION_PROPERTY_DEFAULT_LOB_PREFETCH_SIZE} */ - public static final Option DEFAULT_LOB_PREFETCH_SIZE = - Option.valueOf(OracleConnection.CONNECTION_PROPERTY_DEFAULT_LOB_PREFETCH_SIZE); + public static final Option DEFAULT_LOB_PREFETCH_SIZE; /** * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: * {@link OracleConnection#CONNECTION_PROPERTY_THIN_NET_DISABLE_OUT_OF_BAND_BREAK} */ - public static final Option DISABLE_OUT_OF_BAND_BREAK = - Option.valueOf(OracleConnection.CONNECTION_PROPERTY_THIN_NET_DISABLE_OUT_OF_BAND_BREAK); + public static final Option DISABLE_OUT_OF_BAND_BREAK; /** * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: * {@link OracleConnection#CONNECTION_PROPERTY_ENABLE_QUERY_RESULT_CACHE} */ - public static final Option ENABLE_QUERY_RESULT_CACHE = - Option.valueOf(OracleConnection.CONNECTION_PROPERTY_ENABLE_QUERY_RESULT_CACHE); + public static final Option ENABLE_QUERY_RESULT_CACHE; /** * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: * {@link OracleConnection#CONNECTION_PROPERTY_THIN_VSESSION_TERMINAL} */ - public static final Option VSESSION_TERMINAL = - Option.valueOf(OracleConnection.CONNECTION_PROPERTY_THIN_VSESSION_TERMINAL); + public static final Option VSESSION_TERMINAL; /** * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: * {@link OracleConnection#CONNECTION_PROPERTY_THIN_VSESSION_MACHINE} */ - public static final Option VSESSION_MACHINE = - Option.valueOf(OracleConnection.CONNECTION_PROPERTY_THIN_VSESSION_MACHINE); + public static final Option VSESSION_MACHINE; /** * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: * {@link OracleConnection#CONNECTION_PROPERTY_THIN_VSESSION_OSUSER} */ - public static final Option VSESSION_OSUSER = - Option.valueOf(OracleConnection.CONNECTION_PROPERTY_THIN_VSESSION_OSUSER); + public static final Option VSESSION_OSUSER; /** * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: * {@link OracleConnection#CONNECTION_PROPERTY_THIN_VSESSION_PROGRAM} */ - public static final Option VSESSION_PROGRAM = - Option.valueOf(OracleConnection.CONNECTION_PROPERTY_THIN_VSESSION_PROGRAM); + public static final Option VSESSION_PROGRAM; /** * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: * {@link OracleConnection#CONNECTION_PROPERTY_THIN_VSESSION_PROCESS} */ - public static final Option VSESSION_PROCESS = - Option.valueOf(OracleConnection.CONNECTION_PROPERTY_THIN_VSESSION_PROCESS); + public static final Option VSESSION_PROCESS; + /** + * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: + * {@link OracleConnection#CONNECTION_PROPERTY_THIN_LDAP_SECURITY_AUTHENTICATION} + */ + public static final Option LDAP_SECURITY_AUTHENTICATION; + + /** + * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: + * {@link OracleConnection#CONNECTION_PROPERTY_THIN_LDAP_SECURITY_PRINCIPAL} + */ + public static final Option LDAP_SECURITY_PRINCIPAL; + + /** + * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: + * {@link OracleConnection#CONNECTION_PROPERTY_THIN_LDAP_SECURITY_CREDENTIALS} + */ + public static final Option LDAP_SECURITY_CREDENTIALS; + + /** + * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: + * {@link OracleConnection#CONNECTION_PROPERTY_THIN_JNDI_LDAP_CONNECT_TIMEOUT} + */ + public static final Option LDAP_CONNECT_TIMEOUT; + + /** + * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: + * {@link OracleConnection#CONNECTION_PROPERTY_THIN_JNDI_LDAP_READ_TIMEOUT} + */ + public static final Option LDAP_READ_TIMEOUT; + /** + * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: + * {@link OracleConnection#CONNECTION_PROPERTY_THIN_LDAP_SSL_WALLET_LOCATION} + */ + public static final Option LDAP_WALLET_LOCATION; + + /** + * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: + * {@link OracleConnection#CONNECTION_PROPERTY_THIN_LDAP_SSL_WALLET_PASSWORD} + */ + public static final Option LDAP_WALLET_PASSWORD; + + /** + * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: + * {@link OracleConnection#CONNECTION_PROPERTY_THIN_LDAP_SSL_KEYSTORE_TYPE} + */ + public static final Option LDAP_KEYSTORE_TYPE; + + /** + * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: + * {@link OracleConnection#CONNECTION_PROPERTY_THIN_LDAP_SSL_KEYSTORE} + */ + public static final Option LDAP_KEYSTORE; + + /** + * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: + * {@link OracleConnection#CONNECTION_PROPERTY_THIN_LDAP_SSL_KEYSTORE_PASSWORD} + */ + public static final Option LDAP_KEYSTORE_PASSWORD; + + /** + * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: + * {@link OracleConnection#CONNECTION_PROPERTY_THIN_LDAP_SSL_TRUSTSTORE_TYPE} + */ + public static final Option LDAP_TRUSTSTORE_TYPE; + + /** + * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: + * {@link OracleConnection#CONNECTION_PROPERTY_THIN_LDAP_SSL_TRUSTSTORE} + */ + public static final Option LDAP_TRUSTSTORE; + + /** + * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: + * {@link OracleConnection#CONNECTION_PROPERTY_THIN_LDAP_SSL_TRUSTSTORE_PASSWORD} + */ + public static final Option LDAP_TRUSTSTORE_PASSWORD; + + /** + * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: + * {@link OracleConnection#CONNECTION_PROPERTY_THIN_LDAP_SSL_CIPHER_SUITES} + */ + public static final Option LDAP_CIPHER_SUITES; + + /** + * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: + * {@link OracleConnection#CONNECTION_PROPERTY_THIN_LDAP_SSL_VERSIONS} + */ + public static final Option LDAP_VERSIONS; + + /** + * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: + * {@link OracleConnection#CONNECTION_PROPERTY_THIN_LDAP_SSL_KEYMANAGER_FACTORY_ALGORITHM} + */ + public static final Option LDAP_KEYMANAGER_FACTORY_ALGORITHM; + + /** + * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: + * {@link OracleConnection#CONNECTION_PROPERTY_THIN_LDAP_SSL_TRUSTMANAGER_FACTORY_ALGORITHM} + */ + public static final Option LDAP_TRUSTMANAGER_FACTORY_ALGORITHM; + + /** + * Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by: + * {@link OracleConnection#CONNECTION_PROPERTY_THIN_LDAP_SSL_CONTEXT_PROTOCOL} + */ + public static final Option LDAP_CONTEXT_PROTOCOL; + + + /** The unmodifiable set of all extended options */ + private static final Set> OPTIONS = Set.of( + DESCRIPTOR = Option.valueOf("oracle.r2dbc.descriptor"), + EXECUTOR = Option.valueOf("oracle.r2dbc.executor"), + TNS_ADMIN = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_TNS_ADMIN), + TLS_WALLET_LOCATION = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_WALLET_LOCATION), + TLS_WALLET_PASSWORD = Option.sensitiveValueOf( + OracleConnection.CONNECTION_PROPERTY_WALLET_PASSWORD), + TLS_KEYSTORE = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_JAVAX_NET_SSL_KEYSTORE), + TLS_KEYSTORE_TYPE = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_JAVAX_NET_SSL_KEYSTORETYPE), + TLS_KEYSTORE_PASSWORD = Option.sensitiveValueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_JAVAX_NET_SSL_KEYSTOREPASSWORD), + TLS_TRUSTSTORE = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_JAVAX_NET_SSL_TRUSTSTORE), + TLS_TRUSTSTORE_TYPE = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_JAVAX_NET_SSL_TRUSTSTORETYPE), + TLS_TRUSTSTORE_PASSWORD = Option.sensitiveValueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_JAVAX_NET_SSL_TRUSTSTOREPASSWORD), + AUTHENTICATION_SERVICES = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_NET_AUTHENTICATION_SERVICES), + TLS_CERTIFICATE_ALIAS = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_SSL_CERTIFICATE_ALIAS), + TLS_SERVER_DN_MATCH = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_SSL_SERVER_DN_MATCH), + TLS_SERVER_CERT_DN = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_SSL_SERVER_CERT_DN), + TLS_VERSION = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_SSL_VERSION), + TLS_CIPHER_SUITES = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_SSL_CIPHER_SUITES), + TLS_KEYMANAGERFACTORY_ALGORITHM = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_SSL_KEYMANAGERFACTORY_ALGORITHM), + TLS_TRUSTMANAGERFACTORY_ALGORITHM = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_SSL_TRUSTMANAGERFACTORY_ALGORITHM), + SSL_CONTEXT_PROTOCOL = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_SSL_CONTEXT_PROTOCOL), + FAN_ENABLED = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_FAN_ENABLED), + IMPLICIT_STATEMENT_CACHE_SIZE = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_IMPLICIT_STATEMENT_CACHE_SIZE), + DEFAULT_LOB_PREFETCH_SIZE = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_DEFAULT_LOB_PREFETCH_SIZE), + DISABLE_OUT_OF_BAND_BREAK = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_NET_DISABLE_OUT_OF_BAND_BREAK), + ENABLE_QUERY_RESULT_CACHE = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_ENABLE_QUERY_RESULT_CACHE), + VSESSION_TERMINAL = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_VSESSION_TERMINAL), + VSESSION_MACHINE = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_VSESSION_MACHINE), + VSESSION_OSUSER = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_VSESSION_OSUSER), + VSESSION_PROGRAM = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_VSESSION_PROGRAM), + VSESSION_PROCESS = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_VSESSION_PROCESS), + LDAP_SECURITY_AUTHENTICATION = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_LDAP_SECURITY_AUTHENTICATION), + LDAP_SECURITY_PRINCIPAL = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_LDAP_SECURITY_PRINCIPAL), + LDAP_SECURITY_CREDENTIALS = Option.sensitiveValueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_LDAP_SECURITY_CREDENTIALS), + LDAP_CONNECT_TIMEOUT = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_JNDI_LDAP_CONNECT_TIMEOUT), + LDAP_READ_TIMEOUT = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_JNDI_LDAP_READ_TIMEOUT), + LDAP_WALLET_LOCATION = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_LDAP_SSL_WALLET_LOCATION), + LDAP_WALLET_PASSWORD = Option.sensitiveValueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_LDAP_SSL_WALLET_PASSWORD), + LDAP_KEYSTORE_TYPE = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_LDAP_SSL_KEYSTORE_TYPE), + LDAP_KEYSTORE = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_LDAP_SSL_KEYSTORE), + LDAP_KEYSTORE_PASSWORD = Option.sensitiveValueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_LDAP_SSL_KEYSTORE_PASSWORD), + LDAP_TRUSTSTORE_TYPE = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_LDAP_SSL_TRUSTSTORE_TYPE), + LDAP_TRUSTSTORE = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_LDAP_SSL_TRUSTSTORE), + LDAP_TRUSTSTORE_PASSWORD = Option.sensitiveValueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_LDAP_SSL_TRUSTSTORE_PASSWORD), + LDAP_CIPHER_SUITES = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_LDAP_SSL_CIPHER_SUITES), + LDAP_VERSIONS = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_LDAP_SSL_VERSIONS), + LDAP_KEYMANAGER_FACTORY_ALGORITHM = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_LDAP_SSL_KEYMANAGER_FACTORY_ALGORITHM), + LDAP_TRUSTMANAGER_FACTORY_ALGORITHM = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_LDAP_SSL_TRUSTMANAGER_FACTORY_ALGORITHM), + LDAP_CONTEXT_PROTOCOL = Option.valueOf( + OracleConnection.CONNECTION_PROPERTY_THIN_LDAP_SSL_CONTEXT_PROTOCOL) + ); + + /** + * Returns the set of all extended options supported by Oracle R2DBC. The + * returned set contains each {@code Option} constant declared by + * {@code OracleR2dbcOptions}. The {@code Set} returned by this method is + * unmodifiable, as described in the class level JavaDoc of {@link Set}. + * @return The set of all options. Not null. Not mutable. + */ + public static Set> options() { + return OPTIONS; + } } diff --git a/src/main/java/oracle/r2dbc/impl/OracleReactiveJdbcAdapter.java b/src/main/java/oracle/r2dbc/impl/OracleReactiveJdbcAdapter.java index 164ea6f..f9879ac 100755 --- a/src/main/java/oracle/r2dbc/impl/OracleReactiveJdbcAdapter.java +++ b/src/main/java/oracle/r2dbc/impl/OracleReactiveJdbcAdapter.java @@ -123,92 +123,6 @@ */ final class OracleReactiveJdbcAdapter implements ReactiveJdbcAdapter { - /** - *

- * The set of JDBC connection properties that this adapter supports. Each - * property in this set is represented as an {@link Option} having the name - * of the supported JDBC connection property. When a property is configured - * with a sensitive value, such as a password, it is represented in this - * set as a {@linkplain Option#sensitiveValueOf(String) sensitive Option}. - *

- * If a new Option is added to this set, then it must be documented - * in the javadoc of {@link #createDataSource(ConnectionFactoryOptions)}, - * and in any other reference that lists which options the Oracle R2DBC Driver - * supports. Undocumented options are useless; Other programmers won't be - * able to use an option if they have no way to understand what the option - * does or how it should be configured. - *

- */ - private static final Set> - JDBC_CONNECTION_PROPERTY_OPTIONS = Set.of( - - // Support TNS_ADMIN (tnsnames.ora, ojdbc.properties). - OracleR2dbcOptions.TNS_ADMIN, - - // Support wallet properties for TCPS/SSL/TLS - OracleR2dbcOptions.TLS_WALLET_LOCATION, - OracleR2dbcOptions.TLS_WALLET_PASSWORD, - - // Support keystore properties for TCPS/SSL/TLS - OracleR2dbcOptions.TLS_KEYSTORE, - OracleR2dbcOptions.TLS_KEYSTORE_TYPE, - Option.sensitiveValueOf( - OracleConnection - .CONNECTION_PROPERTY_THIN_JAVAX_NET_SSL_KEYSTOREPASSWORD), - - // Support truststore properties for TCPS/SSL/TLS - OracleR2dbcOptions.TLS_TRUSTSTORE, - OracleR2dbcOptions.TLS_TRUSTSTORE_TYPE, - OracleR2dbcOptions.TLS_TRUSTSTORE_PASSWORD, - - // Support authentication services (RADIUS, KERBEROS, and TCPS) - OracleR2dbcOptions.AUTHENTICATION_SERVICES, - - // Support fine grained configuration for TCPS/SSL/TLS - OracleR2dbcOptions.TLS_CERTIFICATE_ALIAS, - OracleR2dbcOptions.TLS_SERVER_DN_MATCH, - OracleR2dbcOptions.TLS_SERVER_CERT_DN, - OracleR2dbcOptions.TLS_VERSION, - OracleR2dbcOptions.TLS_CIPHER_SUITES, - OracleR2dbcOptions.TLS_KEYMANAGERFACTORY_ALGORITHM, - OracleR2dbcOptions.TLS_TRUSTMANAGERFACTORY_ALGORITHM, - OracleR2dbcOptions.SSL_CONTEXT_PROTOCOL, - - // Because of bug 32378754, the FAN support in the driver may cause a 10s - // delay to connect. As a workaround the following property can be set - // to false to disable FAN support in the driver. - OracleR2dbcOptions.FAN_ENABLED, - - // Support statement cache configuration - OracleR2dbcOptions.IMPLICIT_STATEMENT_CACHE_SIZE, - - // Support LOB prefetch size configuration. A large size is configured - // by default to support cases where memory is available to store entire - // LOB values. A non-default size may be configured when LOB values are - // too large to be prefetched and must be streamed from Blob/Clob objects. - OracleR2dbcOptions.DEFAULT_LOB_PREFETCH_SIZE, - - // Allow out-of-band (OOB) breaks to be disabled. Oracle JDBC uses OOB - // breaks to interrupt a SQL call after a timeout expires. This option - // may need to be disabled when connecting to an 18.x database. Starting - // in 19.x, the database can detect when it's running on a system where - // OOB is not supported and automatically disable OOB. This automated - // detection is not implemented in 18.x. - OracleR2dbcOptions.DISABLE_OUT_OF_BAND_BREAK, - - // Allow the client-side ResultSet cache to be disabled. It is - // necessary to do so when using the serializable transaction isolation - // level in order to prevent phantom reads. - OracleR2dbcOptions.ENABLE_QUERY_RESULT_CACHE, - - // Allow v$session attributes to be configured for tracing - OracleR2dbcOptions.VSESSION_OSUSER, - OracleR2dbcOptions.VSESSION_TERMINAL, - OracleR2dbcOptions.VSESSION_PROCESS, - OracleR2dbcOptions.VSESSION_PROGRAM, - OracleR2dbcOptions.VSESSION_MACHINE - ); - /** Guards access to a JDBC {@code Connection} created by this adapter */ private final AsyncLock asyncLock = new AsyncLock(); @@ -583,7 +497,13 @@ private static void configureExtendedOptions( } // Apply any JDBC connection property options - for (Option option : JDBC_CONNECTION_PROPERTY_OPTIONS) { + for (Option option : OracleR2dbcOptions.options()) { + + // Skip options in the oracle.r2dbc namespace. These are not JDBC + // connection properties + if (option.name().startsWith("oracle.r2dbc.")) + continue; + // Using Object as the value type allows options to be set as types like // Boolean or Integer. These types make sense for numeric or boolean // connection property values, such as statement cache size, or enable x. diff --git a/src/test/java/oracle/r2dbc/impl/OracleReactiveJdbcAdapterTest.java b/src/test/java/oracle/r2dbc/impl/OracleReactiveJdbcAdapterTest.java index 4a01fe6..b0aae93 100644 --- a/src/test/java/oracle/r2dbc/impl/OracleReactiveJdbcAdapterTest.java +++ b/src/test/java/oracle/r2dbc/impl/OracleReactiveJdbcAdapterTest.java @@ -542,8 +542,6 @@ public void testLdapUrl() throws Exception { // Set up the mock LDAP context factory. See JavaDoc of TestContextFactory // for details about this. - NamingManager.setInitialContextFactoryBuilder(environment -> - new TestContextFactory()); TestContextFactory.bind(ldapPath, createDescriptor()); // Now verify that the LDAP URL is resolved to the descriptor @@ -586,8 +584,6 @@ public void testMultiLdapUrl() throws Exception { // Set up the mock LDAP context factory. A descriptor is bound to the last // endpoint only. See JavaDoc of TestContextFactory for details about this. - NamingManager.setInitialContextFactoryBuilder(environment -> - new TestContextFactory()); TestContextFactory.bind("salesdb", createDescriptor()); // Now verify that the LDAP URL is resolved to the descriptor diff --git a/src/test/java/oracle/r2dbc/util/TestContextFactory.java b/src/test/java/oracle/r2dbc/util/TestContextFactory.java index e0c2f1e..46ffa93 100644 --- a/src/test/java/oracle/r2dbc/util/TestContextFactory.java +++ b/src/test/java/oracle/r2dbc/util/TestContextFactory.java @@ -33,6 +33,7 @@ import javax.naming.directory.ModificationItem; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; +import javax.naming.spi.NamingManager; import java.util.HashMap; import java.util.Hashtable; @@ -94,6 +95,17 @@ public final class TestContextFactory implements javax.naming.spi.InitialContextFactory { + static { + // Set a factory builder that overrides any configuration of + // Context.INITIAL_FACTORY_CONTEXT. + try { + NamingManager.setInitialContextFactoryBuilder(environment -> + new TestContextFactory()); + } + catch (Exception exception) { + throw new RuntimeException(exception); + } + } /** * The {@code DirContext} object constructed by Oracle JDBC will delegate to * this instance of {@code TestDirContext}. From e0e434b0cbae91e2eb28fbe494ad63abee16b916 Mon Sep 17 00:00:00 2001 From: Michael-A-McMahon Date: Mon, 10 Oct 2022 12:17:23 -0700 Subject: [PATCH 5/5] Fixing JavaDocs --- .../oracle/r2dbc/impl/OracleReactiveJdbcAdapter.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/oracle/r2dbc/impl/OracleReactiveJdbcAdapter.java b/src/main/java/oracle/r2dbc/impl/OracleReactiveJdbcAdapter.java index 318db52..fd8b726 100755 --- a/src/main/java/oracle/r2dbc/impl/OracleReactiveJdbcAdapter.java +++ b/src/main/java/oracle/r2dbc/impl/OracleReactiveJdbcAdapter.java @@ -352,19 +352,20 @@ public DataSource createDataSource(ConnectionFactoryOptions options) { * as a strict directive to use TLS, and so it takes precedence over any value * that may otherwise be specified by the {@code PROTOCOL} option. *

- * If the {@code SSL} option is not set, then URL is composed with the any + * If the {@code SSL} option is not set, then the URL is composed with any * value set for {@link ConnectionFactoryOptions#PROTOCOL} option. For * instance, if the {@code PROTOCOL} option is set to "ldap" then the URL - * is composed as: {@code jdbc:oracle:thins:@ldap://...}. + * is composed as: {@code jdbc:oracle:thin:@ldap://...}. *

* For consistency with the Oracle JDBC URL, an Oracle R2DBC URL might include - * multiple space separated LDAP addresses, like this: + * multiple space separated LDAP addresses, where the space is percent encoded, + * like this: *

    * r2dbc:oracle:ldap://example.com:3500/cn=salesdept,cn=OracleContext,dc=com/salesdb%20ldap://example.com:3500/cn=salesdept,cn=OracleContext,dc=com/salesdb
    * 
* The %20 encoding of the space character must be used in order for * {@link ConnectionFactoryOptions#parse(CharSequence)} to recognize the URL - * syntax. When multiple address are specified this way, the {@code DATABASE} + * syntax. When multiple addresses are specified this way, the {@code DATABASE} * option will have the value of: *
    * cn=salesdept,cn=OracleContext,dc=com/salesdb ldap://example.com:3500/cn=salesdept,cn=OracleContext,dc=com/salesdb