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 }