@@ -161,6 +161,7 @@ public String[] getValidValues() {
161161 private static final String PLAIN_TEXT_PROTOCOL = "http:" ;
162162 private static final String HOST_PROTOCOL = "https:" ;
163163 private static final String DEFAULT_HOST = "https://spanner.googleapis.com" ;
164+ private static final String DEFAULT_EMULATOR_HOST = "http://localhost:9010" ;
164165 /** Use plain text is only for local testing purposes. */
165166 private static final String USE_PLAIN_TEXT_PROPERTY_NAME = "usePlainText" ;
166167 /** Name of the 'autocommit' connection property. */
@@ -231,6 +232,10 @@ public String[] getValidValues() {
231232 OPTIMIZER_VERSION_PROPERTY_NAME ,
232233 "Sets the default query optimizer version to use for this connection." ),
233234 ConnectionProperty .createBooleanProperty ("returnCommitStats" , "" , false ),
235+ ConnectionProperty .createBooleanProperty (
236+ "autoConfigEmulator" ,
237+ "Automatically configure the connection to try to connect to the Cloud Spanner emulator (true/false). The instance and database in the connection string will automatically be created if these do not yet exist on the emulator." ,
238+ false ),
234239 ConnectionProperty .createBooleanProperty (
235240 LENIENT_PROPERTY_NAME ,
236241 "Silently ignore unknown properties in the connection string/properties (true/false)" ,
@@ -347,6 +352,14 @@ private boolean isValidUri(String uri) {
347352 * <li>retryAbortsInternally (boolean): Sets the initial retryAbortsInternally mode for the
348353 * connection. Default is true.
349354 * <li>optimizerVersion (string): Sets the query optimizer version to use for the connection.
355+ * <li>autoConfigEmulator (boolean): Automatically configures the connection to connect to the
356+ * Cloud Spanner emulator. If no host and port is specified in the connection string, the
357+ * connection will automatically use the default emulator host/port combination
358+ * (localhost:9010). Plain text communication will be enabled and authentication will be
359+ * disabled. The instance and database in the connection string will automatically be
360+ * created on the emulator if any of them do not yet exist. Any existing instance or
361+ * database on the emulator will remain untouched. No other configuration is needed in
362+ * order to connect to the emulator than setting this property.
350363 * </ul>
351364 *
352365 * @param uri The URI of the Spanner database to connect to.
@@ -459,6 +472,7 @@ public static Builder newBuilder() {
459472 private final String userAgent ;
460473 private final QueryOptions queryOptions ;
461474 private final boolean returnCommitStats ;
475+ private final boolean autoConfigEmulator ;
462476
463477 private final boolean autocommit ;
464478 private final boolean readOnly ;
@@ -483,18 +497,15 @@ private ConnectionOptions(Builder builder) {
483497 (builder .credentials == null && this .credentialsUrl == null ) || this .oauthToken == null ,
484498 "Cannot specify both credentials and an OAuth token." );
485499
486- this .usePlainText = parseUsePlainText (this .uri );
487500 this .userAgent = parseUserAgent (this .uri );
488501 QueryOptions .Builder queryOptionsBuilder = QueryOptions .newBuilder ();
489502 queryOptionsBuilder .setOptimizerVersion (parseOptimizerVersion (this .uri ));
490503 this .queryOptions = queryOptionsBuilder .build ();
491504 this .returnCommitStats = parseReturnCommitStats (this .uri );
505+ this .autoConfigEmulator = parseAutoConfigEmulator (this .uri );
506+ this .usePlainText = this .autoConfigEmulator || parseUsePlainText (this .uri );
507+ this .host = determineHost (matcher , autoConfigEmulator , usePlainText );
492508
493- this .host =
494- matcher .group (Builder .HOST_GROUP ) == null
495- ? DEFAULT_HOST
496- : (usePlainText ? PLAIN_TEXT_PROTOCOL : HOST_PROTOCOL )
497- + matcher .group (Builder .HOST_GROUP );
498509 this .instanceId = matcher .group (Builder .INSTANCE_GROUP );
499510 this .databaseName = matcher .group (Builder .DATABASE_GROUP );
500511 // Using credentials on a plain text connection is not allowed, so if the user has not specified
@@ -549,6 +560,23 @@ private ConnectionOptions(Builder builder) {
549560 }
550561 }
551562
563+ private static String determineHost (
564+ Matcher matcher , boolean autoConfigEmulator , boolean usePlainText ) {
565+ if (matcher .group (Builder .HOST_GROUP ) == null ) {
566+ if (autoConfigEmulator ) {
567+ return DEFAULT_EMULATOR_HOST ;
568+ } else {
569+ return DEFAULT_HOST ;
570+ }
571+ } else {
572+ if (usePlainText ) {
573+ return PLAIN_TEXT_PROTOCOL + matcher .group (Builder .HOST_GROUP );
574+ } else {
575+ return HOST_PROTOCOL + matcher .group (Builder .HOST_GROUP );
576+ }
577+ }
578+ }
579+
552580 private static Integer parseIntegerProperty (String propertyName , String value ) {
553581 if (value != null ) {
554582 try {
@@ -644,6 +672,11 @@ static boolean parseReturnCommitStats(String uri) {
644672 return value != null ? Boolean .valueOf (value ) : false ;
645673 }
646674
675+ static boolean parseAutoConfigEmulator (String uri ) {
676+ String value = parseUriProperty (uri , "autoConfigEmulator" );
677+ return value != null ? Boolean .valueOf (value ) : false ;
678+ }
679+
647680 @ VisibleForTesting
648681 static boolean parseLenient (String uri ) {
649682 String value = parseUriProperty (uri , LENIENT_PROPERTY_NAME );
@@ -838,6 +871,16 @@ public boolean isReturnCommitStats() {
838871 return returnCommitStats ;
839872 }
840873
874+ /**
875+ * Whether connections created by this {@link ConnectionOptions} will automatically try to connect
876+ * to the emulator using the default host/port of the emulator, and automatically create the
877+ * instance and database that is specified in the connection string if these do not exist on the
878+ * emulator instance.
879+ */
880+ public boolean isAutoConfigEmulator () {
881+ return autoConfigEmulator ;
882+ }
883+
841884 /** Interceptors that should be executed after each statement */
842885 List <StatementExecutionInterceptor > getStatementExecutionInterceptors () {
843886 return statementExecutionInterceptors ;
0 commit comments