4343import java .util .Map ;
4444import java .util .logging .Level ;
4545import java .util .logging .Logger ;
46- import java .util .regex .Pattern ;
4746import javax .annotation .Nullable ;
4847import org .apache .commons .cli .CommandLine ;
4948import org .apache .commons .cli .CommandLineParser ;
@@ -65,13 +64,12 @@ public class OptionsMetadata {
6564 private static final String SPANNER_EMULATOR_HOST_ENV_VAR = "SPANNER_EMULATOR_HOST" ;
6665
6766 static Duration DEFAULT_STARTUP_TIMEOUT = Duration .ofSeconds (30L );
68- private static final String CLOUD_SPANNER_HOST_FORMAT = ".*\\ .googleapis\\ .com.*" ;
69- private static final Pattern CLOUD_SPANNER_HOST_PATTERN =
70- Pattern .compile (CLOUD_SPANNER_HOST_FORMAT );
7167
7268 private static final String EXTERNAL_HOST_PROJECT = "default" ;
7369 private static final String EXTERNAL_HOST_INSTANCE = "default" ;
7470
71+ private static final String IS_EXPERIMENTAL_HOST_PROPERTY_NAME = "isExperimentalHost" ;
72+
7573 /**
7674 * Builder class for creating an instance of {@link OptionsMetadata}.
7775 *
@@ -118,6 +116,7 @@ public static class Builder {
118116 private Duration startupTimeout = DEFAULT_STARTUP_TIMEOUT ;
119117 private String clientCertificate ;
120118 private String clientKey ;
119+ private boolean isExperimentalHost = false ;
121120
122121 Builder () {}
123122
@@ -425,7 +424,7 @@ Builder setStartupTimeout(Duration timeout) {
425424
426425 /**
427426 * Configures mTLS authentication using the provided client certificate and key files. mTLS is
428- * only supported for external spanner hosts.
427+ * only supported for experimental spanner hosts.
429428 *
430429 * @param clientCertificate Path to the client certificate file.
431430 * @param clientKey Path to the client private key file.
@@ -437,6 +436,18 @@ Builder useClientCert(String clientCertificate, String clientKey) {
437436 return this ;
438437 }
439438
439+ /*
440+ * Configures connection to an experimental host endpoint
441+ *
442+ * @params experimentalHost url of the experimental host endpoint
443+ */
444+ @ ExperimentalApi ("https://github.com/googleapis/java-spanner/pull/3574" )
445+ Builder setExperimentalHost (String experimentalHost ) {
446+ setEndpoint (experimentalHost );
447+ this .isExperimentalHost = true ;
448+ return this ;
449+ }
450+
440451 public OptionsMetadata build () {
441452 if (Strings .isNullOrEmpty (project ) && !Strings .isNullOrEmpty (instance )) {
442453 throw SpannerExceptionFactory .newSpannerException (
@@ -504,7 +515,8 @@ private String[] toCommandLineArguments() {
504515 || autoConfigEmulator
505516 || useVirtualGrpcTransportThreads
506517 || enableEndToEndTracing
507- || (clientKey != null && clientCertificate != null )) {
518+ || (clientKey != null && clientCertificate != null )
519+ || isExperimentalHost ) {
508520 StringBuilder jdbcOptionBuilder = new StringBuilder ();
509521 if (usePlainText ) {
510522 jdbcOptionBuilder .append ("usePlainText=true;" );
@@ -530,6 +542,9 @@ private String[] toCommandLineArguments() {
530542 jdbcOptionBuilder .append ("clientCertificate=" ).append (clientCertificate ).append (";" );
531543 jdbcOptionBuilder .append ("clientKey=" ).append (clientKey ).append (";" );
532544 }
545+ if (isExperimentalHost ) {
546+ jdbcOptionBuilder .append ("isExperimentalHost=true;" );
547+ }
533548 addOption (args , OPTION_JDBC_PROPERTIES , jdbcOptionBuilder .toString ());
534549 }
535550 if (logGrpcMessages ) {
@@ -733,8 +748,7 @@ private OptionsMetadata(Builder builder) {
733748 if (!propertyMap .containsKey ("defaultSequenceKind" )) {
734749 propertyMap .put ("defaultSequenceKind" , "bit_reversed_positive" );
735750 }
736- boolean usesExternalHost =
737- isExternalHost (Preconditions .checkNotNull (environment ), commandLine , propertyMap );
751+ boolean usesExperimentalHost = isExperimentalHost (commandLine , propertyMap );
738752
739753 this .environment = environment ;
740754 this .osName = osName ;
@@ -752,7 +766,7 @@ private OptionsMetadata(Builder builder) {
752766 + "OR use -c to set the credentials in PGAdapter and use these credentials for all connections." );
753767 }
754768 if (this .commandLine .hasOption (OPTION_DATABASE_NAME )
755- && !usesExternalHost
769+ && !usesExperimentalHost
756770 && !(this .commandLine .hasOption (OPTION_PROJECT_ID )
757771 && this .commandLine .hasOption (OPTION_INSTANCE_ID ))) {
758772 throw SpannerExceptionFactory .newSpannerException (
@@ -761,7 +775,7 @@ private OptionsMetadata(Builder builder) {
761775 + "Use the options -p <project-id> -i <instance-id> -d <database-id> to specify the "
762776 + "database that all connections to this instance of PGAdapter should use." );
763777 }
764- if ((usesExternalHost
778+ if ((usesExperimentalHost
765779 || (this .commandLine .hasOption (OPTION_PROJECT_ID )
766780 && this .commandLine .hasOption (OPTION_INSTANCE_ID )))
767781 && this .commandLine .hasOption (OPTION_DATABASE_NAME )) {
@@ -1023,7 +1037,7 @@ public SessionPoolOptions getSessionPoolOptions() {
10231037 */
10241038 public String buildCredentialsFile () {
10251039 // Skip if a com.google.auth.Credentials instance has been set.
1026- if (isExternalHost () || credentials != null ) {
1040+ if (isExperimentalHost () || credentials != null ) {
10271041 return null ;
10281042 }
10291043 if (!commandLine .hasOption (OPTION_CREDENTIALS_FILE )) {
@@ -1113,17 +1127,23 @@ private static boolean usesEmulator(
11131127 || isAutoConfigEmulator (propertyMap );
11141128 }
11151129
1116- private boolean isExternalHost () {
1117- return isExternalHost (this .environment , this .commandLine , this .propertyMap );
1130+ private boolean isExperimentalHost () {
1131+ if (this .propertyMap == null ) {
1132+ return isExperimentalHost (
1133+ this .commandLine ,
1134+ parseProperties (this .commandLine .getOptionValue (OPTION_JDBC_PROPERTIES , "" )));
1135+ }
1136+ return isExperimentalHost (this .commandLine , this .propertyMap );
11181137 }
11191138
1120- private static boolean isExternalHost (
1121- Map <String , String > environment , CommandLine commandLine , Map <String , String > propertyMap ) {
1122- return !usesEmulator (environment , propertyMap )
1123- && commandLine .hasOption (OPTION_SPANNER_ENDPOINT )
1124- && !CLOUD_SPANNER_HOST_PATTERN
1125- .matcher (commandLine .getOptionValue (OPTION_SPANNER_ENDPOINT ))
1126- .matches ();
1139+ private static boolean isExperimentalHost (
1140+ CommandLine commandLine , Map <String , String > propertyMap ) {
1141+ if (propertyMap == null ) {
1142+ return false ;
1143+ }
1144+ return commandLine .hasOption (OPTION_SPANNER_ENDPOINT )
1145+ && commandLine .hasOption (OPTION_JDBC_PROPERTIES )
1146+ && propertyMap .containsKey (IS_EXPERIMENTAL_HOST_PROPERTY_NAME );
11271147 }
11281148
11291149 /** Returns the fully qualified database name based on the given database id or name. */
@@ -1135,7 +1155,7 @@ public DatabaseName getDatabaseName(String database) {
11351155 String projectId ;
11361156 if (commandLine .hasOption (OPTION_PROJECT_ID )) {
11371157 projectId = commandLine .getOptionValue (OPTION_PROJECT_ID );
1138- } else if (isExternalHost ()) {
1158+ } else if (isExperimentalHost ()) {
11391159 projectId = EXTERNAL_HOST_PROJECT ;
11401160 } else {
11411161 projectId = getDefaultProjectId ();
@@ -1150,7 +1170,7 @@ public DatabaseName getDatabaseName(String database) {
11501170 String instanceId ;
11511171 if (commandLine .hasOption (OPTION_INSTANCE_ID )) {
11521172 instanceId = commandLine .getOptionValue (OPTION_INSTANCE_ID );
1153- } else if (isExternalHost ()) {
1173+ } else if (isExperimentalHost ()) {
11541174 instanceId = EXTERNAL_HOST_INSTANCE ;
11551175 } else {
11561176 throw SpannerExceptionFactory .newSpannerException (
@@ -1576,10 +1596,10 @@ public boolean hasDefaultConnectionUrl() {
15761596 public DatabaseId getDefaultDatabaseId () {
15771597 return this .hasDefaultConnectionUrl ()
15781598 ? DatabaseId .of (
1579- !commandLine .hasOption (OPTION_PROJECT_ID ) && isExternalHost ()
1599+ !commandLine .hasOption (OPTION_PROJECT_ID ) && isExperimentalHost ()
15801600 ? EXTERNAL_HOST_PROJECT
15811601 : commandLine .getOptionValue (OPTION_PROJECT_ID ),
1582- !commandLine .hasOption (OPTION_INSTANCE_ID ) && isExternalHost ()
1602+ !commandLine .hasOption (OPTION_INSTANCE_ID ) && isExperimentalHost ()
15831603 ? EXTERNAL_HOST_INSTANCE
15841604 : commandLine .getOptionValue (OPTION_INSTANCE_ID ),
15851605 commandLine .getOptionValue (OPTION_DATABASE_NAME ))
@@ -1588,18 +1608,18 @@ public DatabaseId getDefaultDatabaseId() {
15881608
15891609 /** Returns true if these options contain a default instance id. */
15901610 public boolean hasDefaultInstanceId () {
1591- return isExternalHost ()
1611+ return isExperimentalHost ()
15921612 || (commandLine .hasOption (OPTION_PROJECT_ID ) && commandLine .hasOption (OPTION_INSTANCE_ID ));
15931613 }
15941614
15951615 /** Returns the id of the default instance or null if no default has been selected. */
15961616 public InstanceId getDefaultInstanceId () {
15971617 if (hasDefaultInstanceId ()) {
15981618 return InstanceId .of (
1599- !commandLine .hasOption (OPTION_PROJECT_ID ) && isExternalHost ()
1619+ !commandLine .hasOption (OPTION_PROJECT_ID ) && isExperimentalHost ()
16001620 ? EXTERNAL_HOST_PROJECT
16011621 : commandLine .getOptionValue (OPTION_PROJECT_ID ),
1602- !commandLine .hasOption (OPTION_INSTANCE_ID ) && isExternalHost ()
1622+ !commandLine .hasOption (OPTION_INSTANCE_ID ) && isExperimentalHost ()
16031623 ? EXTERNAL_HOST_INSTANCE
16041624 : commandLine .getOptionValue (OPTION_INSTANCE_ID ));
16051625 }
0 commit comments