@@ -127,7 +127,8 @@ public class SpannerOptions extends ServiceOptions<Spanner, SpannerOptions> {
127127 private static final String API_SHORT_NAME = "Spanner" ;
128128 private static final String SPANNER_SERVICE_NAME = "spanner" ;
129129 private static final String GOOGLE_DEFAULT_UNIVERSE = "googleapis.com" ;
130- private static final String EXPERIMENTAL_HOST_PROJECT_ID = "default" ;
130+ public static final String SPANNER_OMNI_PROJECT_ID = "default" ;
131+ public static final String DEFAULT_SPANNER_OMNI_INSTANCE_ID = "default" ;
131132
132133 static final ImmutableSet <String > SCOPES =
133134 ImmutableSet .of (
@@ -318,6 +319,15 @@ enum TracingFramework {
318319 OPEN_TELEMETRY
319320 }
320321
322+ /**
323+ * Specifies the type of Spanner instance to connect to (CLOUD or OMNI). Setting it to OMNI is
324+ * mandatory when connecting to a Spanner Omni instance.
325+ */
326+ public enum InstanceType {
327+ CLOUD ,
328+ OMNI
329+ }
330+
321331 private static final Object lock = new Object ();
322332
323333 @ GuardedBy ("lock" )
@@ -871,14 +881,20 @@ public static CloseableExecutorProvider createAsyncExecutorProvider(
871881 }
872882
873883 protected SpannerOptions (Builder builder ) {
874- super (SpannerFactory .class , SpannerRpcFactory .class , builder , new SpannerDefaults ());
884+ super (
885+ SpannerFactory .class ,
886+ SpannerRpcFactory .class ,
887+ Builder .prepareBuilder (builder ),
888+ new SpannerDefaults ());
875889 numChannels = builder .numChannels == null ? DEFAULT_CHANNELS : builder .numChannels ;
876890 Preconditions .checkArgument (
877891 numChannels >= 1 && numChannels <= MAX_CHANNELS ,
878892 "Number of channels must fall in the range [1, %s], found: %s" ,
879893 MAX_CHANNELS ,
880894 numChannels );
881-
895+ Preconditions .checkArgument (
896+ builder .instanceType != InstanceType .OMNI || !Strings .isNullOrEmpty (builder .host ),
897+ "Host must be set for connecting to Spanner Omni instances" );
882898 transportChannelExecutorThreadNameFormat = builder .transportChannelExecutorThreadNameFormat ;
883899 channelProvider = builder .channelProvider ;
884900 channelEndpointCacheFactory = builder .channelEndpointCacheFactory ;
@@ -928,16 +944,17 @@ protected SpannerOptions(Builder builder) {
928944
929945 // Dynamic channel pooling is disabled by default.
930946 // It is only enabled when:
931- // 1. enableDynamicChannelPool() was explicitly called (or experimentalHost is set and DCP was
947+ // 1. enableDynamicChannelPool() was explicitly called (or instance is set to OMNI and DCP was
932948 // not explicitly disabled), AND
933949 // 2. grpc-gcp extension is enabled, AND
934950 // 3. numChannels was not explicitly set
935951 boolean dcpRequested =
936952 builder .dynamicChannelPoolEnabled != null
937953 ? builder .dynamicChannelPoolEnabled
938- : builder .experimentalHost != null ;
954+ : builder .instanceType == InstanceType . OMNI ;
939955 if (dcpRequested ) {
940- // DCP was enabled (explicitly or via experimentalHost), but respect numChannels if set
956+ // DCP was enabled (explicitly or via instance type being OMNI), but respect numChannels if
957+ // set
941958 dynamicChannelPoolEnabled = grpcGcpExtensionEnabled && !builder .numChannelsExplicitlySet ;
942959 } else {
943960 // DCP is disabled by default, or was explicitly disabled
@@ -980,14 +997,14 @@ protected SpannerOptions(Builder builder) {
980997 openTelemetry = builder .openTelemetry ;
981998 enableApiTracing = builder .enableApiTracing ;
982999 enableExtendedTracing = builder .enableExtendedTracing ;
983- if (builder .experimentalHost != null ) {
1000+ if (builder .instanceType == InstanceType . OMNI ) {
9841001 enableBuiltInMetrics = false ;
9851002 } else {
9861003 enableBuiltInMetrics = builder .enableBuiltInMetrics ;
9871004 }
988- // Enable location API when experimental host is set , unless explicitly disabled
1005+ // Enable location API when InstanceType is OMNI , unless explicitly disabled
9891006 // via GOOGLE_SPANNER_EXPERIMENTAL_LOCATION_API=false.
990- if (builder .experimentalHost != null ) {
1007+ if (builder .instanceType == InstanceType . OMNI ) {
9911008 String locationApiEnvValue = System .getenv (EXPERIMENTAL_LOCATION_API_ENV_VAR );
9921009 enableLocationApi = locationApiEnvValue == null || Boolean .parseBoolean (locationApiEnvValue );
9931010 } else {
@@ -1091,13 +1108,12 @@ default String getMonitoringHost() {
10911108 return null ;
10921109 }
10931110
1094- default GoogleCredentials getDefaultExperimentalHostCredentials () {
1111+ default GoogleCredentials getDefaultSpannerOmniCredentials () {
10951112 return null ;
10961113 }
10971114 }
10981115
1099- static final String DEFAULT_SPANNER_EXPERIMENTAL_HOST_CREDENTIALS =
1100- "SPANNER_EXPERIMENTAL_HOST_AUTH_TOKEN" ;
1116+ static final String DEFAULT_SPANNER_OMNI_CREDENTIALS = "SPANNER_OMNI_AUTH_TOKEN" ;
11011117
11021118 /**
11031119 * Default implementation of {@link SpannerEnvironment}. Reads all configuration from environment
@@ -1205,14 +1221,33 @@ public String getMonitoringHost() {
12051221 }
12061222
12071223 @ Override
1208- public GoogleCredentials getDefaultExperimentalHostCredentials () {
1209- return getOAuthTokenFromFile (System .getenv (DEFAULT_SPANNER_EXPERIMENTAL_HOST_CREDENTIALS ));
1224+ public GoogleCredentials getDefaultSpannerOmniCredentials () {
1225+ return getOAuthTokenFromFile (System .getenv (DEFAULT_SPANNER_OMNI_CREDENTIALS ));
12101226 }
12111227 }
12121228
12131229 /** Builder for {@link SpannerOptions} instances. */
12141230 public static class Builder
12151231 extends ServiceOptions .Builder <Spanner , SpannerOptions , SpannerOptions .Builder > {
1232+ private static Builder prepareBuilder (Builder builder ) {
1233+ if (builder .instanceType == InstanceType .OMNI ) {
1234+ builder .enableBuiltInMetrics = false ;
1235+ builder .setProjectId (SPANNER_OMNI_PROJECT_ID );
1236+ builder .configureOmniHost ();
1237+ if (builder .sessionPoolOptions == null ) {
1238+ builder .sessionPoolOptions =
1239+ SessionPoolOptions .newBuilder ().setExperimentalHost ().build ();
1240+ } else {
1241+ builder .sessionPoolOptions =
1242+ builder .sessionPoolOptions .toBuilder ().setExperimentalHost ().build ();
1243+ }
1244+ if (builder .credentials == null ) {
1245+ builder .setCredentials (environment .getDefaultSpannerOmniCredentials ());
1246+ }
1247+ }
1248+ return builder ;
1249+ }
1250+
12161251 static final int DEFAULT_PREFETCH_CHUNKS = 4 ;
12171252 static final QueryOptions DEFAULT_QUERY_OPTIONS = QueryOptions .getDefaultInstance ();
12181253 static final DecodeMode DEFAULT_DECODE_MODE = DecodeMode .DIRECT ;
@@ -1283,10 +1318,11 @@ public static class Builder
12831318 private boolean enableLocationApi = SpannerOptions .environment .isEnableLocationApi ();
12841319 private String monitoringHost = SpannerOptions .environment .getMonitoringHost ();
12851320 private SslContext mTLSContext = null ;
1286- private String experimentalHost = null ;
12871321 private boolean usePlainText = false ;
12881322 private TransactionOptions defaultTransactionOptions = TransactionOptions .getDefaultInstance ();
12891323 private RequestOptions .ClientContext clientContext ;
1324+ private InstanceType instanceType = InstanceType .CLOUD ;
1325+ private String host = null ;
12901326 private boolean autoTaggingEnabled = false ;
12911327 private List <String > autoTaggingPackages = Collections .emptyList ();
12921328 private int autoTaggingTracerLimit = 50 ;
@@ -1829,29 +1865,50 @@ public Builder setDecodeMode(DecodeMode decodeMode) {
18291865 return this ;
18301866 }
18311867
1868+ private void configureOmniHost () {
1869+ if (this .instanceType == InstanceType .OMNI
1870+ && !Strings .isNullOrEmpty (this .host )
1871+ && this .usePlainText ) {
1872+ Preconditions .checkArgument (
1873+ !this .host .startsWith ("https:" ),
1874+ "Please remove the 'https:' protocol prefix from the host string when using plain text"
1875+ + " communication" );
1876+ if (!this .host .startsWith ("http" )) {
1877+ this .host = "http://" + this .host ;
1878+ }
1879+ }
1880+ }
1881+
18321882 @ Override
18331883 public Builder setHost (String host ) {
1834- super .setHost (host );
1884+ this .host = host ;
1885+ configureOmniHost ();
1886+ super .setHost (this .host );
18351887 // Setting a host should override any SPANNER_EMULATOR_HOST setting.
18361888 setEmulatorHost (null );
18371889 return this ;
18381890 }
18391891
1892+ /**
1893+ * @deprecated Use {@link #setType(InstanceType)} instead.
1894+ */
1895+ @ Deprecated
1896+ @ ObsoleteApi ("Use setHost(String).setType(InstanceType.OMNI) instead" )
18401897 @ ExperimentalApi ("https://github.com/googleapis/java-spanner/pull/3676" )
18411898 public Builder setExperimentalHost (String host ) {
1842- if (this .usePlainText ) {
1843- Preconditions .checkArgument (
1844- !host .startsWith ("https:" ),
1845- "Please remove the 'https:' protocol prefix from the host string when using plain text"
1846- + " communication" );
1847- if (!host .startsWith ("http" )) {
1848- host = "http://" + host ;
1849- }
1899+ if (!Strings .isNullOrEmpty (host )) {
1900+ setType (InstanceType .OMNI );
18501901 }
1851- super .setHost (host );
1852- super .setProjectId (EXPERIMENTAL_HOST_PROJECT_ID );
1853- setSessionPoolOption (SessionPoolOptions .newBuilder ().setExperimentalHost ().build ());
1854- this .experimentalHost = host ;
1902+ setHost (host );
1903+ return this ;
1904+ }
1905+
1906+ /**
1907+ * Specifies the type of Spanner instance to connect to (CLOUD or OMNI). Setting it to OMNI is
1908+ * mandatory when connecting to a Spanner Omni instance.
1909+ */
1910+ public Builder setType (InstanceType instanceType ) {
1911+ this .instanceType = instanceType ;
18551912 return this ;
18561913 }
18571914
@@ -1953,14 +2010,13 @@ public Builder setEmulatorHost(String emulatorHost) {
19532010 }
19542011
19552012 /**
1956- * Configures mTLS authentication using the provided client certificate and key files. mTLS is
1957- * only supported for experimental spanner hosts .
2013+ * Configures mTLS authentication using the provided client certificate and key files. mTLS via
2014+ * useClientCert is only supported for Spanner Omni instances .
19582015 *
19592016 * @param clientCertificate Path to the client certificate file.
19602017 * @param clientCertificateKey Path to the client private key file.
19612018 * @throws SpannerException If an error occurs while configuring the mTLS context
19622019 */
1963- @ ExperimentalApi ("https://github.com/googleapis/java-spanner/pull/3574" )
19642020 public Builder useClientCert (String clientCertificate , String clientCertificateKey ) {
19652021 try {
19662022 this .mTLSContext =
@@ -1978,14 +2034,14 @@ public Builder useClientCert(String clientCertificate, String clientCertificateK
19782034 * credentials to {@link com.google.cloud.NoCredentials} to avoid sending authentication over an
19792035 * unsecured channel.
19802036 */
1981- @ ExperimentalApi ("https://github.com/googleapis/java-spanner/pull/4264" )
19822037 public Builder usePlainText () {
19832038 this .usePlainText = true ;
19842039 this .setChannelConfigurator (ManagedChannelBuilder ::usePlaintext )
19852040 .setCredentials (NoCredentials .getInstance ());
1986- if (this .experimentalHost != null ) {
2041+ if (this .instanceType == InstanceType . OMNI ) {
19872042 // Re-apply host settings to ensure http:// is prepended.
1988- setExperimentalHost (this .experimentalHost );
2043+ configureOmniHost ();
2044+ super .setHost (this .host );
19892045 }
19902046 return this ;
19912047 }
@@ -2191,7 +2247,7 @@ public Builder setAutoTaggingTracerLimit(int autoTaggingTracerLimit) {
21912247 @ Override
21922248 public SpannerOptions build () {
21932249 // Set the host of emulator has been set.
2194- if (emulatorHost != null && experimentalHost == null ) {
2250+ if (emulatorHost != null && this . instanceType != InstanceType . OMNI ) {
21952251 if (!emulatorHost .startsWith ("http" )) {
21962252 emulatorHost = "http://" + emulatorHost ;
21972253 }
@@ -2201,8 +2257,6 @@ public SpannerOptions build() {
22012257 this .setChannelConfigurator (ManagedChannelBuilder ::usePlaintext );
22022258 // As we are using plain text, we should never send any credentials.
22032259 this .setCredentials (NoCredentials .getInstance ());
2204- } else if (experimentalHost != null && credentials == null ) {
2205- credentials = environment .getDefaultExperimentalHostCredentials ();
22062260 }
22072261 if (this .numChannels == null ) {
22082262 this .numChannels =
@@ -2244,8 +2298,8 @@ public static void useDefaultEnvironment() {
22442298 }
22452299
22462300 @ InternalApi
2247- public static GoogleCredentials getDefaultExperimentalCredentialsFromSysEnv () {
2248- return getOAuthTokenFromFile (System .getenv (DEFAULT_SPANNER_EXPERIMENTAL_HOST_CREDENTIALS ));
2301+ public static GoogleCredentials getDefaultSpannerOmniCredentialsFromSysEnv () {
2302+ return getOAuthTokenFromFile (System .getenv (DEFAULT_SPANNER_OMNI_CREDENTIALS ));
22492303 }
22502304
22512305 private static @ Nullable GoogleCredentials getOAuthTokenFromFile (@ Nullable String file ) {
0 commit comments