Java tutorial
/* * Copyright (C) 2011 Google Inc. * * Licensed 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 org.ros.internal.transport; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.netty.bootstrap.ServerBootstrap; import org.jboss.netty.buffer.HeapChannelBufferFactory; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.ChannelPipeline; import org.jboss.netty.channel.ChannelStateEvent; import org.jboss.netty.channel.ExceptionEvent; import org.jboss.netty.channel.SimpleChannelHandler; import org.jboss.netty.channel.group.ChannelGroup; import org.jboss.netty.channel.group.DefaultChannelGroup; import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.experimental.categories.Category; import org.ros.concurrent.CancellableLoop; import org.ros.concurrent.DefaultScheduledExecutorService; import org.ros.internal.message.DefaultMessageDeserializer; import org.ros.internal.message.DefaultMessageSerializer; import org.ros.internal.message.Message; import org.ros.internal.message.definition.MessageDefinitionReflectionProvider; import org.ros.internal.message.topic.TopicMessageFactory; import org.ros.internal.node.service.ServiceManager; import org.ros.internal.node.topic.TopicParticipantManager; import org.ros.internal.transport.queue.IncomingMessageQueue; import org.ros.internal.transport.queue.OutgoingMessageQueue; import org.ros.internal.transport.tcp.TcpRosClient; import org.ros.internal.transport.tcp.TcpRosClientManager; import org.ros.internal.transport.tcp.TcpRosServerPipelineFactory; import org.ros.message.MessageDefinitionProvider; import org.ros.message.MessageIdentifier; import org.ros.message.MessageListener; import io.smartspaces.testing.sizes.TestSizeLarge; import java.net.InetSocketAddress; import java.nio.ByteOrder; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; /** * @author damonkohler@google.com (Damon Kohler) */ public class MessageQueueIntegrationTest { private static final boolean DEBUG = false; private static final Log log = LogFactory.getLog(MessageQueueIntegrationTest.class); private static final int QUEUE_CAPACITY = 128; private ExecutorService executorService; private TcpRosClientManager firstTcpClientManager; private TcpRosClientManager secondTcpClientManager; private OutgoingMessageQueue<Message> outgoingMessageQueue; private IncomingMessageQueue<std_msgs.String> firstIncomingMessageQueue; private IncomingMessageQueue<std_msgs.String> secondIncomingMessageQueue; private std_msgs.String expectedMessage; private class ServerHandler extends SimpleChannelHandler { @Override public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { if (DEBUG) { log.info("Channel connected: " + e.getChannel().toString()); } Channel channel = e.getChannel(); outgoingMessageQueue.addChannel(channel); super.channelConnected(ctx, e); } @Override public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { if (DEBUG) { log.info("Channel disconnected: " + e.getChannel().toString()); } super.channelDisconnected(ctx, e); } @Override public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { if (DEBUG) { log.info("Channel exception: " + e.getChannel().toString()); } e.getChannel().close(); throw new RuntimeException(e.getCause()); } } @Before public void setup() { executorService = new DefaultScheduledExecutorService(); MessageDefinitionProvider messageDefinitionProvider = new MessageDefinitionReflectionProvider(); TopicMessageFactory topicMessageFactory = new TopicMessageFactory(messageDefinitionProvider); expectedMessage = topicMessageFactory.newFromType(std_msgs.String._TYPE); expectedMessage.setData("Would you like to play a game?"); outgoingMessageQueue = new OutgoingMessageQueue<Message>(new DefaultMessageSerializer(), executorService); firstIncomingMessageQueue = new IncomingMessageQueue<std_msgs.String>( new DefaultMessageDeserializer<std_msgs.String>(MessageIdentifier.of(std_msgs.String._TYPE), topicMessageFactory), executorService); secondIncomingMessageQueue = new IncomingMessageQueue<std_msgs.String>( new DefaultMessageDeserializer<std_msgs.String>(MessageIdentifier.of(std_msgs.String._TYPE), topicMessageFactory), executorService); firstTcpClientManager = new TcpRosClientManager(executorService); firstTcpClientManager.addNamedChannelHandler(firstIncomingMessageQueue.getMessageReceiver()); secondTcpClientManager = new TcpRosClientManager(executorService); secondTcpClientManager.addNamedChannelHandler(secondIncomingMessageQueue.getMessageReceiver()); } @After public void tearDown() { outgoingMessageQueue.shutdown(); executorService.shutdown(); } private void startRepeatingPublisher() { executorService.execute(new CancellableLoop() { @Override protected void loop() throws InterruptedException { outgoingMessageQueue.add(expectedMessage); Thread.sleep(100); } }); } private Channel buildServerChannel() { TopicParticipantManager topicParticipantManager = new TopicParticipantManager(); ServiceManager serviceManager = new ServiceManager(); NioServerSocketChannelFactory channelFactory = new NioServerSocketChannelFactory(executorService, executorService); ServerBootstrap bootstrap = new ServerBootstrap(channelFactory); bootstrap.setOption("child.bufferFactory", new HeapChannelBufferFactory(ByteOrder.LITTLE_ENDIAN)); bootstrap.setOption("child.keepAlive", true); ChannelGroup serverChannelGroup = new DefaultChannelGroup(); TcpRosServerPipelineFactory serverPipelineFactory = new TcpRosServerPipelineFactory(serverChannelGroup, topicParticipantManager, serviceManager) { @Override public ChannelPipeline getPipeline() { ChannelPipeline pipeline = super.getPipeline(); // We're not interested firstIncomingMessageQueue testing the // handshake here. Removing it means connections are established // immediately. pipeline.remove(TcpRosServerPipelineFactory.HANDSHAKE_HANDLER); pipeline.addLast("ServerHandler", new ServerHandler()); return pipeline; } }; bootstrap.setPipelineFactory(serverPipelineFactory); Channel serverChannel = bootstrap.bind(new InetSocketAddress(0)); return serverChannel; } private TcpRosClient connect(TcpRosClientManager TcpClientManager, Channel serverChannel) { return TcpClientManager.connect("Foo", serverChannel.getLocalAddress()); } private CountDownLatch expectMessage(IncomingMessageQueue<std_msgs.String> incomingMessageQueue) throws InterruptedException { final CountDownLatch latch = new CountDownLatch(1); incomingMessageQueue.addListener(new MessageListener<std_msgs.String>() { @Override public void onNewMessage(std_msgs.String message) { assertEquals(message, expectedMessage); latch.countDown(); } }, QUEUE_CAPACITY); return latch; } private void expectMessages() throws InterruptedException { CountDownLatch firstLatch = expectMessage(firstIncomingMessageQueue); CountDownLatch secondLatch = expectMessage(secondIncomingMessageQueue); assertTrue(firstLatch.await(3, TimeUnit.SECONDS)); assertTrue(secondLatch.await(3, TimeUnit.SECONDS)); } @Category(TestSizeLarge.class) @Test public void testSendAndReceiveMessage() throws InterruptedException { startRepeatingPublisher(); Channel serverChannel = buildServerChannel(); connect(firstTcpClientManager, serverChannel); connect(secondTcpClientManager, serverChannel); expectMessages(); } @Category(TestSizeLarge.class) @Test public void testSendAndReceiveLatchedMessage() throws InterruptedException { // Setting latched mode and writing a message should cause any // IncomingMessageQueues that connect in the future to receive the message. outgoingMessageQueue.setLatchMode(true); outgoingMessageQueue.add(expectedMessage); Channel serverChannel = buildServerChannel(); firstIncomingMessageQueue.setLatchMode(true); secondIncomingMessageQueue.setLatchMode(true); connect(firstTcpClientManager, serverChannel); connect(secondTcpClientManager, serverChannel); // The first set of incoming messages could either be from the // OutgoingMessageQueue latching or the Subscriber latching. This is // equivalent to waiting for the message to arrive and ensures that we've // latched it in. expectMessages(); // The second set of incoming messages can only be from the // IncomingMessageQueue latching since we only sent one message. expectMessages(); } @Category(TestSizeLarge.class) @Test public void testSendAfterIncomingQueueShutdown() throws InterruptedException { startRepeatingPublisher(); Channel serverChannel = buildServerChannel(); connect(firstTcpClientManager, serverChannel); firstTcpClientManager.shutdown(); outgoingMessageQueue.add(expectedMessage); } @Category(TestSizeLarge.class) @Test public void testSendAfterServerChannelClosed() throws InterruptedException { startRepeatingPublisher(); Channel serverChannel = buildServerChannel(); connect(firstTcpClientManager, serverChannel); assertTrue(serverChannel.close().await(1, TimeUnit.SECONDS)); outgoingMessageQueue.add(expectedMessage); } @Category(TestSizeLarge.class) @Test public void testSendAfterOutgoingQueueShutdown() throws InterruptedException { startRepeatingPublisher(); Channel serverChannel = buildServerChannel(); connect(firstTcpClientManager, serverChannel); outgoingMessageQueue.shutdown(); outgoingMessageQueue.add(expectedMessage); } }