• 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.Collections;
19 import java.util.HashMap;
20 import java.util.Map;
21 import java.util.Optional;
22 import software.amazon.awssdk.annotations.NotThreadSafe;
23 import software.amazon.awssdk.annotations.SdkPublicApi;
24 import software.amazon.awssdk.utils.ToString;
25 import software.amazon.awssdk.utils.Validate;
26 import software.amazon.awssdk.utils.builder.CopyableBuilder;
27 import software.amazon.awssdk.utils.builder.ToCopyableBuilder;
28 
29 /**
30  * A mutable collection of {@link ExecutionAttribute}s that can be modified by {@link ExecutionInterceptor}s in order to save and
31  * retrieve information specific to the current execution.
32  *
33  * This is useful for sharing data between {@link ExecutionInterceptor} method calls specific to a particular execution.
34  */
35 @SdkPublicApi
36 @NotThreadSafe
37 public class ExecutionAttributes implements ToCopyableBuilder<ExecutionAttributes.Builder, ExecutionAttributes> {
38     private final Map<ExecutionAttribute<?>, Object> attributes;
39 
ExecutionAttributes()40     public ExecutionAttributes() {
41         this.attributes = new HashMap<>(32);
42     }
43 
ExecutionAttributes(Map<? extends ExecutionAttribute<?>, ?> attributes)44     protected ExecutionAttributes(Map<? extends ExecutionAttribute<?>, ?> attributes) {
45         this.attributes = new HashMap<>(attributes);
46     }
47 
48     /**
49      * Retrieve the current value of the provided attribute in this collection of attributes. This will return null if the value
50      * is not set.
51      */
getAttribute(ExecutionAttribute<U> attribute)52     public <U> U getAttribute(ExecutionAttribute<U> attribute) {
53         return attribute.storage().get(attributes);
54     }
55 
56     /**
57      * Retrieve the collection of attributes.
58      */
getAttributes()59     public Map<ExecutionAttribute<?>, Object> getAttributes() {
60         return Collections.unmodifiableMap(attributes);
61     }
62 
63     /**
64      * Retrieve the Optional current value of the provided attribute in this collection of attributes.
65      * This will return Optional Value.
66      */
getOptionalAttribute(ExecutionAttribute<U> attribute)67     public <U> Optional<U> getOptionalAttribute(ExecutionAttribute<U> attribute) {
68         return Optional.ofNullable(getAttribute(attribute));
69     }
70 
71     /**
72      * Update or set the provided attribute in this collection of attributes.
73      */
putAttribute(ExecutionAttribute<U> attribute, U value)74     public <U> ExecutionAttributes putAttribute(ExecutionAttribute<U> attribute, U value) {
75         attribute.storage().set(attributes, value);
76         return this;
77     }
78 
79     /**
80      * Set the provided attribute in this collection of attributes if it does not already exist in the collection.
81      */
putAttributeIfAbsent(ExecutionAttribute<U> attribute, U value)82     public <U> ExecutionAttributes putAttributeIfAbsent(ExecutionAttribute<U> attribute, U value) {
83         attribute.storage().setIfAbsent(attributes, value);
84         return this;
85     }
86 
87     /**
88      * Merge attributes of a higher precedence into the current lower precedence collection.
89      */
merge(ExecutionAttributes lowerPrecedenceExecutionAttributes)90     public ExecutionAttributes merge(ExecutionAttributes lowerPrecedenceExecutionAttributes) {
91         Map<ExecutionAttribute<?>, Object> copiedAttributes = new HashMap<>(this.attributes);
92         lowerPrecedenceExecutionAttributes.getAttributes().forEach(copiedAttributes::putIfAbsent);
93         return new ExecutionAttributes(copiedAttributes);
94     }
95 
96     /**
97      * Add the provided attributes to this attribute, if the provided attribute does not exist.
98      */
putAbsentAttributes(ExecutionAttributes lowerPrecedenceExecutionAttributes)99     public void putAbsentAttributes(ExecutionAttributes lowerPrecedenceExecutionAttributes) {
100         if (lowerPrecedenceExecutionAttributes != null) {
101             lowerPrecedenceExecutionAttributes.getAttributes().forEach(attributes::putIfAbsent);
102         }
103     }
104 
builder()105     public static Builder builder() {
106         return new Builder();
107     }
108 
109     @Override
toBuilder()110     public Builder toBuilder() {
111         return new ExecutionAttributes.Builder(this);
112     }
113 
copy()114     public ExecutionAttributes copy() {
115         return toBuilder().build();
116     }
117 
118     @Override
equals(Object o)119     public boolean equals(Object o) {
120         if (this == o) {
121             return true;
122         }
123 
124         if (o == null || !(o instanceof ExecutionAttributes)) {
125             return false;
126         }
127 
128         ExecutionAttributes that = (ExecutionAttributes) o;
129 
130         return attributes != null ? attributes.equals(that.attributes) : that.attributes == null;
131     }
132 
133     @Override
hashCode()134     public int hashCode() {
135         return attributes != null ? attributes.hashCode() : 0;
136     }
137 
138     @Override
toString()139     public String toString() {
140         return ToString.builder("ExecutionAttributes")
141                        .add("attributes", attributes.keySet())
142                        .build();
143     }
144 
unmodifiableExecutionAttributes(ExecutionAttributes attributes)145     public static ExecutionAttributes unmodifiableExecutionAttributes(ExecutionAttributes attributes) {
146         return new UnmodifiableExecutionAttributes(attributes);
147     }
148 
149     private static class UnmodifiableExecutionAttributes extends ExecutionAttributes {
UnmodifiableExecutionAttributes(ExecutionAttributes executionAttributes)150         UnmodifiableExecutionAttributes(ExecutionAttributes executionAttributes) {
151             super(executionAttributes.attributes);
152         }
153 
154         @Override
putAttribute(ExecutionAttribute<U> attribute, U value)155         public <U> ExecutionAttributes putAttribute(ExecutionAttribute<U> attribute, U value) {
156             throw new UnsupportedOperationException();
157         }
158 
159         @Override
putAttributeIfAbsent(ExecutionAttribute<U> attribute, U value)160         public <U> ExecutionAttributes putAttributeIfAbsent(ExecutionAttribute<U> attribute, U value) {
161             throw new UnsupportedOperationException();
162         }
163     }
164 
165     /**
166      * TODO: We should deprecate this builder - execution attributes are mutable - why do we need a builder? We can just use
167      * copy() if it's because of {@link #unmodifiableExecutionAttributes(ExecutionAttributes)}.
168      */
169     public static final class Builder implements CopyableBuilder<ExecutionAttributes.Builder, ExecutionAttributes> {
170         private final Map<ExecutionAttribute<?>, Object> executionAttributes = new HashMap<>(32);
171 
Builder()172         private Builder() {
173         }
174 
Builder(ExecutionAttributes source)175         private Builder(ExecutionAttributes source) {
176             this.executionAttributes.putAll(source.attributes);
177         }
178 
179         /**
180          * Add a mapping between the provided key and value.
181          */
put(ExecutionAttribute<T> key, T value)182         public <T> ExecutionAttributes.Builder put(ExecutionAttribute<T> key, T value) {
183             Validate.notNull(key, "Key to set must not be null.");
184             key.storage().set(executionAttributes, value);
185             return this;
186         }
187 
188         /**
189          * Adds all the attributes from the map provided.
190          */
putAll(Map<? extends ExecutionAttribute<?>, ?> attributes)191         public ExecutionAttributes.Builder putAll(Map<? extends ExecutionAttribute<?>, ?> attributes) {
192             attributes.forEach(this::unsafePut);
193             return this;
194         }
195 
196         /**
197          * There is no way to make this safe without runtime checks, which we can't do because we don't have the class of T.
198          * This will just throw an exception at runtime if the types don't match up.
199          */
200         @SuppressWarnings("unchecked")
unsafePut(ExecutionAttribute<T> key, Object value)201         private <T> void unsafePut(ExecutionAttribute<T> key, Object value) {
202             key.storage().set(executionAttributes, (T) value);
203         }
204 
205         @Override
build()206         public ExecutionAttributes build() {
207             return new ExecutionAttributes(executionAttributes);
208         }
209     }
210 }