Skip to content

Commit 6694ff8

Browse files
committed
Move aggretate profile supplier logic to a dedicated (internal) class
1 parent 77e64f2 commit 6694ff8

File tree

2 files changed

+102
-44
lines changed

2 files changed

+102
-44
lines changed

core/profiles/src/main/java/software/amazon/awssdk/profiles/ProfileFileSupplier.java

Lines changed: 2 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,11 @@
1717

1818
import java.nio.file.Files;
1919
import java.nio.file.Path;
20-
import java.util.Collections;
21-
import java.util.LinkedHashMap;
22-
import java.util.Map;
23-
import java.util.Objects;
2420
import java.util.Optional;
25-
import java.util.concurrent.atomic.AtomicReference;
2621
import java.util.function.Supplier;
2722
import software.amazon.awssdk.annotations.SdkPublicApi;
23+
import software.amazon.awssdk.profiles.internal.AggregateProfileFileSupplier;
2824
import software.amazon.awssdk.profiles.internal.ProfileFileRefresher;
29-
import software.amazon.awssdk.utils.Pair;
3025

3126
/**
3227
* Encapsulates the logic for supplying either a single or multiple ProfileFile instances.
@@ -126,44 +121,7 @@ static ProfileFileSupplier fixedProfileFile(ProfileFile profileFile) {
126121
*/
127122
static ProfileFileSupplier aggregate(ProfileFileSupplier... suppliers) {
128123

129-
return new ProfileFileSupplier() {
130-
131-
final AtomicReference<Pair<Map<Supplier<ProfileFile>, ProfileFile>, ProfileFile>> state =
132-
new AtomicReference<>(Pair.of(Collections.emptyMap(), ProfileFile.empty()));
133-
134-
@Override
135-
public ProfileFile get() {
136-
while(true) {
137-
Pair<Map<Supplier<ProfileFile>, ProfileFile>, ProfileFile> currentState = state.get();
138-
Map<Supplier<ProfileFile>, ProfileFile> nextValues = new LinkedHashMap<>(currentState.left());
139-
140-
boolean refreshAggregate = false;
141-
142-
for (ProfileFileSupplier supplier : suppliers) {
143-
ProfileFile next = supplier.get();
144-
ProfileFile prev = nextValues.put(supplier, next);
145-
// we ONLY care about if the reference has changed, we don't care about object equality here
146-
if (prev != next) {
147-
refreshAggregate = true;
148-
}
149-
}
150-
151-
if (!refreshAggregate) {
152-
return currentState.right();
153-
}
154-
155-
ProfileFile.Aggregator aggregator = ProfileFile.aggregator();
156-
nextValues.values().forEach(aggregator::addFile);
157-
ProfileFile nextAggregate = aggregator.build();
158-
159-
Pair<Map<Supplier<ProfileFile>, ProfileFile>, ProfileFile> nextState = Pair.of(nextValues, nextAggregate);
160-
if (state.compareAndSet(currentState, nextState)) {
161-
return nextAggregate;
162-
}
163-
// else: another thread has modified the state in between, retry with the fresh state
164-
}
165-
}
166-
};
124+
return new AggregateProfileFileSupplier(suppliers);
167125
}
168126

169127
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
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.internal;
17+
18+
import java.util.Arrays;
19+
import java.util.Collections;
20+
import java.util.LinkedHashMap;
21+
import java.util.List;
22+
import java.util.Map;
23+
import java.util.concurrent.atomic.AtomicReference;
24+
import java.util.function.Supplier;
25+
import software.amazon.awssdk.annotations.SdkInternalApi;
26+
import software.amazon.awssdk.profiles.ProfileFile;
27+
import software.amazon.awssdk.profiles.ProfileFileSupplier;
28+
29+
/**
30+
* A {@link ProfileFileSupplier} that combines the {@link ProfileFile} objects from multiple
31+
* {@code ProfileFileSupplier}s. Objects are passed into {@link ProfileFile.Aggregator}.
32+
*/
33+
@SdkInternalApi
34+
public class AggregateProfileFileSupplier implements ProfileFileSupplier {
35+
final List<ProfileFileSupplier> suppliers;
36+
37+
// supplier values and the resulting aggregate must always be updated atomically together
38+
final AtomicReference<SupplierState> state =
39+
new AtomicReference<>(new SupplierState(Collections.emptyMap(), null));
40+
41+
public AggregateProfileFileSupplier(ProfileFileSupplier... suppliers) {
42+
this.suppliers = Collections.unmodifiableList(Arrays.asList(suppliers));
43+
}
44+
45+
@Override
46+
public ProfileFile get() {
47+
SupplierState currentState = state.get();
48+
Map<Supplier<ProfileFile>, ProfileFile> currentValues = currentState.values;
49+
Map<Supplier<ProfileFile>, ProfileFile> changedValues = changedSupplierValues(currentValues);
50+
51+
if (changedValues == null) {
52+
// no suppliers have changed values, return the current aggregate
53+
return currentState.aggregate;
54+
}
55+
56+
// one or more supplier values have changed, we need to update the aggregate (and the state)
57+
// the order of the suppliers matters so we MUST preserve it using LinkedHashMap with insertion ordering
58+
Map<Supplier<ProfileFile>, ProfileFile> nextValues = new LinkedHashMap<>(currentValues);
59+
nextValues.putAll(changedValues);
60+
61+
ProfileFile.Aggregator aggregator = ProfileFile.aggregator();
62+
nextValues.values().forEach(aggregator::addFile);
63+
ProfileFile nextAggregate = aggregator.build();
64+
65+
SupplierState nextState = new SupplierState(nextValues, nextAggregate);
66+
if (state.compareAndSet(currentState, nextState)) {
67+
return nextAggregate;
68+
}
69+
// else: another thread has modified the state in between, assume it is up to date and use the new state
70+
return state.get().aggregate;
71+
}
72+
73+
// return the suppliers with changed values. Returns null if no values have changed
74+
private Map<Supplier<ProfileFile>, ProfileFile> changedSupplierValues(Map<Supplier<ProfileFile>, ProfileFile> currentValues) {
75+
Map<Supplier<ProfileFile>, ProfileFile> changedValues = null;
76+
for (ProfileFileSupplier supplier : suppliers) {
77+
ProfileFile next = supplier.get();
78+
ProfileFile prev = currentValues.get(supplier);
79+
// we ONLY care about if the reference has changed, we don't care about object equality here
80+
if (prev != next) {
81+
if (changedValues == null) {
82+
// changed values must also preserve supplier order
83+
changedValues = new LinkedHashMap<>();
84+
}
85+
changedValues.put(supplier, next);
86+
}
87+
}
88+
return changedValues;
89+
}
90+
91+
private static final class SupplierState {
92+
final Map<Supplier<ProfileFile>, ProfileFile> values;
93+
final ProfileFile aggregate;
94+
95+
private SupplierState(Map<Supplier<ProfileFile>, ProfileFile> values, ProfileFile aggregate) {
96+
this.values = values;
97+
this.aggregate = aggregate;
98+
}
99+
}
100+
}

0 commit comments

Comments
 (0)