• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2015 The gRPC Authors
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  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package io.grpc;
18 
19 import static com.google.common.base.Preconditions.checkNotNull;
20 
21 import com.google.common.base.Objects;
22 import java.util.Collections;
23 import java.util.IdentityHashMap;
24 import java.util.Map;
25 import java.util.Set;
26 import javax.annotation.Nullable;
27 import javax.annotation.concurrent.Immutable;
28 
29 /**
30  * An immutable type-safe container of attributes.
31  *
32  * <h3>Annotation semantics</h3>
33  *
34  * <p>As a convention, annotations such as {@link Grpc.TransportAttr} is defined to associate
35  * attribute {@link Key}s and their propagation paths.  The annotation may be applied to a {@code
36  * Key} definition field, a method that returns {@link Attributes}, or a variable of type {@link
37  * Attributes}, to indicate that the annotated {@link Attributes} objects may contain the annotated
38  * {@code Key}.
39  *
40  * <p>Javadoc users may click "USE" on the navigation bars of the annotation's javadoc page to view
41  * references of such annotation.
42  *
43  * @since 1.13.0
44  */
45 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1764")
46 @Immutable
47 public final class Attributes {
48 
49   private final IdentityHashMap<Key<?>, Object> data;
50 
51   private static final IdentityHashMap<Key<?>, Object> EMPTY_MAP =
52       new IdentityHashMap<Key<?>, Object>();
53   public static final Attributes EMPTY = new Attributes(EMPTY_MAP);
54 
Attributes(IdentityHashMap<Key<?>, Object> data)55   private Attributes(IdentityHashMap<Key<?>, Object> data) {
56     assert data != null;
57     this.data = data;
58   }
59 
60   /**
61    * Gets the value for the key, or {@code null} if it's not present.
62    */
63   @SuppressWarnings("unchecked")
64   @Nullable
get(Key<T> key)65   public <T> T get(Key<T> key) {
66     return (T) data.get(key);
67   }
68 
69   /**
70    * Returns set of keys stored in container.
71    *
72    * @return Set of Key objects.
73    * @deprecated This method is being considered for removal, if you feel this method is needed
74    *     please reach out on this Github issue:
75    *     <a href="https://github.com/grpc/grpc-java/issues/1764">grpc-java/issues/1764</a>.
76    */
77   @Deprecated
keys()78   public Set<Key<?>> keys() {
79     return Collections.unmodifiableSet(data.keySet());
80   }
81 
keysForTest()82   Set<Key<?>> keysForTest() {
83     return Collections.unmodifiableSet(data.keySet());
84   }
85 
86   /**
87    * Create a new builder that is pre-populated with the content from a given container.
88    * @deprecated Use {@link Attributes#toBuilder()} on the {@link Attributes} instance instead.
89    *     This method will be removed in the future.
90    */
91   @Deprecated
newBuilder(Attributes base)92   public static Builder newBuilder(Attributes base) {
93     checkNotNull(base, "base");
94     return new Builder(base);
95   }
96 
97   /**
98    * Create a new builder.
99    */
newBuilder()100   public static Builder newBuilder() {
101     return new Builder(EMPTY);
102   }
103 
104   /**
105    * Creates a new builder that is pre-populated with the content of this container.
106    * @return a new builder.
107    */
toBuilder()108   public Builder toBuilder() {
109     return new Builder(this);
110   }
111 
112   /**
113    * Key for an key-value pair. Uses reference equality.
114    *
115    * @param <T> type of the value in the key-value pair
116    */
117   @Immutable
118   @SuppressWarnings("UnusedTypeParameter")
119   public static final class Key<T> {
120     private final String debugString;
121 
Key(String debugString)122     private Key(String debugString) {
123       this.debugString = debugString;
124     }
125 
126     @Override
toString()127     public String toString() {
128       return debugString;
129     }
130 
131     /**
132      * Factory method for creating instances of {@link Key}.
133      *
134      * @param debugString a string used to describe the key, used for debugging.
135      * @param <T> Key type
136      * @return Key object
137      * @deprecated use {@link #create} instead. This method will be removed in the future.
138      */
139     @Deprecated
of(String debugString)140     public static <T> Key<T> of(String debugString) {
141       return new Key<>(debugString);
142     }
143 
144     /**
145      * Factory method for creating instances of {@link Key}.
146      *
147      * @param debugString a string used to describe the key, used for debugging.
148      * @param <T> Key type
149      * @return Key object
150      */
create(String debugString)151     public static <T> Key<T> create(String debugString) {
152       return new Key<>(debugString);
153     }
154   }
155 
156   @Override
toString()157   public String toString() {
158     return data.toString();
159   }
160 
161   /**
162    * Returns true if the given object is also a {@link Attributes} with an equal attribute values.
163    *
164    * <p>Note that if a stored values are mutable, it is possible for two objects to be considered
165    * equal at one point in time and not equal at another (due to concurrent mutation of attribute
166    * values).
167    *
168    * <p>This method is not implemented efficiently and is meant for testing.
169    *
170    * @param o an object.
171    * @return true if the given object is a {@link Attributes} equal attributes.
172    */
173   @Override
equals(Object o)174   public boolean equals(Object o) {
175     if (this == o) {
176       return true;
177     }
178     if (o == null || getClass() != o.getClass()) {
179       return false;
180     }
181     Attributes that = (Attributes) o;
182     if (data.size() != that.data.size()) {
183       return false;
184     }
185     for (Map.Entry<Key<?>, Object> e : data.entrySet()) {
186       if (!that.data.containsKey(e.getKey())) {
187         return false;
188       }
189       if (!Objects.equal(e.getValue(), that.data.get(e.getKey()))) {
190         return false;
191       }
192     }
193     return true;
194   }
195 
196   /**
197    * Returns a hash code for the attributes.
198    *
199    * <p>Note that if a stored values are mutable, it is possible for two objects to be considered
200    * equal at one point in time and not equal at another (due to concurrent mutation of attribute
201    * values).
202    *
203    * @return a hash code for the attributes map.
204    */
205   @Override
hashCode()206   public int hashCode() {
207     int hashCode = 0;
208     for (Map.Entry<Key<?>, Object> e : data.entrySet()) {
209       hashCode += Objects.hashCode(e.getKey(), e.getValue());
210     }
211     return hashCode;
212   }
213 
214   /**
215    * The helper class to build an Attributes instance.
216    */
217   public static final class Builder {
218     private Attributes base;
219     private IdentityHashMap<Key<?>, Object> newdata;
220 
Builder(Attributes base)221     private Builder(Attributes base) {
222       assert base != null;
223       this.base = base;
224     }
225 
data(int size)226     private IdentityHashMap<Key<?>, Object> data(int size) {
227       if (newdata == null) {
228         newdata = new IdentityHashMap<>(size);
229       }
230       return newdata;
231     }
232 
set(Key<T> key, T value)233     public <T> Builder set(Key<T> key, T value) {
234       data(1).put(key, value);
235       return this;
236     }
237 
238     /**
239      * Removes the key and associated value from the attribtues.
240      *
241      * @since 1.22.0
242      * @param key The key to remove
243      * @return this
244      */
245     @ExperimentalApi("https://github.com/grpc/grpc-java/issues/5777")
discard(Key<T> key)246     public <T> Builder discard(Key<T> key) {
247       if (base.data.containsKey(key)) {
248         IdentityHashMap<Key<?>, Object> newBaseData = new IdentityHashMap<>(base.data);
249         newBaseData.remove(key);
250         base = new Attributes(newBaseData);
251       }
252       if (newdata != null) {
253         newdata.remove(key);
254       }
255       return this;
256     }
257 
setAll(Attributes other)258     public Builder setAll(Attributes other) {
259       data(other.data.size()).putAll(other.data);
260       return this;
261     }
262 
263     /**
264      * Build the attributes.
265      */
build()266     public Attributes build() {
267       if (newdata != null) {
268         for (Map.Entry<Key<?>, Object> entry : base.data.entrySet()) {
269           if (!newdata.containsKey(entry.getKey())) {
270             newdata.put(entry.getKey(), entry.getValue());
271           }
272         }
273         base = new Attributes(newdata);
274         newdata = null;
275       }
276       return base;
277     }
278   }
279 }
280