Skip to content

Commit 6dd5fbe

Browse files
Handle empty protocol option
1 parent 04aceb5 commit 6dd5fbe

File tree

3 files changed

+132
-49
lines changed

3 files changed

+132
-49
lines changed

src/main/java/oracle/r2dbc/impl/OracleReactiveJdbcAdapter.java

Lines changed: 71 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -346,17 +346,6 @@ public DataSource createDataSource(ConnectionFactoryOptions options) {
346346
* specified in the javadoc of
347347
* {@link #createDataSource(ConnectionFactoryOptions)}
348348
* </p><p>
349-
* If the {@link ConnectionFactoryOptions#SSL} option is set, then the JDBC
350-
* URL is composed with the tcps protocol, as in:
351-
* {@code jdbc:oracle:thins:@tcps:...}. The {@code SSL} option is interpreted
352-
* as a strict directive to use TLS, and so it takes precedence over any value
353-
* that may otherwise be specified by the {@code PROTOCOL} option.
354-
* </p><p>
355-
* If the {@code SSL} option is not set, then the URL is composed with any
356-
* value set for {@link ConnectionFactoryOptions#PROTOCOL} option. For
357-
* instance, if the {@code PROTOCOL} option is set to "ldap" then the URL
358-
* is composed as: {@code jdbc:oracle:thin:@ldap://...}.
359-
* </p><p>
360349
* For consistency with the Oracle JDBC URL, an Oracle R2DBC URL might include
361350
* multiple space separated LDAP addresses, where the space is percent encoded,
362351
* like this:
@@ -389,30 +378,77 @@ private static String composeJdbcUrl(ConnectionFactoryOptions options) {
389378
validateDescriptorOptions(options);
390379
return "jdbc:oracle:thin:@" + descriptor;
391380
}
392-
else {
393-
Object protocol =
394-
Boolean.TRUE.equals(parseOptionValue(
395-
SSL, options, Boolean.class, Boolean::valueOf))
396-
? "tcps"
397-
: options.getValue(PROTOCOL);
398-
Object host = options.getRequiredValue(HOST);
399-
Integer port = parseOptionValue(
400-
PORT, options, Integer.class, Integer::valueOf);
401-
Object serviceName = options.getValue(DATABASE);
402-
403-
Object dnMatch =
404-
options.getValue(OracleR2dbcOptions.TLS_SERVER_DN_MATCH);
405-
406-
return String.format("jdbc:oracle:thin:@%s%s%s%s?%s=%s",
407-
protocol == null ? "" : protocol + "://",
408-
host,
409-
port != null ? (":" + port) : "",
410-
serviceName != null ? ("/" + serviceName) : "",
411-
// Workaround for Oracle JDBC bug #33150409. DN matching is enabled
412-
// unless the property is set as a query parameter.
413-
OracleR2dbcOptions.TLS_SERVER_DN_MATCH.name(),
414-
dnMatch == null ? "false" : dnMatch);
415-
}
381+
382+
String protocol = composeJdbcProtocol(options);
383+
384+
Object host = options.getRequiredValue(HOST);
385+
386+
Integer port =
387+
parseOptionValue(PORT, options, Integer.class, Integer::valueOf);
388+
389+
Object serviceName = options.getValue(DATABASE);
390+
391+
Object dnMatch =
392+
options.getValue(OracleR2dbcOptions.TLS_SERVER_DN_MATCH);
393+
394+
return String.format("jdbc:oracle:thin:@%s%s%s%s?%s=%s",
395+
protocol,
396+
host,
397+
port != null ? (":" + port) : "",
398+
serviceName != null ? ("/" + serviceName) : "",
399+
// Workaround for Oracle JDBC bug #33150409. DN matching is enabled
400+
// unless the property is set as a query parameter.
401+
OracleR2dbcOptions.TLS_SERVER_DN_MATCH.name(),
402+
dnMatch == null ? "false" : dnMatch);
403+
}
404+
405+
/**
406+
* <p>
407+
* Composes the protocol section of an Oracle JDBC URL. This is an optional
408+
* section that may appear after the '@' symbol. For instance, the follow URL
409+
* would specify the "tcps" protocol:
410+
* </p><p>
411+
* <pre>
412+
* jdbc:oracle:thin:@tcps://...
413+
* </pre>
414+
* </p><p>
415+
* If {@link ConnectionFactoryOptions#SSL} is set, then "tcps://" is returned.
416+
* The {@code SSL} option is interpreted as a strict directive to use TLS, and
417+
* so it takes precedence over any value that may be specified with the
418+
* {@link ConnectionFactoryOptions#PROTOCOL} option.
419+
* </p><p>
420+
* Otherwise, if the {@code SSL} option is not set, then the protocol section
421+
* is composed with any value set to the
422+
* {@link ConnectionFactoryOptions#PROTOCOL} option. For
423+
* instance, if the {@code PROTOCOL} option is set to "ldap" then the URL
424+
* is composed as: {@code jdbc:oracle:thin:@ldap://...}.
425+
* </p><p>
426+
* If the {@code PROTOCOL} option is set to an empty string, this is
427+
* considered equivalent to not setting the option at all. The R2DBC Pool
428+
* library is known to set an empty string as the protocol .
429+
* </p>
430+
* @param options Options that may or may not specify a protocol. Not null.
431+
* @return The specified protocol, or an empty string if none is specified.
432+
*/
433+
private static String composeJdbcProtocol(ConnectionFactoryOptions options) {
434+
435+
Boolean isSSL =
436+
parseOptionValue(SSL, options, Boolean.class, Boolean::valueOf);
437+
438+
if (Boolean.TRUE.equals(isSSL))
439+
return "tcps://";
440+
441+
Object protocolObject = options.getValue(PROTOCOL);
442+
443+
if (protocolObject == null)
444+
return "";
445+
446+
String protocol = protocolObject.toString();
447+
448+
if (protocol.isEmpty())
449+
return "";
450+
451+
return protocol + "://";
416452
}
417453

418454
/**

src/test/java/oracle/r2dbc/impl/OracleConnectionFactoryImplTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,4 +189,5 @@ public void testGetMetadata() {
189189
.getMetadata()
190190
.getName());
191191
}
192+
192193
}

src/test/java/oracle/r2dbc/impl/OracleReactiveJdbcAdapterTest.java

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,18 @@
2828
import io.r2dbc.spi.Option;
2929
import io.r2dbc.spi.R2dbcTimeoutException;
3030
import io.r2dbc.spi.Result;
31+
import io.r2dbc.spi.Statement;
3132
import oracle.jdbc.OracleConnection;
3233
import oracle.jdbc.datasource.OracleDataSource;
3334
import oracle.r2dbc.OracleR2dbcOptions;
3435
import oracle.r2dbc.test.DatabaseConfig;
3536
import oracle.r2dbc.util.TestContextFactory;
37+
import org.junit.jupiter.api.Assumptions;
3638
import org.junit.jupiter.api.Test;
3739
import reactor.core.publisher.Flux;
3840
import reactor.core.publisher.Mono;
3941

42+
import javax.xml.crypto.Data;
4043
import java.io.IOException;
4144
import java.nio.channels.ServerSocketChannel;
4245
import java.nio.channels.SocketChannel;
@@ -46,6 +49,7 @@
4649
import java.sql.SQLException;
4750
import java.time.Duration;
4851
import java.time.ZonedDateTime;
52+
import java.util.List;
4953
import java.util.Objects;
5054
import java.util.Optional;
5155
import java.util.Properties;
@@ -68,6 +72,7 @@
6872
import static io.r2dbc.spi.ConnectionFactoryOptions.HOST;
6973
import static io.r2dbc.spi.ConnectionFactoryOptions.PASSWORD;
7074
import static io.r2dbc.spi.ConnectionFactoryOptions.PORT;
75+
import static io.r2dbc.spi.ConnectionFactoryOptions.PROTOCOL;
7176
import static io.r2dbc.spi.ConnectionFactoryOptions.STATEMENT_TIMEOUT;
7277
import static io.r2dbc.spi.ConnectionFactoryOptions.USER;
7378
import static java.lang.String.format;
@@ -85,6 +90,7 @@
8590
import static oracle.r2dbc.util.Awaits.awaitExecution;
8691
import static oracle.r2dbc.util.Awaits.awaitNone;
8792
import static oracle.r2dbc.util.Awaits.awaitOne;
93+
import static oracle.r2dbc.util.Awaits.awaitQuery;
8894
import static oracle.r2dbc.util.Awaits.awaitUpdate;
8995
import static oracle.r2dbc.util.Awaits.tryAwaitExecution;
9096
import static oracle.r2dbc.util.Awaits.tryAwaitNone;
@@ -603,20 +609,6 @@ public void testMultiLdapUrl() throws Exception {
603609
}
604610
}
605611

606-
/**
607-
* Returns an Oracle Net Descriptor having the values configured by
608-
* {@link DatabaseConfig}
609-
* @return An Oracle Net Descriptor for the test database.
610-
*/
611-
private static String createDescriptor() {
612-
return format(
613-
"(DESCRIPTION=(ADDRESS=(HOST=%s)(PORT=%d)(PROTOCOL=%s))" +
614-
"(CONNECT_DATA=(SERVICE_NAME=%s)))",
615-
host(), port(),
616-
Objects.requireNonNullElse(protocol(), "tcp"),
617-
serviceName());
618-
}
619-
620612
/**
621613
* Verifies the {@link OracleR2dbcOptions#TIMEZONE_AS_REGION} option
622614
*/
@@ -665,6 +657,60 @@ public void testTimezoneAsRegion() {
665657
}
666658
}
667659

660+
661+
/**
662+
* Verifies behavior when {@link ConnectionFactoryOptions#PROTOCOL} is set
663+
* to an empty string. In this case, the driver is expected to behave as if
664+
* no protocol were specified.
665+
*/
666+
@Test
667+
public void testEmptyProtocol() {
668+
Assumptions.assumeTrue(
669+
DatabaseConfig.protocol() == null,
670+
"Test requires no PROTOCOL in config.properties");
671+
672+
ConnectionFactoryOptions.Builder optionsBuilder =
673+
ConnectionFactoryOptions.builder()
674+
.option(PROTOCOL, "")
675+
.option(DRIVER, "oracle")
676+
.option(HOST, DatabaseConfig.host())
677+
.option(PORT, DatabaseConfig.port())
678+
.option(DATABASE, DatabaseConfig.serviceName())
679+
.option(USER, DatabaseConfig.user())
680+
.option(PASSWORD, DatabaseConfig.password());
681+
682+
Duration timeout = DatabaseConfig.connectTimeout();
683+
if (timeout != null)
684+
optionsBuilder.option(CONNECT_TIMEOUT, timeout);
685+
686+
ConnectionFactoryOptions options = optionsBuilder.build();
687+
688+
Connection connection = awaitOne(ConnectionFactories.get(options).create());
689+
try {
690+
Statement statement =
691+
connection.createStatement("SELECT 1 FROM sys.dual");
692+
693+
awaitQuery(List.of(1), row -> row.get(0, Integer.class), statement);
694+
}
695+
finally {
696+
tryAwaitNone(connection.close());
697+
}
698+
}
699+
700+
/**
701+
* Returns an Oracle Net Descriptor having the values configured by
702+
* {@link DatabaseConfig}
703+
* @return An Oracle Net Descriptor for the test database.
704+
*/
705+
private static String createDescriptor() {
706+
return format(
707+
"(DESCRIPTION=(ADDRESS=(HOST=%s)(PORT=%d)(PROTOCOL=%s))" +
708+
"(CONNECT_DATA=(SERVICE_NAME=%s)))",
709+
host(), port(),
710+
Objects.requireNonNullElse(protocol(), "tcp"),
711+
serviceName());
712+
}
713+
668714
/**
669715
* Verifies that an attempt to connect with a {@code listeningChannel}
670716
* results in an {@link R2dbcTimeoutException}.

0 commit comments

Comments
 (0)