From 47f095856cebf1db6982d8a1e945ccca0ac32bb1 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Mon, 9 Feb 2015 09:41:56 +0100 Subject: [PATCH] Allow to get existing ChannelOption / AttributeKey from String Motivation: We should allow to get a ChannelOption/AttributeKey from a String. This will make it a lot easier to make use of configuration files in applications. Modifications: - Add exists(...), newInstance(...) method to ChannelOption and AttributeKey and alter valueOf(...) to return an existing instance for a String or create one. - Add unit tests. Result: Much more flexible usage of ChannelOption and AttributeKey. --- .../main/java/io/netty/util/AttributeKey.java | 46 +++++++++++-- .../main/java/io/netty/util/UniqueName.java | 18 +++--- .../java/io/netty/util/AttributeKeyTest.java | 59 +++++++++++++++++ .../java/io/netty/channel/ChannelOption.java | 45 +++++++++++-- .../io/netty/channel/ChannelOptionTest.java | 64 +++++++++++++++++++ 5 files changed, 215 insertions(+), 17 deletions(-) create mode 100644 common/src/test/java/io/netty/util/AttributeKeyTest.java create mode 100644 transport/src/test/java/io/netty/channel/ChannelOptionTest.java diff --git a/common/src/main/java/io/netty/util/AttributeKey.java b/common/src/main/java/io/netty/util/AttributeKey.java index 97d703574d..3ab9032cca 100644 --- a/common/src/main/java/io/netty/util/AttributeKey.java +++ b/common/src/main/java/io/netty/util/AttributeKey.java @@ -19,6 +19,8 @@ import io.netty.util.internal.PlatformDependent; import java.util.concurrent.ConcurrentMap; +import static io.netty.util.internal.ObjectUtil.checkNotNull; + /** * Key which can be used to access {@link Attribute} out of the {@link AttributeMap}. Be aware that it is not be * possible to have multiple keys with the same name. @@ -29,14 +31,48 @@ import java.util.concurrent.ConcurrentMap; @SuppressWarnings({ "UnusedDeclaration", "deprecation" }) // 'T' is used only at compile time public final class AttributeKey extends UniqueName { - private static final ConcurrentMap names = PlatformDependent.newConcurrentHashMap(); + @SuppressWarnings("rawtypes") + private static final ConcurrentMap names = PlatformDependent.newConcurrentHashMap(); /** - * Creates a new {@link AttributeKey} with the specified {@code name}. + * Creates a new {@link AttributeKey} with the specified {@param name} or return the already existing + * {@link AttributeKey} for the given name. */ - @SuppressWarnings("deprecation") + @SuppressWarnings("unchecked") public static AttributeKey valueOf(String name) { - return new AttributeKey(name); + checkNotNull(name, "name"); + AttributeKey option = names.get(name); + if (option == null) { + option = new AttributeKey(name); + AttributeKey old = names.putIfAbsent(name, option); + if (old != null) { + option = old; + } + } + return option; + } + + /** + * Returns {@code true} if a {@link AttributeKey} exists for the given {@code name}. + */ + public static boolean exists(String name) { + checkNotNull(name, "name"); + return names.containsKey(name); + } + + /** + * Creates a new {@link AttributeKey} for the given {@param name} or fail with an + * {@link IllegalArgumentException} if a {@link AttributeKey} for the given {@param name} exists. + */ + @SuppressWarnings("unchecked") + public static AttributeKey newInstance(String name) { + checkNotNull(name, "name"); + AttributeKey option = new AttributeKey(name); + AttributeKey old = names.putIfAbsent(name, option); + if (old != null) { + throw new IllegalArgumentException(String.format("'%s' is already in use", name)); + } + return option; } /** @@ -44,6 +80,6 @@ public final class AttributeKey extends UniqueName { */ @Deprecated public AttributeKey(String name) { - super(names, name); + super(name); } } diff --git a/common/src/main/java/io/netty/util/UniqueName.java b/common/src/main/java/io/netty/util/UniqueName.java index af9d135fcc..5ab7d8c500 100644 --- a/common/src/main/java/io/netty/util/UniqueName.java +++ b/common/src/main/java/io/netty/util/UniqueName.java @@ -18,6 +18,8 @@ package io.netty.util; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; +import static io.netty.util.internal.ObjectUtil.checkNotNull; + /** * @deprecated Known to have problems with class loaders. * @@ -39,12 +41,8 @@ public class UniqueName implements Comparable { * @param args the arguments to process */ public UniqueName(ConcurrentMap map, String name, Object... args) { - if (map == null) { - throw new NullPointerException("map"); - } - if (name == null) { - throw new NullPointerException("name"); - } + checkNotNull(map, "map"); + if (args != null && args.length > 0) { validateArgs(args); } @@ -52,9 +50,13 @@ public class UniqueName implements Comparable { if (map.putIfAbsent(name, Boolean.TRUE) != null) { throw new IllegalArgumentException(String.format("'%s' is already in use", name)); } - + this.name = checkNotNull(name, "name"); + id = nextId.incrementAndGet(); + } + + protected UniqueName(String name) { + this.name = checkNotNull(name, "name"); id = nextId.incrementAndGet(); - this.name = name; } /** diff --git a/common/src/test/java/io/netty/util/AttributeKeyTest.java b/common/src/test/java/io/netty/util/AttributeKeyTest.java new file mode 100644 index 0000000000..ebaa3adbc3 --- /dev/null +++ b/common/src/test/java/io/netty/util/AttributeKeyTest.java @@ -0,0 +1,59 @@ +/* + * Copyright 2015 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.util; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class AttributeKeyTest { + + @Test + public void testExists() { + String name = "test"; + assertFalse(AttributeKey.exists(name)); + AttributeKey attr = AttributeKey.valueOf(name); + + assertTrue(AttributeKey.exists(name)); + assertNotNull(attr); + } + + @Test + public void testValueOf() { + String name = "test1"; + assertFalse(AttributeKey.exists(name)); + AttributeKey attr = AttributeKey.valueOf(name); + AttributeKey attr2 = AttributeKey.valueOf(name); + + assertSame(attr, attr2); + } + + @Test + public void testNewInstance() { + String name = "test2"; + assertFalse(AttributeKey.exists(name)); + AttributeKey attr = AttributeKey.newInstance(name); + assertTrue(AttributeKey.exists(name)); + assertNotNull(attr); + + try { + AttributeKey.newInstance(name); + fail(); + } catch (IllegalArgumentException e) { + // expected + } + } +} diff --git a/transport/src/main/java/io/netty/channel/ChannelOption.java b/transport/src/main/java/io/netty/channel/ChannelOption.java index c6f87cf2cf..8e24cb7b8c 100644 --- a/transport/src/main/java/io/netty/channel/ChannelOption.java +++ b/transport/src/main/java/io/netty/channel/ChannelOption.java @@ -23,6 +23,8 @@ import java.net.InetAddress; import java.net.NetworkInterface; import java.util.concurrent.ConcurrentMap; +import static io.netty.util.internal.ObjectUtil.checkNotNull; + /** * A {@link ChannelOption} allows to configure a {@link ChannelConfig} in a type-safe * way. Which {@link ChannelOption} is supported depends on the actual implementation @@ -34,7 +36,8 @@ import java.util.concurrent.ConcurrentMap; @SuppressWarnings("deprecation") public class ChannelOption extends UniqueName { - private static final ConcurrentMap names = PlatformDependent.newConcurrentHashMap(); + @SuppressWarnings("rawtypes") + private static final ConcurrentMap names = PlatformDependent.newConcurrentHashMap(); public static final ChannelOption ALLOCATOR = valueOf("ALLOCATOR"); public static final ChannelOption RCVBUF_ALLOCATOR = valueOf("RCVBUF_ALLOCATOR"); @@ -85,10 +88,44 @@ public class ChannelOption extends UniqueName { valueOf("DATAGRAM_CHANNEL_ACTIVE_ON_REGISTRATION"); /** - * Creates a new {@link ChannelOption} with the specified {@code name}. + * Creates a new {@link ChannelOption} with the specified {@param name} or return the already existing + * {@link ChannelOption} for the given name. */ + @SuppressWarnings("unchecked") public static ChannelOption valueOf(String name) { - return new ChannelOption(name); + checkNotNull(name, "name"); + ChannelOption option = names.get(name); + if (option == null) { + option = new ChannelOption(name); + ChannelOption old = names.putIfAbsent(name, option); + if (old != null) { + option = old; + } + } + return option; + } + + /** + * Returns {@code true} if a {@link ChannelOption} exists for the given {@code name}. + */ + public static boolean exists(String name) { + checkNotNull(name, "name"); + return names.containsKey(name); + } + + /** + * Creates a new {@link ChannelOption} for the given {@param name} or fail with an + * {@link IllegalArgumentException} if a {@link ChannelOption} for the given {@param name} exists. + */ + @SuppressWarnings("unchecked") + public static ChannelOption newInstance(String name) { + checkNotNull(name, "name"); + ChannelOption option = new ChannelOption(name); + ChannelOption old = names.putIfAbsent(name, option); + if (old != null) { + throw new IllegalArgumentException(String.format("'%s' is already in use", name)); + } + return option; } /** @@ -96,7 +133,7 @@ public class ChannelOption extends UniqueName { */ @Deprecated protected ChannelOption(String name) { - super(names, name); + super(name); } /** diff --git a/transport/src/test/java/io/netty/channel/ChannelOptionTest.java b/transport/src/test/java/io/netty/channel/ChannelOptionTest.java new file mode 100644 index 0000000000..24542bf5f0 --- /dev/null +++ b/transport/src/test/java/io/netty/channel/ChannelOptionTest.java @@ -0,0 +1,64 @@ +/* + * Copyright 2015 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + + +public class ChannelOptionTest { + + @Test + public void testExists() { + String name = "test"; + assertFalse(ChannelOption.exists(name)); + ChannelOption option = ChannelOption.valueOf(name); + + assertTrue(ChannelOption.exists(name)); + assertNotNull(option); + } + + @Test + public void testValueOf() { + String name = "test1"; + assertFalse(ChannelOption.exists(name)); + ChannelOption option = ChannelOption.valueOf(name); + ChannelOption option2 = ChannelOption.valueOf(name); + + assertSame(option, option2); + } + + @Test + public void testCreateOrFail() { + String name = "test2"; + assertFalse(ChannelOption.exists(name)); + ChannelOption option = ChannelOption.newInstance(name); + assertTrue(ChannelOption.exists(name)); + assertNotNull(option); + + try { + ChannelOption.newInstance(name); + fail(); + } catch (IllegalArgumentException e) { + // expected + } + } +}