diff --git a/src/main/java/io/r2dbc/postgresql/PostgresqlConnectionConfiguration.java b/src/main/java/io/r2dbc/postgresql/PostgresqlConnectionConfiguration.java index 294c45ff..cb9ec11d 100644 --- a/src/main/java/io/r2dbc/postgresql/PostgresqlConnectionConfiguration.java +++ b/src/main/java/io/r2dbc/postgresql/PostgresqlConnectionConfiguration.java @@ -16,7 +16,6 @@ package io.r2dbc.postgresql; - import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.r2dbc.postgresql.client.DefaultHostnameVerifier; @@ -26,6 +25,7 @@ import io.r2dbc.postgresql.extension.CodecRegistrar; import io.r2dbc.postgresql.extension.Extension; import io.r2dbc.postgresql.util.Assert; +import io.r2dbc.spi.ConnectionFactoryOptions; import reactor.netty.tcp.SslProvider; import reactor.util.annotation.Nullable; @@ -39,6 +39,26 @@ import java.util.function.Function; import java.util.function.Supplier; +import static io.r2dbc.postgresql.PostgresqlConnectionFactoryProvider.APPLICATION_NAME; +import static io.r2dbc.postgresql.PostgresqlConnectionFactoryProvider.AUTODETECT_EXTENSIONS; +import static io.r2dbc.postgresql.PostgresqlConnectionFactoryProvider.FORCE_BINARY; +import static io.r2dbc.postgresql.PostgresqlConnectionFactoryProvider.OPTIONS; +import static io.r2dbc.postgresql.PostgresqlConnectionFactoryProvider.SCHEMA; +import static io.r2dbc.postgresql.PostgresqlConnectionFactoryProvider.SOCKET; +import static io.r2dbc.postgresql.PostgresqlConnectionFactoryProvider.SSL_CERT; +import static io.r2dbc.postgresql.PostgresqlConnectionFactoryProvider.SSL_CONTEXT_BUILDER_CUSTOMIZER; +import static io.r2dbc.postgresql.PostgresqlConnectionFactoryProvider.SSL_HOSTNAME_VERIFIER; +import static io.r2dbc.postgresql.PostgresqlConnectionFactoryProvider.SSL_KEY; +import static io.r2dbc.postgresql.PostgresqlConnectionFactoryProvider.SSL_MODE; +import static io.r2dbc.postgresql.PostgresqlConnectionFactoryProvider.SSL_PASSWORD; +import static io.r2dbc.postgresql.PostgresqlConnectionFactoryProvider.SSL_ROOT_CERT; +import static io.r2dbc.spi.ConnectionFactoryOptions.CONNECT_TIMEOUT; +import static io.r2dbc.spi.ConnectionFactoryOptions.DATABASE; +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.SSL; +import static io.r2dbc.spi.ConnectionFactoryOptions.USER; import static reactor.netty.tcp.SslProvider.DefaultConfigurationType.TCP; /** @@ -108,33 +128,31 @@ public static Builder builder() { return new Builder(); } - private static String repeat(int length, String character) { - - StringBuilder builder = new StringBuilder(); - - for (int i = 0; i < length; i++) { - builder.append(character); - } - - return builder.toString(); + /** + * Returns a new {@link Builder} configured with the given {@link ConnectionFactoryOptions}. + * + * @return a {@link Builder} + */ + public static Builder builder(ConnectionFactoryOptions connectionFactoryOptions) { + return Builder.fromConnectionFactoryOptions(connectionFactoryOptions); } @Override public String toString() { return "PostgresqlConnectionConfiguration{" + - "applicationName='" + this.applicationName + '\'' + - ", autodetectExtensions='" + this.autodetectExtensions + '\'' + - ", connectTimeout=" + this.connectTimeout + - ", database='" + this.database + '\'' + - ", extensions=" + this.extensions + - ", forceBinary='" + this.forceBinary + '\'' + - ", host='" + this.host + '\'' + - ", options='" + this.options + '\'' + - ", password='" + repeat(this.password != null ? this.password.length() : 0, "*") + '\'' + - ", port=" + this.port + - ", schema='" + this.schema + '\'' + - ", username='" + this.username + '\'' + - '}'; + "applicationName='" + this.applicationName + '\'' + + ", autodetectExtensions='" + this.autodetectExtensions + '\'' + + ", connectTimeout=" + this.connectTimeout + + ", database='" + this.database + '\'' + + ", extensions=" + this.extensions + + ", forceBinary='" + this.forceBinary + '\'' + + ", host='" + this.host + '\'' + + ", options='" + this.options + '\'' + + ", password='" + obfuscate(this.password != null ? this.password.length() : 0) + '\'' + + ", port=" + this.port + + ", schema='" + this.schema + '\'' + + ", username='" + this.username + '\'' + + '}'; } String getApplicationName() { @@ -226,6 +244,17 @@ SSLConfig getSslConfig() { return this.sslConfig; } + private static String obfuscate(int length) { + + StringBuilder builder = new StringBuilder(); + + for (int i = 0; i < length; i++) { + builder.append("*"); + } + + return builder.toString(); + } + /** * A builder for {@link PostgresqlConnectionConfiguration} instances. *

@@ -287,6 +316,60 @@ public static final class Builder { private Builder() { } + /** + * Configure the builder with the given {@link ConnectionFactoryOptions}. + * + * @param connectionFactoryOptions {@link ConnectionFactoryOptions} + * @return this {@link Builder} + * @throws IllegalArgumentException if {@code connectionFactoryOptions} is {@code null} + */ + private static Builder fromConnectionFactoryOptions(ConnectionFactoryOptions connectionFactoryOptions) { + + Assert.requireNonNull(connectionFactoryOptions, "connectionFactoryOptions must not be null"); + + Builder builder = new Builder(); + + builder.connectTimeout(connectionFactoryOptions.getValue(CONNECT_TIMEOUT)); + builder.database(connectionFactoryOptions.getValue(DATABASE)); + builder.password(connectionFactoryOptions.getValue(PASSWORD)); + builder.schema(connectionFactoryOptions.getValue(SCHEMA)); + builder.username(connectionFactoryOptions.getRequiredValue(USER)); + + String applicationName = connectionFactoryOptions.getValue(APPLICATION_NAME); + if (applicationName != null) { + builder.applicationName(applicationName); + } + + Object autodetectExtensions = connectionFactoryOptions.getValue(AUTODETECT_EXTENSIONS); + if (autodetectExtensions != null) { + builder.autodetectExtensions(convertToBoolean(autodetectExtensions)); + } + + Integer port = connectionFactoryOptions.getValue(PORT); + if (port != null) { + builder.port(port); + } + + Object forceBinary = connectionFactoryOptions.getValue(FORCE_BINARY); + if (forceBinary != null) { + builder.forceBinary(convertToBoolean(forceBinary)); + } + + Map options = connectionFactoryOptions.getValue(OPTIONS); + if (options != null) { + builder.options(options); + } + + if (isUsingTcp(connectionFactoryOptions)) { + builder.host(connectionFactoryOptions.getRequiredValue(HOST)); + setupSsl(builder, connectionFactoryOptions); + } else { + builder.socket(connectionFactoryOptions.getRequiredValue(SOCKET)); + } + + return builder; + } + /** * Configure the application name. Defaults to {@code postgresql-r2dbc}. * @@ -505,7 +588,7 @@ public Builder sslCert(String sslCert) { /** * Configure ssl HostnameVerifier. * - * @param sslHostnameVerifier {@link javax.net.ssl.HostnameVerifier} + * @param sslHostnameVerifier {@link HostnameVerifier} * @return this {@link Builder} */ public Builder sslHostnameVerifier(HostnameVerifier sslHostnameVerifier) { @@ -546,31 +629,6 @@ public Builder sslPassword(@Nullable CharSequence sslPassword) { return this; } - @Override - public String toString() { - return "Builder{" + - "applicationName='" + this.applicationName + '\'' + - ", autodetectExtensions='" + this.autodetectExtensions + '\'' + - ", connectTimeout='" + this.connectTimeout + '\'' + - ", database='" + this.database + '\'' + - ", extensions='" + this.extensions + '\'' + - ", forceBinary='" + this.forceBinary + '\'' + - ", host='" + this.host + '\'' + - ", parameters='" + this.options + '\'' + - ", password='" + repeat(this.password != null ? this.password.length() : 0, "*") + '\'' + - ", port=" + this.port + - ", schema='" + this.schema + '\'' + - ", username='" + this.username + '\'' + - ", socket='" + this.socket + '\'' + - ", sslContextBuilderCustomizer='" + this.sslContextBuilderCustomizer + '\'' + - ", sslMode='" + this.sslMode + '\'' + - ", sslRootCert='" + this.sslRootCert + '\'' + - ", sslCert='" + this.sslCert + '\'' + - ", sslKey='" + this.sslKey + '\'' + - ", sslHostnameVerifier='" + this.sslHostnameVerifier + '\'' + - '}'; - } - /** * Configure ssl root cert for server certificate validation. * @@ -594,6 +652,31 @@ public Builder username(String username) { return this; } + @Override + public String toString() { + return "Builder{" + + "applicationName='" + this.applicationName + '\'' + + ", autodetectExtensions='" + this.autodetectExtensions + '\'' + + ", connectTimeout='" + this.connectTimeout + '\'' + + ", database='" + this.database + '\'' + + ", extensions='" + this.extensions + '\'' + + ", forceBinary='" + this.forceBinary + '\'' + + ", host='" + this.host + '\'' + + ", parameters='" + this.options + '\'' + + ", password='" + obfuscate(this.password != null ? this.password.length() : 0) + '\'' + + ", port=" + this.port + + ", schema='" + this.schema + '\'' + + ", username='" + this.username + '\'' + + ", socket='" + this.socket + '\'' + + ", sslContextBuilderCustomizer='" + this.sslContextBuilderCustomizer + '\'' + + ", sslMode='" + this.sslMode + '\'' + + ", sslRootCert='" + this.sslRootCert + '\'' + + ", sslCert='" + this.sslCert + '\'' + + ", sslKey='" + this.sslKey + '\'' + + ", sslHostnameVerifier='" + this.sslHostnameVerifier + '\'' + + '}'; + } + private SSLConfig createSslConfig() { if (this.socket != null || this.sslMode == SSLMode.DISABLE) { return SSLConfig.disabled(); @@ -618,23 +701,23 @@ private Supplier createSslProvider() { // Emulate Libpq behavior // Determining the default file location - String pathsep = System.getProperty("file.separator"); - String defaultdir; + String pathSeparator = System.getProperty("file.separator"); + String defaultDir; if (System.getProperty("os.name").toLowerCase().contains("windows")) { // It is Windows - defaultdir = System.getenv("APPDATA") + pathsep + "postgresql" + pathsep; + defaultDir = System.getenv("APPDATA") + pathSeparator + "postgresql" + pathSeparator; } else { - defaultdir = System.getProperty("user.home") + pathsep + ".postgresql" + pathsep; + defaultDir = System.getProperty("user.home") + pathSeparator + ".postgresql" + pathSeparator; } if (sslCert == null) { - String pathname = defaultdir + "postgresql.crt"; + String pathname = defaultDir + "postgresql.crt"; if (new File(pathname).exists()) { sslCert = pathname; } } if (sslKey == null) { - String pathname = defaultdir + "postgresql.pk8"; + String pathname = defaultDir + "postgresql.pk8"; if (new File(pathname).exists()) { sslKey = pathname; } @@ -645,11 +728,80 @@ private Supplier createSslProvider() { sslContextBuilder.keyManager(new File(sslCert), new File(sslKey), sslPassword); } - return () -> SslProvider.builder() .sslContext(this.sslContextBuilderCustomizer.apply(sslContextBuilder)) .defaultConfiguration(TCP) .build(); } + + private static void setupSsl(Builder builder, ConnectionFactoryOptions connectionFactoryOptions) { + Boolean ssl = connectionFactoryOptions.getValue(SSL); + if (ssl != null && ssl) { + builder.enableSsl(); + } + + Object sslMode = connectionFactoryOptions.getValue(SSL_MODE); + if (sslMode != null) { + if (sslMode instanceof String) { + builder.sslMode(SSLMode.fromValue(sslMode.toString())); + } else { + builder.sslMode((SSLMode) sslMode); + } + } + + String sslRootCert = connectionFactoryOptions.getValue(SSL_ROOT_CERT); + if (sslRootCert != null) { + builder.sslRootCert(sslRootCert); + } + + String sslCert = connectionFactoryOptions.getValue(SSL_CERT); + if (sslCert != null) { + builder.sslCert(sslCert); + } + + String sslKey = connectionFactoryOptions.getValue(SSL_KEY); + if (sslKey != null) { + builder.sslKey(sslKey); + } + + String sslPassword = connectionFactoryOptions.getValue(SSL_PASSWORD); + if (sslPassword != null) { + builder.sslPassword(sslPassword); + } + + if (connectionFactoryOptions.hasOption(SSL_CONTEXT_BUILDER_CUSTOMIZER)) { + builder.sslContextBuilderCustomizer(connectionFactoryOptions.getRequiredValue(SSL_CONTEXT_BUILDER_CUSTOMIZER)); + } + + setSslHostnameVerifier(builder, connectionFactoryOptions); + } + + private static void setSslHostnameVerifier(Builder builder, ConnectionFactoryOptions connectionFactoryOptions) { + Object sslHostnameVerifier = connectionFactoryOptions.getValue(SSL_HOSTNAME_VERIFIER); + if (sslHostnameVerifier != null) { + + if (sslHostnameVerifier instanceof String) { + + try { + Class verifierClass = Class.forName((String) sslHostnameVerifier); + Object verifier = verifierClass.getConstructor().newInstance(); + + builder.sslHostnameVerifier((HostnameVerifier) verifier); + } catch (ReflectiveOperationException e) { + throw new IllegalStateException("Cannot instantiate " + sslHostnameVerifier, e); + } + } else { + builder.sslHostnameVerifier((HostnameVerifier) sslHostnameVerifier); + } + } + } + + private static boolean isUsingTcp(ConnectionFactoryOptions connectionFactoryOptions) { + return !connectionFactoryOptions.hasOption(SOCKET); + } + + private static boolean convertToBoolean(Object value) { + return value instanceof Boolean ? (boolean) value : Boolean.parseBoolean(value.toString()); + } } } diff --git a/src/main/java/io/r2dbc/postgresql/PostgresqlConnectionFactoryProvider.java b/src/main/java/io/r2dbc/postgresql/PostgresqlConnectionFactoryProvider.java index 72abb765..0515e746 100644 --- a/src/main/java/io/r2dbc/postgresql/PostgresqlConnectionFactoryProvider.java +++ b/src/main/java/io/r2dbc/postgresql/PostgresqlConnectionFactoryProvider.java @@ -27,14 +27,7 @@ import java.util.Map; import java.util.function.Function; -import static io.r2dbc.spi.ConnectionFactoryOptions.CONNECT_TIMEOUT; -import static io.r2dbc.spi.ConnectionFactoryOptions.DATABASE; import static io.r2dbc.spi.ConnectionFactoryOptions.DRIVER; -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.SSL; -import static io.r2dbc.spi.ConnectionFactoryOptions.USER; /** * An implementation of {@link ConnectionFactoryProvider} for creating {@link PostgresqlConnectionFactory}s. @@ -118,114 +111,12 @@ public final class PostgresqlConnectionFactoryProvider implements ConnectionFact @Override public PostgresqlConnectionFactory create(ConnectionFactoryOptions connectionFactoryOptions) { - return new PostgresqlConnectionFactory(createConfiguration(connectionFactoryOptions)); - } - private static PostgresqlConnectionConfiguration createConfiguration(ConnectionFactoryOptions connectionFactoryOptions) { - Assert.requireNonNull(connectionFactoryOptions, "connectionFactoryOptions must not be null"); + PostgresqlConnectionConfiguration configuration = PostgresqlConnectionConfiguration + .builder(connectionFactoryOptions) + .build(); - boolean tcp; - PostgresqlConnectionConfiguration.Builder builder = PostgresqlConnectionConfiguration.builder(); - - String applicationName = connectionFactoryOptions.getValue(APPLICATION_NAME); - if (applicationName != null) { - builder.applicationName(applicationName); - } - - Object autodetectExtensions = connectionFactoryOptions.getValue(AUTODETECT_EXTENSIONS); - if (autodetectExtensions != null) { - builder.autodetectExtensions(convertToBoolean(autodetectExtensions)); - } - - builder.connectTimeout(connectionFactoryOptions.getValue(CONNECT_TIMEOUT)); - builder.database(connectionFactoryOptions.getValue(DATABASE)); - - if (connectionFactoryOptions.hasOption(SOCKET)) { - tcp = false; - builder.socket(connectionFactoryOptions.getRequiredValue(SOCKET)); - } else { - tcp = true; - builder.host(connectionFactoryOptions.getRequiredValue(HOST)); - } - builder.password(connectionFactoryOptions.getValue(PASSWORD)); - builder.schema(connectionFactoryOptions.getValue(SCHEMA)); - builder.username(connectionFactoryOptions.getRequiredValue(USER)); - - Integer port = connectionFactoryOptions.getValue(PORT); - if (port != null) { - builder.port(port); - } - - Object forceBinary = connectionFactoryOptions.getValue(FORCE_BINARY); - - if (forceBinary != null) { - builder.forceBinary(convertToBoolean(forceBinary)); - } - - Map options = connectionFactoryOptions.getValue(OPTIONS); - if (options != null) { - builder.options(options); - } - - if (tcp) { - Boolean ssl = connectionFactoryOptions.getValue(SSL); - if (ssl != null && ssl) { - builder.enableSsl(); - } - - Object sslMode = connectionFactoryOptions.getValue(SSL_MODE); - if (sslMode != null) { - if (sslMode instanceof String) { - builder.sslMode(SSLMode.fromValue(sslMode.toString())); - } else { - builder.sslMode((SSLMode) sslMode); - } - } - - String sslRootCert = connectionFactoryOptions.getValue(SSL_ROOT_CERT); - if (sslRootCert != null) { - builder.sslRootCert(sslRootCert); - } - - String sslCert = connectionFactoryOptions.getValue(SSL_CERT); - if (sslCert != null) { - builder.sslCert(sslCert); - } - - String sslKey = connectionFactoryOptions.getValue(SSL_KEY); - if (sslKey != null) { - builder.sslKey(sslKey); - } - - String sslPassword = connectionFactoryOptions.getValue(SSL_PASSWORD); - if (sslPassword != null) { - builder.sslPassword(sslPassword); - } - - Object sslHostnameVerifier = connectionFactoryOptions.getValue(SSL_HOSTNAME_VERIFIER); - if (sslHostnameVerifier != null) { - - if (sslHostnameVerifier instanceof String) { - - try { - Class verifierClass = Class.forName((String) sslHostnameVerifier); - Object verifier = verifierClass.getConstructor().newInstance(); - - builder.sslHostnameVerifier((HostnameVerifier) verifier); - } catch (ReflectiveOperationException e) { - throw new IllegalStateException("Cannot instantiate " + sslHostnameVerifier, e); - } - } else { - builder.sslHostnameVerifier((HostnameVerifier) sslHostnameVerifier); - } - } - - if (connectionFactoryOptions.hasOption(SSL_CONTEXT_BUILDER_CUSTOMIZER)) { - builder.sslContextBuilderCustomizer(connectionFactoryOptions.getRequiredValue(SSL_CONTEXT_BUILDER_CUSTOMIZER)); - } - } - - return builder.build(); + return new PostgresqlConnectionFactory(configuration); } @Override @@ -240,8 +131,4 @@ public boolean supports(ConnectionFactoryOptions connectionFactoryOptions) { String driver = connectionFactoryOptions.getValue(DRIVER); return driver != null && (driver.equals(POSTGRESQL_DRIVER) || driver.equals(LEGACY_POSTGRESQL_DRIVER)); } - - private static boolean convertToBoolean(Object value) { - return value instanceof Boolean ? (boolean) value : Boolean.parseBoolean(value.toString()); - } }