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