• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright Amazon.com, Inc. or its affiliates. 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  * A copy of the License is located at
7  *
8  *  http://aws.amazon.com/apache2.0
9  *
10  * or in the "license" file accompanying this file. This file is distributed
11  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12  * express or implied. See the License for the specific language governing
13  * permissions and limitations under the License.
14  */
15 
16 package software.amazon.awssdk.profiles;
17 
18 import java.nio.file.Path;
19 import java.util.Collections;
20 import java.util.LinkedHashMap;
21 import java.util.Map;
22 import java.util.Objects;
23 import java.util.Optional;
24 import java.util.concurrent.atomic.AtomicReference;
25 import java.util.function.Supplier;
26 import software.amazon.awssdk.annotations.SdkPublicApi;
27 import software.amazon.awssdk.profiles.internal.ProfileFileRefresher;
28 
29 /**
30  * Encapsulates the logic for supplying either a single or multiple ProfileFile instances.
31  * <p>
32  * Each call to the {@link #get()} method will result in either a new or previously supplied profile based on the
33  * implementation's rules.
34  */
35 @SdkPublicApi
36 @FunctionalInterface
37 public interface ProfileFileSupplier extends Supplier<ProfileFile> {
38 
39     /**
40      * Creates a {@link ProfileFileSupplier} capable of producing multiple profile objects by aggregating the default
41      * credentials and configuration files as determined by {@link ProfileFileLocation#credentialsFileLocation()} abd
42      * {@link ProfileFileLocation#configurationFileLocation()}. This supplier will return a new ProfileFile instance only once
43      * either disk file has been modified. Multiple calls to the supplier while both disk files are unchanged will return the
44      * same object.
45      *
46      * @return Implementation of {@link ProfileFileSupplier} that is capable of supplying a new aggregate profile when either file
47      *         has been modified.
48      */
defaultSupplier()49     static ProfileFileSupplier defaultSupplier() {
50         Optional<ProfileFileSupplier> credentialsSupplierOptional
51             = ProfileFileLocation.credentialsFileLocation()
52                                  .map(path -> reloadWhenModified(path, ProfileFile.Type.CREDENTIALS));
53 
54         Optional<ProfileFileSupplier> configurationSupplierOptional
55             = ProfileFileLocation.configurationFileLocation()
56                                  .map(path -> reloadWhenModified(path, ProfileFile.Type.CONFIGURATION));
57 
58         ProfileFileSupplier supplier = () -> ProfileFile.builder().build();
59         if (credentialsSupplierOptional.isPresent() && configurationSupplierOptional.isPresent()) {
60             supplier = aggregate(credentialsSupplierOptional.get(), configurationSupplierOptional.get());
61         } else if (credentialsSupplierOptional.isPresent()) {
62             supplier = credentialsSupplierOptional.get();
63         } else if (configurationSupplierOptional.isPresent()) {
64             supplier = configurationSupplierOptional.get();
65         }
66 
67         return supplier;
68     }
69 
70     /**
71      * Creates a {@link ProfileFileSupplier} capable of producing multiple profile objects from a file. This supplier will
72      * return a new ProfileFile instance only once the disk file has been modified. Multiple calls to the supplier while the
73      * disk file is unchanged will return the same object.
74      *
75      * @param path Path to the file to read from.
76      * @param type The type of file. See {@link ProfileFile.Type} for possible values.
77      * @return Implementation of {@link ProfileFileSupplier} that is capable of supplying a new profile when the file
78      *         has been modified.
79      */
reloadWhenModified(Path path, ProfileFile.Type type)80     static ProfileFileSupplier reloadWhenModified(Path path, ProfileFile.Type type) {
81         return new ProfileFileSupplier() {
82 
83             final ProfileFile.Builder builder = ProfileFile.builder()
84                                                            .content(path)
85                                                            .type(type);
86 
87             final ProfileFileRefresher refresher = ProfileFileRefresher.builder()
88                                                                        .profileFile(builder::build)
89                                                                        .profileFilePath(path)
90                                                                        .build();
91 
92             @Override
93             public ProfileFile get() {
94                 return refresher.refreshIfStale();
95             }
96 
97         };
98     }
99 
100     /**
101      * Creates a {@link ProfileFileSupplier} that produces an existing profile.
102      *
103      * @param profileFile Profile object to supply.
104      * @return Implementation of {@link ProfileFileSupplier} that is capable of supplying a single profile.
105      */
fixedProfileFile(ProfileFile profileFile)106     static ProfileFileSupplier fixedProfileFile(ProfileFile profileFile) {
107         return () -> profileFile;
108     }
109 
110     /**
111      * Creates a {@link ProfileFileSupplier} by combining the {@link ProfileFile} objects from multiple {@code
112      * ProfileFileSupplier}s. Objects are passed into {@link ProfileFile.Aggregator}.
113      *
114      * @param suppliers Array of {@code ProfileFileSupplier} objects. {@code ProfileFile} objects are passed to
115      *                  {@link ProfileFile.Aggregator#addFile(ProfileFile)} in the same argument order as the supplier that
116      *                  generated it.
117      * @return Implementation of {@link ProfileFileSupplier} aggregating results from the supplier objects.
118      */
aggregate(ProfileFileSupplier... suppliers)119     static ProfileFileSupplier aggregate(ProfileFileSupplier... suppliers) {
120 
121         return new ProfileFileSupplier() {
122 
123             final AtomicReference<ProfileFile> currentAggregateProfileFile = new AtomicReference<>();
124             final Map<Supplier<ProfileFile>, ProfileFile> currentValuesBySupplier
125                 = Collections.synchronizedMap(new LinkedHashMap<>());
126 
127             @Override
128             public ProfileFile get() {
129                 boolean refreshAggregate = false;
130                 for (ProfileFileSupplier supplier : suppliers) {
131                     if (didSuppliedValueChange(supplier)) {
132                         refreshAggregate = true;
133                     }
134                 }
135 
136                 if (refreshAggregate) {
137                     refreshCurrentAggregate();
138                 }
139 
140                 return  currentAggregateProfileFile.get();
141             }
142 
143             private boolean didSuppliedValueChange(Supplier<ProfileFile> supplier) {
144                 ProfileFile next = supplier.get();
145                 ProfileFile current = currentValuesBySupplier.put(supplier, next);
146 
147                 return !Objects.equals(next, current);
148             }
149 
150             private void refreshCurrentAggregate() {
151                 ProfileFile.Aggregator aggregator = ProfileFile.aggregator();
152                 currentValuesBySupplier.values().forEach(aggregator::addFile);
153                 ProfileFile current = currentAggregateProfileFile.get();
154                 ProfileFile next = aggregator.build();
155                 if (!Objects.equals(current, next)) {
156                     currentAggregateProfileFile.compareAndSet(current, next);
157                 }
158             }
159 
160         };
161     }
162 
163 }
164