diff --git a/core/src/main/java/io/grpc/Attributes.java b/core/src/main/java/io/grpc/Attributes.java index 71f0a0cfc4..a338e263c5 100644 --- a/core/src/main/java/io/grpc/Attributes.java +++ b/core/src/main/java/io/grpc/Attributes.java @@ -33,7 +33,9 @@ package io.grpc; import com.google.common.base.Preconditions; +import java.util.Collections; import java.util.HashMap; +import java.util.Set; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; @@ -45,7 +47,7 @@ import javax.annotation.concurrent.Immutable; @Immutable public final class Attributes { - private final HashMap data = new HashMap(); + private final HashMap, Object> data = new HashMap, Object>(); public static final Attributes EMPTY = new Attributes(); @@ -58,7 +60,16 @@ public final class Attributes { @SuppressWarnings("unchecked") @Nullable public T get(Key key) { - return (T) data.get(key.name); + return (T) data.get(key); + } + + /** + * Returns set of keys stored in container. + * + * @return Set of Key objects. + */ + public Set> keys() { + return Collections.unmodifiableSet(data.keySet()); } /** @@ -71,13 +82,7 @@ public final class Attributes { public static final class Key { private final String name; - /** - * Construct the key. - * - * @param name the name, which should be namespaced like com.foo.BarAttribute to avoid - * collision. - */ - public Key(String name) { + private Key(String name) { this.name = name; } @@ -85,6 +90,18 @@ public final class Attributes { public String toString() { return name; } + + /** + * Factory method for creating instances of {@link Key}. + * + * @param name the name of Key, which should be namespaced like com.foo.BarAttribute to avoid + * collision. Name collision, won't cause key collision. + * @param Key type + * @return Key object + */ + public static Key of(String name) { + return new Key(name); + } } @Override @@ -100,7 +117,7 @@ public final class Attributes { } public Builder set(Key key, T value) { - product.data.put(key.name, value); + product.data.put(key, value); return this; } diff --git a/core/src/main/java/io/grpc/NameResolver.java b/core/src/main/java/io/grpc/NameResolver.java index 29db2df9fe..54d7eacf9c 100644 --- a/core/src/main/java/io/grpc/NameResolver.java +++ b/core/src/main/java/io/grpc/NameResolver.java @@ -74,7 +74,7 @@ public abstract class NameResolver { * port number. */ public static final Attributes.Key PARAMS_DEFAULT_PORT = - new Attributes.Key("io.grpc.NameResolverDefaultPort"); + Attributes.Key.of("io.grpc.NameResolverDefaultPort"); /** * Creates a {@link NameResolver} for the given target URI, or {@code null} if the given URI diff --git a/core/src/test/java/io/grpc/AttributesTest.java b/core/src/test/java/io/grpc/AttributesTest.java new file mode 100644 index 0000000000..3f09c5c1c9 --- /dev/null +++ b/core/src/test/java/io/grpc/AttributesTest.java @@ -0,0 +1,68 @@ +/* + * Copyright 2016, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.grpc; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link Attributes}. */ +@RunWith(JUnit4.class) +public class AttributesTest { + private static Attributes.Key YOLO_KEY = Attributes.Key.of("yolo"); + + @Test + public void buildAttributes() { + Attributes attrs = Attributes.newBuilder().set(YOLO_KEY, "To be, or not to be?").build(); + assertSame("To be, or not to be?", attrs.get(YOLO_KEY)); + assertEquals(1, attrs.keys().size()); + } + + @Test + public void duplicates() { + Attributes attrs = Attributes.newBuilder() + .set(YOLO_KEY, "To be?") + .set(YOLO_KEY, "Or not to be?") + .set(Attributes.Key.of("yolo"), "I'm not a duplicate") + .build(); + assertSame("Or not to be?", attrs.get(YOLO_KEY)); + assertEquals(2, attrs.keys().size()); + } + + @Test + public void empty() { + assertEquals(0, Attributes.EMPTY.keys().size()); + } +} diff --git a/core/src/test/java/io/grpc/CallOptionsTest.java b/core/src/test/java/io/grpc/CallOptionsTest.java index fec6ce8dfc..8c1fa39df7 100644 --- a/core/src/test/java/io/grpc/CallOptionsTest.java +++ b/core/src/test/java/io/grpc/CallOptionsTest.java @@ -53,7 +53,7 @@ import java.util.concurrent.TimeUnit; public class CallOptionsTest { private String sampleAuthority = "authority"; private Long sampleDeadlineNanoTime = 1L; - private Key sampleKey = new Attributes.Key("sample"); + private Key sampleKey = Attributes.Key.of("sample"); private Attributes sampleAffinity = Attributes.newBuilder().set(sampleKey, "blah").build(); private CallOptions allSet = CallOptions.DEFAULT .withAuthority(sampleAuthority)