Skip to content

Commit 343f77d

Browse files
jean-philippe-martinmziccard
authored andcommitted
Support enumeration of filesystems when credentials are not available (#1189)
1 parent 1d85f47 commit 343f77d

8 files changed

Lines changed: 117 additions & 16 deletions

File tree

gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,11 @@
8585
@AutoService(FileSystemProvider.class)
8686
public final class CloudStorageFileSystemProvider extends FileSystemProvider {
8787

88-
private final Storage storage;
88+
private Storage storage;
89+
private StorageOptions storageOptions;
8990

9091
// used only when we create a new instance of CloudStorageFileSystemProvider.
91-
private static StorageOptions storageOptions;
92+
private static StorageOptions futureStorageOptions;
9293

9394
private static class LazyPathIterator extends AbstractIterator<Path> {
9495
private final Iterator<Blob> blobIterator;
@@ -122,8 +123,8 @@ protected Path computeNext() {
122123
* Sets options that are only used by the constructor.
123124
*/
124125
@VisibleForTesting
125-
public static void setGCloudOptions(StorageOptions newStorageOptions) {
126-
storageOptions = newStorageOptions;
126+
public static void setStorageOptions(StorageOptions newStorageOptions) {
127+
futureStorageOptions = newStorageOptions;
127128
}
128129

129130
/**
@@ -133,14 +134,24 @@ public static void setGCloudOptions(StorageOptions newStorageOptions) {
133134
* @see CloudStorageFileSystem#forBucket(String)
134135
*/
135136
public CloudStorageFileSystemProvider() {
136-
this(storageOptions);
137+
this(futureStorageOptions);
137138
}
138139

139140
CloudStorageFileSystemProvider(@Nullable StorageOptions gcsStorageOptions) {
140-
if (gcsStorageOptions == null) {
141+
this.storageOptions = gcsStorageOptions;
142+
143+
}
144+
145+
// Initialize this.storage, once. This may throw an exception if default authentication
146+
// credentials are not available (hence not doing it in the ctor).
147+
private void initStorage() {
148+
if (this.storage != null) {
149+
return;
150+
}
151+
if (storageOptions == null) {
141152
this.storage = StorageOptions.defaultInstance().service();
142153
} else {
143-
this.storage = gcsStorageOptions.service();
154+
this.storage = storageOptions.service();
144155
}
145156
}
146157

@@ -154,6 +165,7 @@ public String getScheme() {
154165
*/
155166
@Override
156167
public CloudStorageFileSystem getFileSystem(URI uri) {
168+
initStorage();
157169
return newFileSystem(uri, Collections.<String, Object>emptyMap());
158170
}
159171

@@ -186,11 +198,13 @@ && isNullOrEmpty(uri.getUserInfo()),
186198
"GCS FileSystem URIs mustn't have: port, userinfo, path, query, or fragment: %s",
187199
uri);
188200
CloudStorageUtil.checkBucket(uri.getHost());
201+
initStorage();
189202
return new CloudStorageFileSystem(this, uri.getHost(), CloudStorageConfiguration.fromMap(env));
190203
}
191204

192205
@Override
193206
public CloudStoragePath getPath(URI uri) {
207+
initStorage();
194208
return CloudStoragePath.getPath(
195209
getFileSystem(CloudStorageUtil.stripPathFromUri(uri)), uri.getPath());
196210
}
@@ -199,6 +213,7 @@ public CloudStoragePath getPath(URI uri) {
199213
public SeekableByteChannel newByteChannel(
200214
Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
201215
checkNotNull(path);
216+
initStorage();
202217
CloudStorageUtil.checkNotNullArray(attrs);
203218
if (options.contains(StandardOpenOption.WRITE)) {
204219
// TODO: Make our OpenOptions implement FileAttribute. Also remove buffer option.
@@ -210,6 +225,7 @@ public SeekableByteChannel newByteChannel(
210225

211226
private SeekableByteChannel newReadChannel(Path path, Set<? extends OpenOption> options)
212227
throws IOException {
228+
initStorage();
213229
for (OpenOption option : options) {
214230
if (option instanceof StandardOpenOption) {
215231
switch ((StandardOpenOption) option) {
@@ -244,7 +260,7 @@ private SeekableByteChannel newReadChannel(Path path, Set<? extends OpenOption>
244260

245261
private SeekableByteChannel newWriteChannel(Path path, Set<? extends OpenOption> options)
246262
throws IOException {
247-
263+
initStorage();
248264
CloudStoragePath cloudPath = CloudStorageUtil.checkPath(path);
249265
if (cloudPath.seemsLikeADirectoryAndUsePseudoDirectories()) {
250266
throw new CloudStoragePseudoDirectoryException(cloudPath);
@@ -318,6 +334,7 @@ private SeekableByteChannel newWriteChannel(Path path, Set<? extends OpenOption>
318334

319335
@Override
320336
public InputStream newInputStream(Path path, OpenOption... options) throws IOException {
337+
initStorage();
321338
InputStream result = super.newInputStream(path, options);
322339
CloudStoragePath cloudPath = CloudStorageUtil.checkPath(path);
323340
int blockSize = cloudPath.getFileSystem().config().blockSize();
@@ -331,6 +348,7 @@ public InputStream newInputStream(Path path, OpenOption... options) throws IOExc
331348

332349
@Override
333350
public boolean deleteIfExists(Path path) throws IOException {
351+
initStorage();
334352
CloudStoragePath cloudPath = CloudStorageUtil.checkPath(path);
335353
if (cloudPath.seemsLikeADirectoryAndUsePseudoDirectories()) {
336354
throw new CloudStoragePseudoDirectoryException(cloudPath);
@@ -340,6 +358,7 @@ public boolean deleteIfExists(Path path) throws IOException {
340358

341359
@Override
342360
public void delete(Path path) throws IOException {
361+
initStorage();
343362
CloudStoragePath cloudPath = CloudStorageUtil.checkPath(path);
344363
if (!deleteIfExists(cloudPath)) {
345364
throw new NoSuchFileException(cloudPath.toString());
@@ -348,6 +367,7 @@ public void delete(Path path) throws IOException {
348367

349368
@Override
350369
public void move(Path source, Path target, CopyOption... options) throws IOException {
370+
initStorage();
351371
for (CopyOption option : options) {
352372
if (option == StandardCopyOption.ATOMIC_MOVE) {
353373
throw new AtomicMoveNotSupportedException(
@@ -362,6 +382,7 @@ public void move(Path source, Path target, CopyOption... options) throws IOExcep
362382

363383
@Override
364384
public void copy(Path source, Path target, CopyOption... options) throws IOException {
385+
initStorage();
365386
boolean wantCopyAttributes = false;
366387
boolean wantReplaceExisting = false;
367388
boolean setContentType = false;
@@ -492,6 +513,7 @@ public boolean isHidden(Path path) {
492513

493514
@Override
494515
public void checkAccess(Path path, AccessMode... modes) throws IOException {
516+
initStorage();
495517
for (AccessMode mode : modes) {
496518
switch (mode) {
497519
case READ:
@@ -520,6 +542,7 @@ public <A extends BasicFileAttributes> A readAttributes(
520542
if (type != CloudStorageFileAttributes.class && type != BasicFileAttributes.class) {
521543
throw new UnsupportedOperationException(type.getSimpleName());
522544
}
545+
initStorage();
523546
CloudStoragePath cloudPath = CloudStorageUtil.checkPath(path);
524547
if (cloudPath.seemsLikeADirectoryAndUsePseudoDirectories()) {
525548
@SuppressWarnings("unchecked")
@@ -574,6 +597,7 @@ public void createDirectory(Path dir, FileAttribute<?>... attrs) {
574597
public DirectoryStream<Path> newDirectoryStream(Path dir, final Filter<? super Path> filter) {
575598
final CloudStoragePath cloudPath = CloudStorageUtil.checkPath(dir);
576599
checkNotNull(filter);
600+
initStorage();
577601
String prefix = cloudPath.toString();
578602
final Iterator<Blob> blobIterator = storage.list(cloudPath.bucket(),
579603
Storage.BlobListOption.prefix(prefix), Storage.BlobListOption.currentDirectory(),
@@ -621,6 +645,7 @@ public int hashCode() {
621645

622646
@Override
623647
public String toString() {
648+
initStorage();
624649
return MoreObjects.toStringHelper(this).add("storage", storage).toString();
625650
}
626651

gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public class CloudStorageFileAttributeViewTest {
5151

5252
@Before
5353
public void before() {
54-
CloudStorageFileSystemProvider.setGCloudOptions(LocalStorageHelper.options());
54+
CloudStorageFileSystemProvider.setStorageOptions(LocalStorageHelper.options());
5555
path = Paths.get(URI.create("gs://red/water"));
5656
}
5757

gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributesTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public class CloudStorageFileAttributesTest {
4747

4848
@Before
4949
public void before() {
50-
CloudStorageFileSystemProvider.setGCloudOptions(LocalStorageHelper.options());
50+
CloudStorageFileSystemProvider.setStorageOptions(LocalStorageHelper.options());
5151
path = Paths.get(URI.create("gs://bucket/randompath"));
5252
dir = Paths.get(URI.create("gs://bucket/randompath/"));
5353
}

gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public class CloudStorageFileSystemProviderTest {
8989

9090
@Before
9191
public void before() {
92-
CloudStorageFileSystemProvider.setGCloudOptions(LocalStorageHelper.options());
92+
CloudStorageFileSystemProvider.setStorageOptions(LocalStorageHelper.options());
9393
}
9494

9595
@Test
@@ -628,7 +628,7 @@ public void testNullness() throws IOException, NoSuchMethodException, SecurityEx
628628
tester.setDefault(Path.class, fs.getPath("and/one"));
629629
tester.setDefault(OpenOption.class, CREATE);
630630
tester.setDefault(CopyOption.class, COPY_ATTRIBUTES);
631-
// can't do that, setGCloudOptions accepts a null argument.
631+
// can't do that, setStorageOptions accepts a null argument.
632632
// TODO(jart): Figure out how to re-enable this.
633633
// tester.testAllPublicStaticMethods(CloudStorageFileSystemProvider.class);
634634
tester.testAllPublicInstanceMethods(new CloudStorageFileSystemProvider());
@@ -651,4 +651,4 @@ private static CloudStorageConfiguration permitEmptyPathComponents(boolean value
651651
private static CloudStorageConfiguration usePseudoDirectories(boolean value) {
652652
return CloudStorageConfiguration.builder().usePseudoDirectories(value).build();
653653
}
654-
}
654+
}

gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public class CloudStorageFileSystemTest {
5656

5757
@Before
5858
public void before() {
59-
CloudStorageFileSystemProvider.setGCloudOptions(LocalStorageHelper.options());
59+
CloudStorageFileSystemProvider.setStorageOptions(LocalStorageHelper.options());
6060
}
6161

6262
@Test
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright 2016 Google Inc. All Rights Reserved.
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.google.cloud.storage.contrib.nio;
18+
19+
import com.google.cloud.storage.Storage;
20+
import com.google.cloud.storage.StorageOptions;
21+
import org.junit.Before;
22+
import org.junit.Rule;
23+
import org.junit.Test;
24+
import org.junit.rules.ExpectedException;
25+
import org.junit.runner.RunWith;
26+
import org.junit.runners.JUnit4;
27+
28+
import java.net.URI;
29+
30+
import static org.mockito.Mockito.mock;
31+
import static org.mockito.Mockito.never;
32+
import static org.mockito.Mockito.times;
33+
import static org.mockito.Mockito.verify;
34+
import static org.mockito.Mockito.when;
35+
36+
/**
37+
* Unit tests for {@link CloudStorageFileSystemProvider} late initialization.
38+
*/
39+
@RunWith(JUnit4.class)
40+
public class CloudStorageLateInitializationTest {
41+
42+
@Rule public final ExpectedException thrown = ExpectedException.none();
43+
44+
private StorageOptions mockOptions;
45+
46+
@Before
47+
public void before() {
48+
mockOptions = mock(StorageOptions.class);
49+
Storage mockStorage = mock(Storage.class);
50+
when(mockOptions.service()).thenReturn(mockStorage);
51+
CloudStorageFileSystemProvider.setStorageOptions(mockOptions);
52+
}
53+
54+
@Test
55+
public void ctorDoesNotCreateStorage() {
56+
new CloudStorageFileSystemProvider();
57+
verify(mockOptions, never()).service();
58+
}
59+
60+
@Test
61+
public void getPathCreatesStorageOnce() {
62+
CloudStorageFileSystemProvider provider = new CloudStorageFileSystemProvider();
63+
provider.getPath(URI.create("gs://bucket1/wat"));
64+
provider.getPath(URI.create("gs://bucket2/wat"));
65+
verify(mockOptions, times(1)).service();
66+
}
67+
68+
@Test
69+
public void getFileSystemCreatesStorageOnce() {
70+
CloudStorageFileSystemProvider provider = new CloudStorageFileSystemProvider();
71+
provider.getFileSystem(URI.create("gs://bucket1"));
72+
provider.getFileSystem(URI.create("gs://bucket2"));
73+
verify(mockOptions, times(1)).service();
74+
}
75+
76+
}

gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageOptionsTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public class CloudStorageOptionsTest {
4141

4242
@Before
4343
public void before() {
44-
CloudStorageFileSystemProvider.setGCloudOptions(LocalStorageHelper.options());
44+
CloudStorageFileSystemProvider.setStorageOptions(LocalStorageHelper.options());
4545
}
4646

4747
@Test

gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStoragePathTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public class CloudStoragePathTest {
4646

4747
@Before
4848
public void before() {
49-
CloudStorageFileSystemProvider.setGCloudOptions(LocalStorageHelper.options());
49+
CloudStorageFileSystemProvider.setStorageOptions(LocalStorageHelper.options());
5050
}
5151

5252
@Test

0 commit comments

Comments
 (0)