Skip to content

Commit bcd8f93

Browse files
authored
chore: Use domain name from JDBC URL for SQL Server, Part of #2043. (#2101)
Enables Sql Server users to set the domain name in the JDBC URL. Users simply replace the instance connection name with the domain name. Due to limitations of the SQL Server driver, users cannot use the JDBC URL's host field. Part of #2043
1 parent 1860b76 commit bcd8f93

File tree

4 files changed

+46
-7
lines changed

4 files changed

+46
-7
lines changed

core/src/main/java/com/google/cloud/sql/core/CloudSqlInstanceName.java

+14-1
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@
2424
/**
2525
* This class parses the different parts of a Cloud SQL Connection Name to allow users to easily
2626
* fetch the projectId, regionId, and instanceId.
27+
*
28+
* <p>INTERNAL USE ONLY! This API may change without notice.
2729
*/
28-
class CloudSqlInstanceName {
30+
public class CloudSqlInstanceName {
2931

3032
// Unique identifier for each Cloud SQL instance in the format "PROJECT:REGION:INSTANCE"
3133
// Some legacy project ids are domain-scoped (e.g. "example.com:PROJECT:REGION:INSTANCE")
@@ -68,6 +70,17 @@ public static boolean isValidDomain(String domain) {
6870
return matcher.matches();
6971
}
7072

73+
/**
74+
* Checks if a string is a well-formed instance name.
75+
*
76+
* @param connectionName the value to check
77+
* @return true if it is a well-formed instance name.
78+
*/
79+
public static boolean isValidInstanceName(String connectionName) {
80+
Matcher matcher = CONNECTION_NAME.matcher(connectionName);
81+
return matcher.matches();
82+
}
83+
7184
/**
7285
* Initializes a new CloudSqlInstanceName class.
7386
*

jdbc/sqlserver/src/main/java/com/google/cloud/sql/sqlserver/SocketFactory.java

+11-5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package com.google.cloud.sql.sqlserver;
1818

19+
import com.google.cloud.sql.core.CloudSqlInstanceName;
1920
import com.google.cloud.sql.core.ConnectionConfig;
2021
import com.google.cloud.sql.core.InternalConnectorRegistry;
2122
import com.google.common.annotations.VisibleForTesting;
@@ -25,7 +26,6 @@
2526
import java.net.InetAddress;
2627
import java.net.Socket;
2728
import java.net.URLDecoder;
28-
import java.nio.charset.StandardCharsets;
2929
import java.util.List;
3030
import java.util.Properties;
3131

@@ -43,14 +43,20 @@ public class SocketFactory extends javax.net.SocketFactory {
4343

4444
// props are protected, not private, so that they can be accessed from unit tests
4545
@VisibleForTesting protected Properties props = new Properties();
46+
@VisibleForTesting protected String domainName;
4647

4748
/**
4849
* Implements the {@link SocketFactory} constructor, which can be used to create authenticated
4950
* connections to a Cloud SQL instance.
5051
*/
5152
public SocketFactory(String socketFactoryConstructorArg) throws UnsupportedEncodingException {
5253
List<String> s = Splitter.on('?').splitToList(socketFactoryConstructorArg);
53-
this.props.setProperty(ConnectionConfig.CLOUD_SQL_INSTANCE_PROPERTY, s.get(0));
54+
final String instanceOrDomainName = s.get(0);
55+
if (CloudSqlInstanceName.isValidInstanceName(instanceOrDomainName)) {
56+
this.props.setProperty(ConnectionConfig.CLOUD_SQL_INSTANCE_PROPERTY, instanceOrDomainName);
57+
} else {
58+
domainName = instanceOrDomainName;
59+
}
5460
if (s.size() == 2 && s.get(1).length() > 0) {
5561
Iterable<String> queryParams = Splitter.on('&').split(s.get(1));
5662
for (String param : queryParams) {
@@ -62,8 +68,8 @@ public SocketFactory(String socketFactoryConstructorArg) throws UnsupportedEncod
6268
String.format("Malformed query param in socketFactoryConstructorArg : %s", param));
6369
}
6470
this.props.setProperty(
65-
URLDecoder.decode(splitParam.get(0), StandardCharsets.UTF_8.name()),
66-
URLDecoder.decode(splitParam.get(1), StandardCharsets.UTF_8.name()));
71+
URLDecoder.decode(splitParam.get(0), "utf-8"),
72+
URLDecoder.decode(splitParam.get(1), "utf-8"));
6773
}
6874
} else if (s.size() > 2) {
6975
throw new IllegalArgumentException(
@@ -75,7 +81,7 @@ public SocketFactory(String socketFactoryConstructorArg) throws UnsupportedEncod
7581
public Socket createSocket() throws IOException {
7682
try {
7783
return InternalConnectorRegistry.getInstance()
78-
.connect(ConnectionConfig.fromConnectionProperties(props));
84+
.connect(ConnectionConfig.fromConnectionProperties(props, domainName));
7985
} catch (InterruptedException e) {
8086
throw new RuntimeException(e);
8187
}

jdbc/sqlserver/src/test/java/com/google/cloud/sql/sqlserver/JdbcSqlServerIntegrationTests.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,9 @@ public void setUpPool() throws SQLException {
7474
"socketFactoryClass", "com.google.cloud.sql.sqlserver.SocketFactory");
7575
config.addDataSourceProperty("socketFactoryConstructorArg", CONNECTION_NAME);
7676
config.addDataSourceProperty("encrypt", "false");
77-
config.setConnectionTimeout(10000); // 10s
77+
config.setConnectionTimeout(30000); // 30s
78+
config.setInitializationFailTimeout(10000);
79+
config.setValidationTimeout(10000);
7880

7981
this.connectionPool = new HikariDataSource(config);
8082
}

jdbc/sqlserver/src/test/java/com/google/cloud/sql/sqlserver/JdbcSqlServerUnitTests.java

+18
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
public class JdbcSqlServerUnitTests {
2929

3030
private static final String CONNECTION_NAME = "my-project:my-region:my-instance";
31+
private static final String DOMAIN_NAME = "mydatabase.example.com";
3132

3233
@Test
3334
public void checkConnectionStringNoQueryParams() throws UnsupportedEncodingException {
@@ -46,6 +47,23 @@ public void checkConnectionStringWithQueryParam() throws UnsupportedEncodingExce
4647
assertThat(socketFactory.props.get("ipTypes")).isEqualTo("PRIVATE");
4748
}
4849

50+
@Test
51+
public void checkConnectionStringDomainNameNoQueryParams() throws UnsupportedEncodingException {
52+
SocketFactory socketFactory = new SocketFactory(DOMAIN_NAME);
53+
assertThat(socketFactory.props.get(ConnectionConfig.CLOUD_SQL_INSTANCE_PROPERTY)).isNull();
54+
assertThat(socketFactory.domainName).isEqualTo(DOMAIN_NAME);
55+
}
56+
57+
@Test
58+
public void checkConnectionStringDomainNameWithQueryParam() throws UnsupportedEncodingException {
59+
String socketFactoryConstructorArg =
60+
String.format("%s?%s=%s", DOMAIN_NAME, "ipTypes", "PRIVATE");
61+
SocketFactory socketFactory = new SocketFactory(socketFactoryConstructorArg);
62+
assertThat(socketFactory.props.get(ConnectionConfig.CLOUD_SQL_INSTANCE_PROPERTY)).isNull();
63+
assertThat(socketFactory.domainName).isEqualTo(DOMAIN_NAME);
64+
assertThat(socketFactory.props.get("ipTypes")).isEqualTo("PRIVATE");
65+
}
66+
4967
@Test
5068
public void checkConnectionStringWithEmptyQueryParam() throws UnsupportedEncodingException {
5169
String socketFactoryConstructorArg = String.format("%s?", CONNECTION_NAME);

0 commit comments

Comments
 (0)