1818
1919import static com .google .common .base .Preconditions .checkArgument ;
2020import static com .google .common .base .Preconditions .checkNotNull ;
21+ import static java .util .Objects .requireNonNull ;
2122
2223import com .google .api .gax .paging .Page ;
2324import com .google .cloud .storage .Bucket ;
2425import com .google .cloud .storage .Storage ;
2526import com .google .cloud .storage .StorageException ;
2627import com .google .cloud .storage .StorageOptions ;
2728import com .google .common .base .Strings ;
29+ import com .google .common .cache .CacheBuilder ;
30+ import com .google .common .cache .CacheLoader ;
31+ import com .google .common .cache .LoadingCache ;
2832import com .google .common .collect .ImmutableSet ;
33+ import com .google .common .util .concurrent .UncheckedExecutionException ;
2934import java .io .IOException ;
3035import java .net .URI ;
3136import java .net .URISyntaxException ;
3843import java .nio .file .attribute .FileTime ;
3944import java .nio .file .attribute .UserPrincipalLookupService ;
4045import java .util .HashMap ;
41- import java .util .HashSet ;
42- import java .util .Map ;
4346import java .util .Objects ;
4447import java .util .Set ;
48+ import java .util .concurrent .ExecutionException ;
4549import javax .annotation .CheckReturnValue ;
4650import javax .annotation .Nullable ;
4751import javax .annotation .concurrent .ThreadSafe ;
@@ -66,8 +70,21 @@ public final class CloudStorageFileSystem extends FileSystem {
6670 private final CloudStorageFileSystemProvider provider ;
6771 private final String bucket ;
6872 private final CloudStorageConfiguration config ;
69- private static final Map <CloudStorageConfiguration , Set <CloudStorageFileSystemProvider >>
70- CONFIG_TO_PROVIDERS_MAP = new HashMap <>();
73+ private static final LoadingCache <ProviderCacheKey , CloudStorageFileSystemProvider >
74+ PROVIDER_CACHE_BY_CONFIG =
75+ CacheBuilder .newBuilder ()
76+ .build (
77+ new CacheLoader <ProviderCacheKey , CloudStorageFileSystemProvider >() {
78+ @ Override
79+ public CloudStorageFileSystemProvider load (ProviderCacheKey key ) {
80+ CloudStorageConfiguration config = key .cloudStorageConfiguration ;
81+ StorageOptions storageOptions = key .storageOptions ;
82+ String userProject = config .userProject ();
83+ return (storageOptions == null )
84+ ? new CloudStorageFileSystemProvider (userProject )
85+ : new CloudStorageFileSystemProvider (userProject , storageOptions );
86+ }
87+ });
7188
7289 // Users can change this: then this affects every filesystem object created
7390 // later, including via SPI. This is meant to be done only once, at the beginning
@@ -144,32 +161,7 @@ public static CloudStorageFileSystem forBucket(String bucket) {
144161 */
145162 @ CheckReturnValue
146163 public static CloudStorageFileSystem forBucket (String bucket , CloudStorageConfiguration config ) {
147- checkArgument (
148- !bucket .startsWith (URI_SCHEME + ":" ), "Bucket name must not have schema: %s" , bucket );
149- checkNotNull (config );
150- return new CloudStorageFileSystem (
151- getCloudStorageFileSystemProvider (config , null ), bucket , config );
152- }
153-
154- private static CloudStorageFileSystemProvider getCloudStorageFileSystemProvider (
155- CloudStorageConfiguration config , StorageOptions storageOptions ) {
156- CloudStorageFileSystemProvider newProvider =
157- (storageOptions == null )
158- ? new CloudStorageFileSystemProvider (config .userProject ())
159- : new CloudStorageFileSystemProvider (config .userProject (), storageOptions );
160- Set <CloudStorageFileSystemProvider > existingProviders = CONFIG_TO_PROVIDERS_MAP .get (config );
161- if (existingProviders == null ) {
162- existingProviders = new HashSet <>();
163- } else {
164- for (CloudStorageFileSystemProvider existiningProvider : existingProviders ) {
165- if (existiningProvider .equals (newProvider )) {
166- return existiningProvider ;
167- }
168- }
169- }
170- existingProviders .add (newProvider );
171- CONFIG_TO_PROVIDERS_MAP .put (config , existingProviders );
172- return newProvider ;
164+ return forBucket (bucket , config , null );
173165 }
174166
175167 /**
@@ -192,8 +184,16 @@ public static CloudStorageFileSystem forBucket(
192184 String bucket , CloudStorageConfiguration config , @ Nullable StorageOptions storageOptions ) {
193185 checkArgument (
194186 !bucket .startsWith (URI_SCHEME + ":" ), "Bucket name must not have schema: %s" , bucket );
195- return new CloudStorageFileSystem (
196- getCloudStorageFileSystemProvider (config , storageOptions ), bucket , checkNotNull (config ));
187+ checkNotNull (config );
188+ CloudStorageFileSystemProvider result ;
189+ ProviderCacheKey providerCacheKey = new ProviderCacheKey (config , storageOptions );
190+ try {
191+ result = PROVIDER_CACHE_BY_CONFIG .get (providerCacheKey );
192+ } catch (ExecutionException | UncheckedExecutionException e ) {
193+ throw new IllegalStateException (
194+ "Unable to resolve CloudStorageFileSystemProvider for the provided configuration" , e );
195+ }
196+ return new CloudStorageFileSystem (result , bucket , config );
197197 }
198198
199199 CloudStorageFileSystem (
@@ -335,4 +335,50 @@ public String toString() {
335335 throw new AssertionError (e );
336336 }
337337 }
338+
339+ /**
340+ * In order to cache a {@link CloudStorageFileSystemProvider} we track the config used to
341+ * instantiate that provider. This class creates an immutable key encapsulating the config to
342+ * allow reliable resolution from the cache.
343+ */
344+ private static final class ProviderCacheKey {
345+ private final CloudStorageConfiguration cloudStorageConfiguration ;
346+ @ Nullable private final StorageOptions storageOptions ;
347+
348+ public ProviderCacheKey (
349+ CloudStorageConfiguration cloudStorageConfiguration ,
350+ @ Nullable StorageOptions storageOptions ) {
351+ this .cloudStorageConfiguration =
352+ requireNonNull (cloudStorageConfiguration , "cloudStorageConfiguration must be non null" );
353+ this .storageOptions = storageOptions ;
354+ }
355+
356+ @ Override
357+ public boolean equals (Object o ) {
358+ if (this == o ) {
359+ return true ;
360+ }
361+ if (!(o instanceof ProviderCacheKey )) {
362+ return false ;
363+ }
364+ ProviderCacheKey that = (ProviderCacheKey ) o ;
365+ return cloudStorageConfiguration .equals (that .cloudStorageConfiguration )
366+ && Objects .equals (storageOptions , that .storageOptions );
367+ }
368+
369+ @ Override
370+ public int hashCode () {
371+ return Objects .hash (cloudStorageConfiguration , storageOptions );
372+ }
373+
374+ @ Override
375+ public String toString () {
376+ return "ConfigTuple{"
377+ + "cloudStorageConfiguration="
378+ + cloudStorageConfiguration
379+ + ", storageOptions="
380+ + storageOptions
381+ + '}' ;
382+ }
383+ }
338384}
0 commit comments