Ported the QOTM example to the new API

- Fixed bugs in the NIO datagram transports
  - DefaultNioDatagramChannelConfig did not initialize on Java 6
This commit is contained in:
Trustin Lee 2012-05-24 09:32:14 -07:00
parent c6f3b5762e
commit c7c923cab3
8 changed files with 108 additions and 618 deletions

View File

@ -15,21 +15,18 @@
*/
package io.netty.example.qotm;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
import io.netty.bootstrap.ConnectionlessBootstrap;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPipelineFactory;
import io.netty.channel.Channels;
import io.netty.channel.FixedReceiveBufferSizePredictorFactory;
import io.netty.channel.socket.DatagramChannel;
import io.netty.channel.socket.DatagramChannelFactory;
import io.netty.channel.socket.nio.NioDatagramChannelFactory;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.buffer.ChannelBuffers;
import io.netty.channel.Channel;
import io.netty.channel.ChannelBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.SelectorEventLoop;
import io.netty.util.CharsetUtil;
import java.net.InetSocketAddress;
/**
* A UDP broadcast client that asks for a quote of the moment (QOTM) to
* {@link QuoteOfTheMomentServer}.
@ -44,53 +41,36 @@ public class QuoteOfTheMomentClient {
this.port = port;
}
public void run() {
DatagramChannelFactory f =
new NioDatagramChannelFactory(Executors.newCachedThreadPool());
public void run() throws Exception {
ChannelBootstrap b = new ChannelBootstrap();
try {
b.eventLoop(new SelectorEventLoop())
.channel(new NioDatagramChannel())
.localAddress(new InetSocketAddress(0))
.option(ChannelOption.SO_BROADCAST, true)
.initializer(new ChannelInitializer() {
@Override
public void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(new QuoteOfTheMomentClientHandler());
}
});
ConnectionlessBootstrap b = new ConnectionlessBootstrap(f);
Channel ch = b.bind().sync().channel();
// Configure the pipeline factory.
b.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() throws Exception {
return Channels.pipeline(
new StringEncoder(CharsetUtil.ISO_8859_1),
new StringDecoder(CharsetUtil.ISO_8859_1),
new QuoteOfTheMomentClientHandler());
// Broadcast the QOTM request to port 8080.
ch.write(new DatagramPacket(
ChannelBuffers.copiedBuffer("QOTM?", CharsetUtil.UTF_8),
new InetSocketAddress("255.255.255.255", port)));
// QuoteOfTheMomentClientHandler will close the DatagramChannel when a
// response is received. If the channel is not closed within 5 seconds,
// print an error message and quit.
if (!ch.closeFuture().await(5000)) {
System.err.println("QOTM request timed out.");
}
});
// Enable broadcast
b.setOption("broadcast", "true");
// Allow packets as large as up to 1024 bytes (default is 768).
// You could increase or decrease this value to avoid truncated packets
// or to improve memory footprint respectively.
//
// Please also note that a large UDP packet might be truncated or
// dropped by your router no matter how you configured this option.
// In UDP, a packet is truncated or dropped if it is larger than a
// certain size, depending on router configuration. IPv4 routers
// truncate and IPv6 routers drop a large packet. That's why it is
// safe to send small packets in UDP.
b.setOption(
"receiveBufferSizePredictorFactory",
new FixedReceiveBufferSizePredictorFactory(1024));
DatagramChannel c = (DatagramChannel) b.bind(new InetSocketAddress(0));
// Broadcast the QOTM request to port 8080.
c.write("QOTM?", new InetSocketAddress("255.255.255.255", port));
// QuoteOfTheMomentClientHandler will close the DatagramChannel when a
// response is received. If the channel is not closed within 5 seconds,
// print an error message and quit.
if (!c.getCloseFuture().awaitUninterruptibly(5000)) {
System.err.println("QOTM request timed out.");
c.close().awaitUninterruptibly();
} finally {
b.shutdown();
}
f.releaseExternalResources();
}
public static void main(String[] args) throws Exception {

View File

@ -15,27 +15,30 @@
*/
package io.netty.example.qotm;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ExceptionEvent;
import io.netty.channel.MessageEvent;
import io.netty.channel.SimpleChannelUpstreamHandler;
import io.netty.channel.ChannelInboundHandlerContext;
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
import io.netty.channel.socket.DatagramPacket;
import io.netty.util.CharsetUtil;
public class QuoteOfTheMomentClientHandler extends ChannelInboundMessageHandlerAdapter<DatagramPacket> {
public class QuoteOfTheMomentClientHandler extends SimpleChannelUpstreamHandler {
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
public void messageReceived(
ChannelInboundHandlerContext<DatagramPacket> ctx, DatagramPacket msg)
throws Exception {
String msg = (String) e.getMessage();
if (msg.startsWith("QOTM: ")) {
System.out.println("Quote of the Moment: " + msg.substring(6));
e.channel().close();
String response = msg.data().toString(CharsetUtil.UTF_8);
if (response.startsWith("QOTM: ")) {
System.out.println("Quote of the Moment: " + response.substring(6));
ctx.close();
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
public void exceptionCaught(
ChannelInboundHandlerContext<DatagramPacket> ctx, Throwable cause)
throws Exception {
e.cause().printStackTrace();
e.channel().close();
cause.printStackTrace();
ctx.close();
}
}

View File

@ -15,19 +15,14 @@
*/
package io.netty.example.qotm;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
import io.netty.channel.Channel;
import io.netty.channel.ChannelBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.SelectorEventLoop;
import io.netty.bootstrap.ConnectionlessBootstrap;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPipelineFactory;
import io.netty.channel.Channels;
import io.netty.channel.FixedReceiveBufferSizePredictorFactory;
import io.netty.channel.socket.DatagramChannelFactory;
import io.netty.channel.socket.nio.NioDatagramChannelFactory;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;
import java.net.InetSocketAddress;
/**
* A UDP server that responds to the QOTM (quote of the moment) request to a
@ -43,41 +38,24 @@ public class QuoteOfTheMomentServer {
this.port = port;
}
public void run() {
DatagramChannelFactory f =
new NioDatagramChannelFactory(Executors.newCachedThreadPool());
public void run() throws Exception {
ChannelBootstrap b = new ChannelBootstrap();
try {
b.eventLoop(new SelectorEventLoop())
.channel(new NioDatagramChannel())
.localAddress(new InetSocketAddress(port))
.option(ChannelOption.SO_BROADCAST, true)
.initializer(new ChannelInitializer() {
@Override
public void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(new QuoteOfTheMomentServerHandler());
}
});
ConnectionlessBootstrap b = new ConnectionlessBootstrap(f);
// Configure the pipeline factory.
b.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() throws Exception {
return Channels.pipeline(
new StringEncoder(CharsetUtil.ISO_8859_1),
new StringDecoder(CharsetUtil.ISO_8859_1),
new QuoteOfTheMomentServerHandler());
}
});
// Enable broadcast
b.setOption("broadcast", "false");
// Allow packets as large as up to 1024 bytes (default is 768).
// You could increase or decrease this value to avoid truncated packets
// or to improve memory footprint respectively.
//
// Please also note that a large UDP packet might be truncated or
// dropped by your router no matter how you configured this option.
// In UDP, a packet is truncated or dropped if it is larger than a
// certain size, depending on router configuration. IPv4 routers
// truncate and IPv6 routers drop a large packet. That's why it is
// safe to send small packets in UDP.
b.setOption(
"receiveBufferSizePredictorFactory",
new FixedReceiveBufferSizePredictorFactory(1024));
// Bind to the port and start the service.
b.bind(new InetSocketAddress(port));
b.bind().sync().channel().closeFuture().await();
} finally {
b.shutdown();
}
}
public static void main(String[] args) throws Exception {

View File

@ -15,14 +15,15 @@
*/
package io.netty.example.qotm;
import io.netty.buffer.ChannelBuffers;
import io.netty.channel.ChannelInboundHandlerContext;
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
import io.netty.channel.socket.DatagramPacket;
import io.netty.util.CharsetUtil;
import java.util.Random;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ExceptionEvent;
import io.netty.channel.MessageEvent;
import io.netty.channel.SimpleChannelUpstreamHandler;
public class QuoteOfTheMomentServerHandler extends SimpleChannelUpstreamHandler {
public class QuoteOfTheMomentServerHandler extends ChannelInboundMessageHandlerAdapter<DatagramPacket> {
private static final Random random = new Random();
@ -43,18 +44,21 @@ public class QuoteOfTheMomentServerHandler extends SimpleChannelUpstreamHandler
}
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
public void messageReceived(
ChannelInboundHandlerContext<DatagramPacket> ctx, DatagramPacket msg)
throws Exception {
String msg = (String) e.getMessage();
if (msg.equals("QOTM?")) {
e.channel().write("QOTM: " + nextQuote(), e.getRemoteAddress());
if (msg.data().toString(CharsetUtil.UTF_8).equals("QOTM?")) {
ctx.write(new DatagramPacket(
ChannelBuffers.copiedBuffer("QOTM: " + nextQuote(), CharsetUtil.UTF_8),
msg.remoteAddress()));
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
public void exceptionCaught(
ChannelInboundHandlerContext<DatagramPacket> ctx, Throwable cause)
throws Exception {
e.cause().printStackTrace();
cause.printStackTrace();
// We don't close the channel because we can keep serving requests.
}
}

View File

@ -42,29 +42,29 @@ class DefaultNioDatagramChannelConfig extends DefaultDatagramChannelConfig {
}
Object ipMulticastIf = null;
Method getOption = null;
Method setOption = null;
if (socketOptionType != null) {
try {
ipMulticastIf = Class.forName("java.net.StandardSocketOptions", true, classLoader).getDeclaredField("IP_MULTICAST_IF").get(null);
} catch (Exception e) {
throw new Error("cannot locate the IP_MULTICAST_IF field", e);
}
try {
getOption = DatagramChannel.class.getDeclaredMethod("getOption", socketOptionType);
} catch (Exception e) {
throw new Error("cannot locate the getOption() method", e);
}
try {
setOption = DatagramChannel.class.getDeclaredMethod("setOption", socketOptionType, Object.class);
} catch (Exception e) {
throw new Error("cannot locate the setOption() method", e);
}
}
IP_MULTICAST_IF = ipMulticastIf;
Method getOption;
try {
getOption = DatagramChannel.class.getDeclaredMethod("getOption", socketOptionType);
} catch (Exception e) {
throw new Error("cannot locate the getOption() method", e);
}
GET_OPTION = getOption;
Method setOption;
try {
setOption = DatagramChannel.class.getDeclaredMethod("setOption", socketOptionType, Object.class);
} catch (Exception e) {
throw new Error("cannot locate the setOption() method", e);
}
SET_OPTION = setOption;
}

View File

@ -126,7 +126,7 @@ public final class NioDatagramChannel extends AbstractNioChannel implements io.n
@Override
protected void doBind(SocketAddress localAddress) throws Exception {
javaChannel().bind(localAddress);
javaChannel().socket().bind(localAddress);
selectionKey().interestOps(SelectionKey.OP_READ);
}
@ -174,6 +174,7 @@ public final class NioDatagramChannel extends AbstractNioChannel implements io.n
@Override
protected int doRead(ChannelBufferHolder<Object> buf) throws Exception {
DatagramChannel ch = javaChannel();
// FIXME: Make this configurable.
ByteBuffer data = ByteBuffer.allocate(1024);
InetSocketAddress remoteAddress = (InetSocketAddress) ch.receive(data);
if (remoteAddress == null) {

View File

@ -1,117 +0,0 @@
/*
* Copyright 2011 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.bootstrap;
import static org.junit.Assert.*;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import org.junit.Test;
/**
* A test to make sure that a bootstrap can recognize ordered maps
*/
public class BootstrapOrderedMapTest {
@Test
public void shouldReturnTrueIfLinkedHashMap() {
assertTrue(Bootstrap.isOrderedMap(new LinkedHashMap<String, String>()));
}
@Test
public void shouldReturnTrueIfMapImplementsOrderedMap() {
assertTrue(Bootstrap.isOrderedMap(new DummyOrderedMap<String, String>()));
}
@Test
public void shouldReturnFalseIfMapHasNoDefaultConstructor() {
assertFalse(Bootstrap.isOrderedMap(
new MapWithoutDefaultConstructor<String, String>(
new HashMap<String, String>())));
}
@Test
public void shouldReturnFalseIfMapIsNotOrdered() {
assertFalse(Bootstrap.isOrderedMap(new HashMap<String, String>()));
}
@Test
public void shouldReturnTrueIfMapIsOrdered() {
assertTrue(Bootstrap.isOrderedMap(new UnknownOrderedMap<String, String>()));
}
interface OrderedMap {
// A tag interface
}
static class DummyOrderedMap<K,V> extends AbstractMap<K, V> implements OrderedMap {
private final Map<K, V> map = new HashMap<K, V>();
@Override
public Set<Entry<K, V>> entrySet() {
return map.entrySet();
}
}
static class MapWithoutDefaultConstructor<K, V> extends AbstractMap<K, V> {
private final Map<K, V> map;
MapWithoutDefaultConstructor(Map<K, V> map) {
this.map = map;
}
@Override
public Set<Entry<K, V>> entrySet() {
return map.entrySet();
}
}
static class UnknownOrderedMap<K,V> extends AbstractMap<K, V> {
private final Map<K, V> map = new LinkedHashMap<K, V>();
@Override
public boolean containsKey(Object key) {
return map.containsKey(key);
}
@Override
public int size() {
return map.size();
}
@Override
public V put(K key, V value) {
return map.put(key, value);
}
@Override
public Set<K> keySet() {
return map.keySet();
}
@Override
public Set<Entry<K, V>> entrySet() {
return map.entrySet();
}
}
}

View File

@ -1,359 +0,0 @@
/*
* Copyright 2011 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.bootstrap;
import static org.easymock.EasyMock.*;
import static org.junit.Assert.*;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Map.Entry;
import io.netty.channel.ChannelDownstreamHandler;
import io.netty.channel.ChannelEvent;
import io.netty.channel.ChannelFactory;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPipelineFactory;
import io.netty.channel.ChannelUpstreamHandler;
import org.junit.Test;
/**
* A bootstrap test
*/
public class BootstrapTest {
/**
* Tests to make sure that a new bootstrap should not return null, but an IllegalStateException
*/
@Test(expected = IllegalStateException.class)
public void shouldNotReturnNullFactory() {
new Bootstrap().getFactory();
}
/**
* Tests to make sure that a new bootstrap cannot have it's channel factory changed
*/
@Test(expected = IllegalStateException.class)
public void shouldNotAllowInitialFactoryToChange() {
new Bootstrap(createMock(ChannelFactory.class)).setFactory(createMock(ChannelFactory.class));
}
/**
* Tests to make sure that a bootstrap's factory can only be set once and not reset after that
*/
@Test
public void shouldNotAllowFactoryToChangeMoreThanOnce() {
//Initialize a new bootstrap
Bootstrap b = new Bootstrap();
//Create a mock-up ChannelFactory
ChannelFactory f = createMock(ChannelFactory.class);
//Set the bootstrap's channel factory
b.setFactory(f);
//Assert that both f and the bootstrap's factory are the same
assertSame(f, b.getFactory());
//Start a try block
try {
//Attempt to set the bootstrap's channel factory again
b.setFactory(createMock(ChannelFactory.class));
//Fail if no exception is thrown
fail();
} catch (IllegalStateException e) {
// Success.
}
}
/**
* Tests to make sure that setFactory does not accept null as a parameter
*/
@Test(expected = NullPointerException.class)
public void shouldNotAllowNullFactory() {
new Bootstrap().setFactory(null);
}
/**
* Tests to make sure that a new bootstrap's default pipeline is not null
*/
@Test
public void shouldHaveNonNullInitialPipeline() {
assertNotNull(new Bootstrap().getPipeline());
}
/**
* Tests to make sure a bootstrap's pipeline cannot be set to null
*/
@Test(expected = NullPointerException.class)
public void shouldNotAllowNullPipeline() {
new Bootstrap().setPipeline(null);
}
/**
* Tests to make sure a bootstrap cannot have it's pipeline (map) set to null
*/
@Test(expected = NullPointerException.class)
public void shouldNotAllowNullPipelineMap() {
new Bootstrap().setPipelineAsMap(null);
}
/**
* Tests to make sure a bootstrap's initial pipeline factory is not null
*/
@Test
public void shouldHaveNonNullInitialPipelineFactory() {
assertNotNull(new Bootstrap().getPipelineFactory());
}
/**
* Tests to make sure that a bootstrap's pipeline factory changes if a new pipeline is set
*/
@Test
public void shouldUpdatePipelineFactoryIfPipelineIsSet() {
//Make a new bootstrap
Bootstrap b = new Bootstrap();
//Get the initial pipeline factory
ChannelPipelineFactory oldPipelineFactory = b.getPipelineFactory();
//Set a new pipeline
b.setPipeline(createMock(ChannelPipeline.class));
//Assert that the new pipeline factory is not the same as the initial one
assertNotSame(oldPipelineFactory, b.getPipelineFactory());
}
/**
* Tests to make sure a bootstrap's pipeline is not returned when a pipeline factory is explicitly set
*/
@Test(expected = IllegalStateException.class)
public void shouldNotReturnPipelineWhenPipelineFactoryIsSetByUser() {
Bootstrap b = new Bootstrap();
b.setPipelineFactory(createMock(ChannelPipelineFactory.class));
b.getPipeline();
}
/**
* Tests to make sure a bootstrap's pipeline map is not returned when a pipeline factory is explicitly set
*/
@Test(expected = IllegalStateException.class)
public void shouldNotReturnPipelineMapWhenPipelineFactoryIsSetByUser() {
Bootstrap b = new Bootstrap();
b.setPipelineFactory(createMock(ChannelPipelineFactory.class));
b.getPipelineAsMap();
}
/**
* Tests to make sure a bootstrap's pipeline factory cannot be set to null
*/
@Test(expected = NullPointerException.class)
public void shouldNotAllowNullPipelineFactory() {
new Bootstrap().setPipelineFactory(null);
}
/**
* Tests to make sure a new bootstrap's pipeline map is empty
*/
@Test
public void shouldHaveInitialEmptyPipelineMap() {
assertTrue(new Bootstrap().getPipelineAsMap().isEmpty());
}
/**
* Tests to make sure that a buffer's pipeline map is ordered
*/
@Test
public void shouldReturnOrderedPipelineMap() {
Bootstrap b = new Bootstrap();
ChannelPipeline p = b.getPipeline();
p.addLast("a", new DummyHandler());
p.addLast("b", new DummyHandler());
p.addLast("c", new DummyHandler());
p.addLast("d", new DummyHandler());
Iterator<Entry<String, ChannelHandler>> m =
b.getPipelineAsMap().entrySet().iterator();
Entry<String, ChannelHandler> e;
e = m.next();
assertEquals("a", e.getKey());
assertSame(p.get("a"), e.getValue());
e = m.next();
assertEquals("b", e.getKey());
assertSame(p.get("b"), e.getValue());
e = m.next();
assertEquals("c", e.getKey());
assertSame(p.get("c"), e.getValue());
e = m.next();
assertEquals("d", e.getKey());
assertSame(p.get("d"), e.getValue());
assertFalse(m.hasNext());
}
/**
* Tests to make sure that a buffer's pipeline map cannot be set to an unordered map
*/
@Test(expected = IllegalArgumentException.class)
public void shouldNotAllowUnorderedPipelineMap() {
Map<String, ChannelHandler> m = new HashMap<String, ChannelHandler>();
m.put("a", new DummyHandler());
m.put("b", new DummyHandler());
m.put("c", new DummyHandler());
m.put("d", new DummyHandler());
Bootstrap b = new Bootstrap();
b.setPipelineAsMap(m);
}
/**
* Tests to make sure a buffer's pipeline is ordered when it is set from a map
*/
@Test
public void shouldHaveOrderedPipelineWhenSetFromMap() {
Map<String, ChannelHandler> m = new LinkedHashMap<String, ChannelHandler>();
m.put("a", new DummyHandler());
m.put("b", new DummyHandler());
m.put("c", new DummyHandler());
m.put("d", new DummyHandler());
Bootstrap b = new Bootstrap();
b.setPipelineAsMap(m);
ChannelPipeline p = b.getPipeline();
assertSame(p.first(), m.get("a"));
assertEquals("a", p.context(p.first()).name());
p.removeFirst();
assertSame(p.first(), m.get("b"));
assertEquals("b", p.context(p.first()).name());
p.removeFirst();
assertSame(p.first(), m.get("c"));
assertEquals("c", p.context(p.first()).name());
p.removeFirst();
assertSame(p.first(), m.get("d"));
assertEquals("d", p.context(p.first()).name());
p.removeFirst();
try {
p.removeFirst();
fail();
} catch (NoSuchElementException e) {
// Success.
}
}
/**
* Tests to make sure that a new bootstrap should not have any options set
*/
@Test
public void shouldHaveInitialEmptyOptionMap() {
assertTrue(new Bootstrap().getOptions().isEmpty());
}
/**
* Tests to make sure that a bootstrap's option map updates in real time
*/
@Test
public void shouldUpdateOptionMapAsRequested1() {
Bootstrap b = new Bootstrap();
b.setOption("s", "x");
b.setOption("b", true);
b.setOption("i", 42);
Map<String, Object> o = b.getOptions();
assertEquals(3, o.size());
assertEquals("x", o.get("s"));
assertEquals(true, o.get("b"));
assertEquals(42, o.get("i"));
}
/**
* Tests to make sure that a bootstrap's option map updates in real time
*/
@Test
public void shouldUpdateOptionMapAsRequested2() {
Bootstrap b = new Bootstrap();
Map<String, Object> o1 = new HashMap<String, Object>();
o1.put("s", "x");
o1.put("b", true);
o1.put("i", 42);
b.setOptions(o1);
Map<String, Object> o2 = b.getOptions();
assertEquals(3, o2.size());
assertEquals("x", o2.get("s"));
assertEquals(true, o2.get("b"));
assertEquals(42, o2.get("i"));
assertNotSame(o1, o2);
assertEquals(o1, o2);
}
/**
* Tests to make sure that an option is removed from a buffer's options when the option is set to null
*/
@Test
public void shouldRemoveOptionIfValueIsNull() {
Bootstrap b = new Bootstrap();
b.setOption("s", "x");
assertEquals("x", b.getOption("s"));
b.setOption("s", null);
assertNull(b.getOption("s"));
assertTrue(b.getOptions().isEmpty());
}
/**
* Tests to make sure that a bootstrap can't accept null as a parameter of getOption(key)
*/
@Test(expected = NullPointerException.class)
public void shouldNotAllowNullOptionKeyOnGet() {
new Bootstrap().getOption(null);
}
/**
* Tests to make sure a bootstrap can't accept a null key when using setOption(key, value)
*/
@Test(expected = NullPointerException.class)
public void shouldNotAllowNullOptionKeyOnSet() {
new Bootstrap().setOption(null, "x");
}
/**
* Tests to make sure that a boostrap can't accept a null option map
*/
@Test(expected = NullPointerException.class)
public void shouldNotAllowNullOptionMap() {
new Bootstrap().setOptions(null);
}
private class DummyHandler implements ChannelUpstreamHandler, ChannelDownstreamHandler {
public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e)
throws Exception {
ctx.sendUpstream(e);
}
public void handleDownstream(ChannelHandlerContext ctx, ChannelEvent e)
throws Exception {
ctx.sendDownstream(e);
}
}
}