• 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.core.interceptor;
17 
18 import java.util.Map;
19 import java.util.Objects;
20 import java.util.concurrent.ConcurrentHashMap;
21 import java.util.concurrent.ConcurrentMap;
22 import java.util.function.BiFunction;
23 import java.util.function.Function;
24 import java.util.function.Supplier;
25 import software.amazon.awssdk.annotations.SdkPublicApi;
26 import software.amazon.awssdk.utils.Validate;
27 
28 /**
29  * An attribute attached to a particular execution, stored in {@link ExecutionAttributes}.
30  *
31  * This is typically used as a static final field in an {@link ExecutionInterceptor}:
32  * <pre>
33  * {@code
34  *  class MyExecutionInterceptor implements ExecutionInterceptor {
35  *      private static final ExecutionAttribute<String> DATA = new ExecutionAttribute<>();
36  *
37  *      public void beforeExecution(Context.BeforeExecution execution, ExecutionAttributes executionAttributes) {
38  *          executionAttributes.put(DATA, "Request: " + execution.request());
39  *      }
40  *
41  *      public void afterExecution(Context.AfterExecution execution, ExecutionAttributes executionAttributes) {
42  *          String data = executionAttributes.get(DATA); // Retrieve the value saved in beforeExecution.
43  *      }
44  *  }
45  * }
46  </pre>
47  *
48  * @param <T> The type of data associated with this attribute.
49  */
50 @SdkPublicApi
51 public final class ExecutionAttribute<T> {
52     private static final ConcurrentMap<String, ExecutionAttribute<?>> NAME_HISTORY = new ConcurrentHashMap<>();
53 
54     private final String name;
55     private final ValueStorage<T> storage;
56 
57     /**
58      * Creates a new {@link ExecutionAttribute} bound to the provided type param.
59      *
60      * @param name Descriptive name for the attribute, used primarily for debugging purposes.
61      */
ExecutionAttribute(String name)62     public ExecutionAttribute(String name) {
63         this(name, null);
64     }
65 
ExecutionAttribute(String name, ValueStorage<T> storage)66     private ExecutionAttribute(String name, ValueStorage<T> storage) {
67         this.name = name;
68         this.storage = storage == null ?
69                        new DefaultValueStorage() :
70                        storage;
71         ensureUnique();
72     }
73 
74     /**
75      * Create an execution attribute whose value is derived from another attribute.
76      *
77      * <p>Whenever this value is read, its value is read from a different "real" attribute, and whenever this value is written its
78      * value is written to the "real" attribute, instead.
79      *
80      * <p>This is useful when new attributes are created to replace old attributes, but for backwards-compatibility those old
81      * attributes still need to be made available.
82      *
83      * @param name The name of the attribute to create
84      * @param attributeType The type of the attribute being created
85      * @param realAttribute The "real" attribute from which this attribute is derived
86      */
derivedBuilder(String name, @SuppressWarnings("unused") Class<T> attributeType, ExecutionAttribute<U> realAttribute)87     public static <T, U> DerivedAttributeBuilder<T, U> derivedBuilder(String name,
88                                                                       @SuppressWarnings("unused") Class<T> attributeType,
89                                                                       ExecutionAttribute<U> realAttribute) {
90         return new DerivedAttributeBuilder<>(name, realAttribute);
91     }
92 
93     /**
94      * Create an execution attribute whose value is backed by another attribute, and gets mapped to another execution attribute.
95      *
96      * <p>Whenever this value is read, its value is read from the backing attribute, but whenever this value is written its
97      * value is written to the given attribute AND the mapped attribute.
98      *
99      * <p>This is useful when you have a complex attribute relationship, where certain attributes may depend on other attributes.
100      *
101      * @param name The name of the attribute to create
102      * @param backingAttributeSupplier The supplier for the backing attribute, which this attribute is backed by
103      * @param attributeSupplier The supplier for the attribute which is mapped from the backing attribute
104      */
mappedBuilder(String name, Supplier<ExecutionAttribute<T>> backingAttributeSupplier, Supplier<ExecutionAttribute<U>> attributeSupplier)105     static <T, U> MappedAttributeBuilder<T, U> mappedBuilder(String name,
106                                                              Supplier<ExecutionAttribute<T>> backingAttributeSupplier,
107                                                              Supplier<ExecutionAttribute<U>> attributeSupplier) {
108         return new MappedAttributeBuilder<>(name, backingAttributeSupplier, attributeSupplier);
109     }
110 
ensureUnique()111     private void ensureUnique() {
112         ExecutionAttribute<?> prev = NAME_HISTORY.putIfAbsent(name, this);
113         if (prev != null) {
114             throw new IllegalArgumentException(String.format("No duplicate ExecutionAttribute names allowed but both "
115                                                              + "ExecutionAttributes %s and %s have the same name: %s. "
116                                                              + "ExecutionAttributes should be referenced from a shared static "
117                                                              + "constant to protect against erroneous or unexpected collisions.",
118                                                              Integer.toHexString(System.identityHashCode(prev)),
119                                                              Integer.toHexString(System.identityHashCode(this)),
120                                                              name));
121         }
122     }
123 
124     @Override
toString()125     public String toString() {
126         return name;
127     }
128 
129     /**
130      * This override considers execution attributes with the same name
131      * to be the same object for the purpose of attribute merge.
132      * @return boolean indicating whether the objects are equal or not.
133      */
134     @Override
equals(Object o)135     public boolean equals(Object o) {
136         if (this == o) {
137             return true;
138         }
139 
140         if (o == null || getClass() != o.getClass()) {
141             return false;
142         }
143 
144         ExecutionAttribute that = (ExecutionAttribute) o;
145         return that.name.equals(this.name);
146     }
147 
148     /**
149      * This override considers execution attributes with the same name
150      * to be the same object for the purpose of attribute merge.
151      * @return hash code
152      */
153     @Override
hashCode()154     public int hashCode() {
155         return Objects.hashCode(name);
156     }
157 
158     /**
159      * Visible for {@link ExecutionAttributes} to invoke when writing or reading values for this attribute.
160      */
storage()161     ValueStorage<T> storage() {
162         return storage;
163     }
164 
165     public static final class DerivedAttributeBuilder<T, U> {
166         private final String name;
167         private final ExecutionAttribute<U> realAttribute;
168         private Function<U, T> readMapping;
169         private BiFunction<U, T, U> writeMapping;
170 
DerivedAttributeBuilder(String name, ExecutionAttribute<U> realAttribute)171         private DerivedAttributeBuilder(String name, ExecutionAttribute<U> realAttribute) {
172             this.name = name;
173             this.realAttribute = realAttribute;
174         }
175 
176         /**
177          * Set the "read" mapping for this derived attribute. The provided function accepts the current value of the
178          * "real" attribute and returns the value of the derived attribute.
179          */
readMapping(Function<U, T> readMapping)180         public DerivedAttributeBuilder<T, U> readMapping(Function<U, T> readMapping) {
181             this.readMapping = readMapping;
182             return this;
183         }
184 
185         /**
186          * Set the "write" mapping for this derived attribute. The provided function accepts the current value of the "real"
187          * attribute, the value that we're trying to set to the derived attribute, and returns the value to set to the "real"
188          * attribute.
189          */
writeMapping(BiFunction<U, T, U> writeMapping)190         public DerivedAttributeBuilder<T, U> writeMapping(BiFunction<U, T, U> writeMapping) {
191             this.writeMapping = writeMapping;
192             return this;
193         }
194 
build()195         public ExecutionAttribute<T> build() {
196             return new ExecutionAttribute<>(name, new DerivationValueStorage<>(this));
197         }
198     }
199 
200     /**
201      * The value storage allows reading or writing values to this attribute. Used by {@link ExecutionAttributes} for storing
202      * attribute values, whether they are "real" or derived.
203      */
204     interface ValueStorage<T> {
205         /**
206          * Retrieve an attribute's value from the provided attribute map.
207          */
get(Map<ExecutionAttribute<?>, Object> attributes)208         T get(Map<ExecutionAttribute<?>, Object> attributes);
209 
210         /**
211          * Set an attribute's value to the provided attribute map.
212          */
set(Map<ExecutionAttribute<?>, Object> attributes, T value)213         void set(Map<ExecutionAttribute<?>, Object> attributes, T value);
214 
215         /**
216          * Set an attribute's value to the provided attribute map, if the value is not already in the map.
217          */
setIfAbsent(Map<ExecutionAttribute<?>, Object> attributes, T value)218         void setIfAbsent(Map<ExecutionAttribute<?>, Object> attributes, T value);
219     }
220 
221     /**
222      * An implementation of {@link ValueStorage} that stores the current execution attribute in the provided attributes map.
223      */
224     private final class DefaultValueStorage implements ValueStorage<T> {
225         @SuppressWarnings("unchecked") // Safe because of the implementation of set()
226         @Override
get(Map<ExecutionAttribute<?>, Object> attributes)227         public T get(Map<ExecutionAttribute<?>, Object> attributes) {
228             return (T) attributes.get(ExecutionAttribute.this);
229         }
230 
231         @Override
set(Map<ExecutionAttribute<?>, Object> attributes, T value)232         public void set(Map<ExecutionAttribute<?>, Object> attributes, T value) {
233             attributes.put(ExecutionAttribute.this, value);
234         }
235 
236         @Override
setIfAbsent(Map<ExecutionAttribute<?>, Object> attributes, T value)237         public void setIfAbsent(Map<ExecutionAttribute<?>, Object> attributes, T value) {
238             attributes.putIfAbsent(ExecutionAttribute.this, value);
239         }
240     }
241 
242     /**
243      * An implementation of {@link ValueStorage} that derives its value from a different execution attribute in the provided
244      * attributes map.
245      */
246     private static final class DerivationValueStorage<T, U> implements ValueStorage<T> {
247         private final ExecutionAttribute<U> realAttribute;
248         private final Function<U, T> readMapping;
249         private final BiFunction<U, T, U> writeMapping;
250 
DerivationValueStorage(DerivedAttributeBuilder<T, U> builder)251         private DerivationValueStorage(DerivedAttributeBuilder<T, U> builder) {
252             this.realAttribute = Validate.paramNotNull(builder.realAttribute, "realAttribute");
253             this.readMapping = Validate.paramNotNull(builder.readMapping, "readMapping");
254             this.writeMapping = Validate.paramNotNull(builder.writeMapping, "writeMapping");
255         }
256 
257         @SuppressWarnings("unchecked") // Safe because of the implementation of set
258         @Override
get(Map<ExecutionAttribute<?>, Object> attributes)259         public T get(Map<ExecutionAttribute<?>, Object> attributes) {
260             return readMapping.apply((U) attributes.get(realAttribute));
261         }
262 
263         @SuppressWarnings("unchecked") // Safe because of the implementation of set
264         @Override
set(Map<ExecutionAttribute<?>, Object> attributes, T value)265         public void set(Map<ExecutionAttribute<?>, Object> attributes, T value) {
266             attributes.compute(realAttribute, (k, real) -> writeMapping.apply((U) real, value));
267         }
268 
269         @Override
setIfAbsent(Map<ExecutionAttribute<?>, Object> attributes, T value)270         public void setIfAbsent(Map<ExecutionAttribute<?>, Object> attributes, T value) {
271             T currentValue = get(attributes);
272             if (currentValue == null) {
273                 set(attributes, value);
274             }
275         }
276     }
277 
278     /**
279      * An implementation of {@link ValueStorage} that is backed by a different execution attribute in the provided
280      * attributes map (mirrors its value), and maps (updates) to another attribute.
281      */
282     private static final class MappedValueStorage<T, U> implements ValueStorage<T> {
283         private final Supplier<ExecutionAttribute<T>> backingAttributeSupplier;
284         private final Supplier<ExecutionAttribute<U>> attributeSupplier;
285         private final BiFunction<T, U, T> readMapping;
286         private final BiFunction<U, T, U> writeMapping;
287 
MappedValueStorage(MappedAttributeBuilder<T, U> builder)288         private MappedValueStorage(MappedAttributeBuilder<T, U> builder) {
289             this.backingAttributeSupplier = Validate.paramNotNull(builder.backingAttributeSupplier, "backingAttributeSupplier");
290             this.attributeSupplier = Validate.paramNotNull(builder.attributeSupplier, "attributeSupplier");
291             this.readMapping = Validate.paramNotNull(builder.readMapping, "readMapping");
292             this.writeMapping = Validate.paramNotNull(builder.writeMapping, "writeMapping");
293         }
294 
295         @SuppressWarnings("unchecked") // Safe because of the implementation of set
296         @Override
get(Map<ExecutionAttribute<?>, Object> attributes)297         public T get(Map<ExecutionAttribute<?>, Object> attributes) {
298             return readMapping.apply(
299                 (T) attributes.get(backingAttributeSupplier.get()),
300                 (U) attributes.get(attributeSupplier.get())
301             );
302         }
303 
304         @SuppressWarnings("unchecked") // Safe because of the implementation of set
305         @Override
set(Map<ExecutionAttribute<?>, Object> attributes, T value)306         public void set(Map<ExecutionAttribute<?>, Object> attributes, T value) {
307             attributes.put(backingAttributeSupplier.get(), value);
308             attributes.compute(attributeSupplier.get(), (k, attr) -> writeMapping.apply((U) attr, value));
309         }
310 
311         @Override
setIfAbsent(Map<ExecutionAttribute<?>, Object> attributes, T value)312         public void setIfAbsent(Map<ExecutionAttribute<?>, Object> attributes, T value) {
313             T currentValue = get(attributes);
314             if (currentValue == null) {
315                 set(attributes, value);
316             }
317         }
318     }
319 
320     protected static final class MappedAttributeBuilder<T, U> {
321         private final String name;
322         private final Supplier<ExecutionAttribute<T>> backingAttributeSupplier;
323         private final Supplier<ExecutionAttribute<U>> attributeSupplier;
324         private BiFunction<T, U, T> readMapping;
325         private BiFunction<U, T, U> writeMapping;
326 
MappedAttributeBuilder(String name, Supplier<ExecutionAttribute<T>> backingAttributeSupplier, Supplier<ExecutionAttribute<U>> attributeSupplier)327         private MappedAttributeBuilder(String name, Supplier<ExecutionAttribute<T>> backingAttributeSupplier,
328                                        Supplier<ExecutionAttribute<U>> attributeSupplier) {
329             this.name = name;
330             this.backingAttributeSupplier = backingAttributeSupplier;
331             this.attributeSupplier = attributeSupplier;
332         }
333 
334         /**
335          * Set the "read" mapping for this mapped attribute. The provided function accepts the current value of the
336          * backing attribute,
337          */
readMapping(BiFunction<T, U, T> readMapping)338         public MappedAttributeBuilder<T, U> readMapping(BiFunction<T, U, T> readMapping) {
339             this.readMapping = readMapping;
340             return this;
341         }
342 
343         /**
344          * Set the "write" mapping for this derived attribute. The provided function accepts the current value of the mapped
345          * attribute, the value that we are mapping from (the "backing" attribute), and returns the value to set to the mapped
346          * attribute.
347          */
writeMapping(BiFunction<U, T, U> writeMapping)348         public MappedAttributeBuilder<T, U> writeMapping(BiFunction<U, T, U> writeMapping) {
349             this.writeMapping = writeMapping;
350             return this;
351         }
352 
build()353         public ExecutionAttribute<T> build() {
354             return new ExecutionAttribute<>(name, new MappedValueStorage<>(this));
355         }
356     }
357 }
358