Skip to content
This repository was archived by the owner on Apr 7, 2026. It is now read-only.

Commit eb0164e

Browse files
committed
cleaned up tasks + added sample code
1 parent 4c5ec92 commit eb0164e

5 files changed

Lines changed: 104 additions & 8 deletions

File tree

google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerOptions.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ public class SpannerOptions extends ServiceOptions<Spanner, SpannerOptions> {
128128
private static final String GOOGLE_DEFAULT_UNIVERSE = "googleapis.com";
129129
private static final String EXPERIMENTAL_HOST_PROJECT_ID = "default";
130130

131-
private static final ImmutableSet<String> SCOPES =
131+
public static final ImmutableSet<String> SCOPES =
132132
ImmutableSet.of(
133133
"https://www.googleapis.com/auth/spanner.admin",
134134
"https://www.googleapis.com/auth/spanner.data");

google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/MutableCredentials.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public class MutableCredentials extends Credentials {
4646
private final Set<String> scopes;
4747

4848
public MutableCredentials(ServiceAccountCredentials credentials) {
49-
this(credentials, SpannerOptions.getDefaultInstance().getScopes());
49+
this(credentials, SpannerOptions.SCOPES);
5050
}
5151

5252
public MutableCredentials(ServiceAccountCredentials credentials, @Nonnull Set<String> scopes) {

google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/MutableCredentialsTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public void testCreateMutableCredentials() throws IOException {
7979

8080
@Test
8181
public void testCreateMutableCredentialsWithDefaultScopes() throws IOException {
82-
Set<String> defaultScopes = SpannerOptions.getDefaultInstance().getScopes();
82+
Set<String> defaultScopes = SpannerOptions.SCOPES;
8383
when(initialCredentials.createScoped(defaultScopes)).thenReturn(initialScopedCredentials);
8484
when(initialScopedCredentials.getAuthenticationType()).thenReturn(initialAuthType);
8585
when(initialScopedCredentials.getRequestMetadata(any(URI.class))).thenReturn(initialMetadata);

google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITMutableCredentialsTest.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public class ITMutableCredentialsTest {
4343

4444
@Test
4545
public void testMutableCredentialsUpdateAuthorizationForRunningClient() throws IOException {
46-
GoogleCredentials validCredentials;
46+
GoogleCredentials validCredentials = null;
4747

4848
// accept cert path overridden by environment variable for local testing
4949
if (System.getenv("GOOGLE_ACCOUNT_CREDENTIALS") != null) {
@@ -52,7 +52,10 @@ public void testMutableCredentialsUpdateAuthorizationForRunningClient() throws I
5252
validCredentials = GoogleCredentials.fromStream(stream);
5353
}
5454
} else {
55-
validCredentials = GoogleCredentials.getApplicationDefault();
55+
try {
56+
validCredentials = GoogleCredentials.getApplicationDefault();
57+
} catch (IOException e) {
58+
}
5659
}
5760

5861
// credentials must be ServiceAccountCredentials
@@ -68,23 +71,24 @@ public void testMutableCredentialsUpdateAuthorizationForRunningClient() throws I
6871
MutableCredentials mutableCredentials =
6972
new MutableCredentials((ServiceAccountCredentials) validCredentials);
7073

71-
System.out.println("validCredentials " + validCredentials);
72-
7374
SpannerOptions options =
7475
SpannerOptions.newBuilder()
7576
.setEmulatorHost(
7677
null) // this setting is required otherwise SpannerOptions overrides credentials to
7778
// NoCredentials
7879
.setCredentials(mutableCredentials)
7980
.build();
80-
System.out.println("initial credentials " + options.getCredentials());
81+
8182
ProjectName projectName = ProjectName.of(options.getProjectId());
8283
try (Spanner spanner = options.getService();
8384
InstanceAdminClient instanceAdminClient = spanner.createInstanceAdminClient()) {
8485
instanceAdminClient.listInstances(projectName);
86+
8587
// update mutableCredentials now to use an invalid credentials
8688
mutableCredentials.updateCredentials(invalidCredentials);
89+
8790
try {
91+
// this call should now fail with new invalid credentials
8892
instanceAdminClient.listInstances(projectName);
8993
fail("Expected UNAUTHENTICATED after switching to invalid credentials");
9094
} catch (Exception e) {
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Copyright 2026 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example.spanner;
18+
19+
//[START mutable_credentials]
20+
21+
import com.google.auth.oauth2.ServiceAccountCredentials;
22+
import com.google.cloud.spanner.Spanner;
23+
import com.google.cloud.spanner.SpannerOptions;
24+
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
25+
import com.google.cloud.spanner.connection.MutableCredentials;
26+
import com.google.spanner.admin.database.v1.DatabaseName;
27+
import com.google.spanner.admin.database.v1.GetDatabaseDdlResponse;
28+
import java.io.FileInputStream;
29+
import java.io.IOException;
30+
import java.nio.file.Files;
31+
import java.nio.file.Path;
32+
import java.nio.file.Paths;
33+
import java.nio.file.attribute.FileTime;
34+
import java.util.concurrent.Executors;
35+
import java.util.concurrent.ScheduledExecutorService;
36+
import java.util.concurrent.TimeUnit;
37+
38+
public class MutableCredentialsExample {
39+
40+
41+
static void createClientWithMutableCredentials() throws IOException {
42+
final String credentialsPath = "location_of_service_account_credential_json";
43+
Path path = Paths.get(credentialsPath);
44+
// Use an array to hold the mutable lastModifiedTime so it can be accessed in the lambda
45+
FileTime[] lastModifiedTime = new FileTime[]{ Files.getLastModifiedTime(path) };
46+
47+
// 1 - create service account credentials
48+
ServiceAccountCredentials serviceAccountCredentials;
49+
try (FileInputStream is = new FileInputStream(credentialsPath)) {
50+
serviceAccountCredentials = ServiceAccountCredentials.fromStream(is);
51+
}
52+
53+
// 2 - wrap credentials from step 1 in a MutableCredentials instance
54+
MutableCredentials mutableCredentials = new MutableCredentials(serviceAccountCredentials);
55+
56+
// 3 - set credentials on your SpannerOptions builder to your mutableCredentials
57+
SpannerOptions options = SpannerOptions.newBuilder()
58+
.setCredentials(mutableCredentials)
59+
.build();
60+
61+
// 4 - include logic for when to how your mutableCredentials
62+
// In this example we'll use a SchedulerExecutorService to periodically check for updates
63+
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
64+
executorService.scheduleAtFixedRate(() -> {
65+
try {
66+
FileTime currentModifiedTime = Files.getLastModifiedTime(path);
67+
if (currentModifiedTime.compareTo(lastModifiedTime[0]) > 0) {
68+
lastModifiedTime[0] = currentModifiedTime;
69+
try (FileInputStream is = new FileInputStream(credentialsPath)) {
70+
ServiceAccountCredentials credentials = ServiceAccountCredentials.fromStream(is);
71+
mutableCredentials.updateCredentials(credentials);
72+
System.out.println("Credentials rotated.");
73+
}
74+
}
75+
} catch (IOException e) {
76+
System.err.println("Failed to check or update credentials: " + e.getMessage());
77+
}
78+
}, 15, 15, TimeUnit.MINUTES);
79+
80+
// 5. Use the client
81+
try (Spanner spanner = options.getService();
82+
DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) {
83+
// Perform operations...
84+
// long running client operations will always use the latest credentials wrapped in
85+
// mutableCredentials
86+
} finally {
87+
// Ensure the executor is shut down when the application exits or the client is closed
88+
executorService.shutdown();
89+
}
90+
}
91+
}
92+
//[END mutable_credentials]

0 commit comments

Comments
 (0)