/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.transport.socket;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertNotNull;
import static org.testng.AssertJUnit.assertTrue;
import static org.testng.AssertJUnit.fail;
import java.net.InetAddress;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import org.fudgemsg.FudgeContext;
import org.fudgemsg.FudgeMsg;
import org.fudgemsg.FudgeMsgEnvelope;
import org.fudgemsg.MutableFudgeMsg;
import org.testng.annotations.Test;
import com.opengamma.transport.CollectingFudgeMessageReceiver;
import com.opengamma.transport.FudgeConnection;
import com.opengamma.transport.FudgeConnectionReceiver;
import com.opengamma.transport.FudgeMessageReceiver;
import com.opengamma.transport.FudgeMessageSender;
import com.opengamma.util.test.TestGroup;
import com.opengamma.util.test.Timeout;
/**
* Tests the SocketFudgeConnection and ServerSocketFudgeConnectionReceiver classes
*/
@Test(groups = TestGroup.INTEGRATION, singleThreaded = true)
public class SocketFudgeConnectionConduitTest {
private final AtomicInteger _counter = new AtomicInteger();
private FudgeMsg createMessage() {
final MutableFudgeMsg message = FudgeContext.GLOBAL_DEFAULT.newMessage();
message.add("counter", _counter.incrementAndGet());
return message;
}
public void simpleTest() throws Exception {
final FudgeMsg testMessage1 = createMessage();
final FudgeMsg testMessage2 = createMessage();
// receiver will respond to testMessage1 with testMessage2
final FudgeConnectionReceiver serverReceiver = new FudgeConnectionReceiver() {
@Override
public void connectionReceived(FudgeContext fudgeContext, FudgeMsgEnvelope message, FudgeConnection connection) {
assertNotNull(fudgeContext);
assertNotNull(message);
assertNotNull(connection);
assertEquals(testMessage1, message.getMessage());
connection.getFudgeMessageSender().send(testMessage2);
}
};
final ServerSocketFudgeConnectionReceiver server = new ServerSocketFudgeConnectionReceiver(FudgeContext.GLOBAL_DEFAULT, serverReceiver);
server.start();
final SocketFudgeConnection client = new SocketFudgeConnection(FudgeContext.GLOBAL_DEFAULT);
client.setInetAddress(InetAddress.getLocalHost());
client.setPortNumber(server.getPortNumber());
// connect and send testMessage1, then verify that testMessage2 was sent back
final CollectingFudgeMessageReceiver clientReceiver = new CollectingFudgeMessageReceiver();
client.setFudgeMessageReceiver(clientReceiver);
client.getFudgeMessageSender().send(testMessage1);
final FudgeMsgEnvelope envelope = clientReceiver.waitForMessage(Timeout.standardTimeoutMillis());
assertNotNull(envelope);
assertEquals(testMessage2, envelope.getMessage());
client.stop();
server.stop();
}
public void messageReceiverTest() throws Exception {
final FudgeMsg testMessage1 = createMessage();
final FudgeMsg testMessage2 = createMessage();
final FudgeMsg testMessage3 = createMessage();
final CollectingFudgeMessageReceiver message3Receiver = new CollectingFudgeMessageReceiver();
// receiver will ignore testMessage1
// after receiving testMessage2, will set the message receiver on the connection
// it shouldn't be called again - messages should be dispatched to the connection's receiver
final FudgeConnectionReceiver serverReceiver = new FudgeConnectionReceiver() {
private int _count;
@Override
public void connectionReceived(final FudgeContext fudgeContext, final FudgeMsgEnvelope message, final FudgeConnection connection) {
assertNotNull(fudgeContext);
assertNotNull(message);
assertNotNull(connection);
switch (_count++) {
case 0:
assertEquals(testMessage1, message.getMessage());
break;
case 1:
assertEquals(testMessage2, message.getMessage());
connection.setFudgeMessageReceiver(message3Receiver);
break;
default:
fail("Shouldn't have been called a third time");
break;
}
}
};
final ServerSocketFudgeConnectionReceiver server = new ServerSocketFudgeConnectionReceiver(FudgeContext.GLOBAL_DEFAULT, serverReceiver);
server.start();
final SocketFudgeConnection client = new SocketFudgeConnection(FudgeContext.GLOBAL_DEFAULT);
client.setInetAddress (InetAddress.getLocalHost ());
client.setPortNumber (server.getPortNumber ());
// send messages 1, 2, 3 and verify 3 went to the test receiver
client.getFudgeMessageSender().send(testMessage1);
assertTrue(message3Receiver.getMessages().isEmpty());
client.getFudgeMessageSender().send(testMessage2);
assertTrue(message3Receiver.getMessages().isEmpty());
client.getFudgeMessageSender().send(testMessage3);
final FudgeMsgEnvelope envelope = message3Receiver.waitForMessage(Timeout.standardTimeoutMillis());
assertNotNull(envelope);
assertEquals(testMessage3, envelope.getMessage());
server.stop();
client.stop();
}
private class MessageReadWrite extends Thread implements FudgeMessageReceiver {
private static final int NUM_MESSAGES = 1000;
private FudgeMessageSender _sender;
private int _received;
@Override
public void run() {
for (int i = 0; i < NUM_MESSAGES; i++) {
final FudgeMsg message = createMessage();
_sender.send(message);
}
}
@Override
public synchronized void messageReceived(FudgeContext fudgeContext, FudgeMsgEnvelope msgEnvelope) {
_received++;
if (_received == NUM_MESSAGES) {
notify();
} else if (_received > NUM_MESSAGES) {
fail("Too many messages received");
}
}
public synchronized boolean waitForMessages() throws InterruptedException {
final long period = Timeout.standardTimeoutMillis();
final long timeout = System.currentTimeMillis() + period;
while ((_received < NUM_MESSAGES) && (System.currentTimeMillis() < timeout)) {
wait(period);
}
return _received == NUM_MESSAGES;
}
}
@Test(invocationCount = 5, successPercentage = 19)
public void parallelIOTest() throws Exception {
final MessageReadWrite serverThread = new MessageReadWrite();
// receiver will attach the serverThread to the connection and start the thread
final FudgeConnectionReceiver serverReceiver = new FudgeConnectionReceiver() {
@Override
public void connectionReceived(final FudgeContext fudgeContext, final FudgeMsgEnvelope envelope, final FudgeConnection connection) {
// pass on the first message
serverThread.messageReceived(fudgeContext, envelope);
// and let it receive all others as they arrive
serverThread._sender = connection.getFudgeMessageSender();
connection.setFudgeMessageReceiver(serverThread);
serverThread.start();
}
};
final ServerSocketFudgeConnectionReceiver server = new ServerSocketFudgeConnectionReceiver(FudgeContext.GLOBAL_DEFAULT, serverReceiver);
server.start();
final SocketFudgeConnection client = new SocketFudgeConnection(FudgeContext.GLOBAL_DEFAULT);
client.setInetAddress(InetAddress.getLocalHost());
client.setPortNumber(server.getPortNumber());
// client thread will send a stream of messages, and receive those back from the server
final MessageReadWrite clientThread = new MessageReadWrite();
clientThread._sender = client.getFudgeMessageSender();
client.setFudgeMessageReceiver(clientThread);
clientThread.start();
// wait to see if both have behaved
assertTrue(serverThread.waitForMessages());
assertTrue(clientThread.waitForMessages());
server.stop();
client.stop();
}
private int[] parallelSendTest(final ExecutorService executorClient, final ExecutorService executorServer, final AtomicInteger concurrencyMax) throws Exception {
final FudgeConnectionReceiver serverReceiver = new FudgeConnectionReceiver() {
@Override
public void connectionReceived(final FudgeContext fudgeContext, final FudgeMsgEnvelope envelope, final FudgeConnection connection) {
connection.setFudgeMessageReceiver(new FudgeMessageReceiver() {
@Override
public void messageReceived(FudgeContext fudgeContext, FudgeMsgEnvelope msgEnvelope) {
MutableFudgeMsg message = fudgeContext.newMessage();
message.add("foo", 1);
connection.getFudgeMessageSender().send(message);
try {
Thread.sleep(Timeout.standardTimeoutMillis());
} catch (InterruptedException e) {
}
message = fudgeContext.newMessage();
message.add("foo", 2);
connection.getFudgeMessageSender().send(message);
}
});
}
};
final ServerSocketFudgeConnectionReceiver server = (executorServer != null) ? new ServerSocketFudgeConnectionReceiver(FudgeContext.GLOBAL_DEFAULT, serverReceiver, executorServer)
: new ServerSocketFudgeConnectionReceiver(FudgeContext.GLOBAL_DEFAULT, serverReceiver);
server.start();
final SocketFudgeConnection client = (executorClient != null) ? new SocketFudgeConnection(FudgeContext.GLOBAL_DEFAULT, executorClient) : new SocketFudgeConnection(FudgeContext.GLOBAL_DEFAULT);
client.setInetAddress(InetAddress.getLocalHost());
client.setPortNumber(server.getPortNumber());
final CollectingFudgeMessageReceiver responses = new CollectingFudgeMessageReceiver () {
private final AtomicInteger _concurrency = new AtomicInteger (0);
@Override
public void messageReceived (final FudgeContext fudgeContext, final FudgeMsgEnvelope envelope) {
final int concurrency = _concurrency.incrementAndGet ();
if (concurrency > concurrencyMax.get ()) {
concurrencyMax.set (concurrency);
}
try {
Thread.sleep(Timeout.standardTimeoutMillis() / 2L);
} catch (InterruptedException e) {
}
_concurrency.decrementAndGet ();
super.messageReceived (fudgeContext, envelope);
}
};
client.setFudgeMessageReceiver(responses);
client.getFudgeMessageSender().send(FudgeContext.EMPTY_MESSAGE);
client.getFudgeMessageSender().send(FudgeContext.EMPTY_MESSAGE);
client.getFudgeMessageSender().send(FudgeContext.EMPTY_MESSAGE);
final int[] result = new int[4];
for (int i = 0; i < 4; i++) {
final FudgeMsgEnvelope envelope = responses.waitForMessage(Timeout.standardTimeoutMillis() * 2L);
assertNotNull (envelope);
result[i] = envelope.getMessage().getInt("foo");
}
return result;
}
public void parallelSendTest_single_single() throws Exception {
final AtomicInteger concurrencyMax = new AtomicInteger(0);
final int[] result = parallelSendTest(null, null, concurrencyMax);
assertEquals(1, concurrencyMax.get());
assertEquals(1, result[0]);
assertEquals(2, result[1]);
assertEquals(1, result[2]);
assertEquals(2, result[3]);
}
public void parallelSendTest_multi_single() throws Exception {
final AtomicInteger concurrencyMax = new AtomicInteger(0);
final int[] result = parallelSendTest(Executors.newCachedThreadPool(), null, concurrencyMax);
assertEquals(2, concurrencyMax.get());
assertEquals(1, result[0]);
// The server might send the messages in order, but the client can receive them out of order
if (result[1] == 2) {
assertEquals(1, result[2]);
} else if (result[1] == 1) {
assertEquals(2, result[2]);
} else {
fail();
}
assertEquals(2, result[3]);
}
public void parallelSendTest_single_multi() throws Exception {
final AtomicInteger concurrencyMax = new AtomicInteger(0);
final int[] result = parallelSendTest(null, Executors.newCachedThreadPool(), concurrencyMax);
assertEquals(1, concurrencyMax.get());
assertEquals(1, result[0]);
assertEquals(1, result[1]);
assertEquals(2, result[2]);
assertEquals(2, result[3]);
}
public void parallelSendTest_multi_multi() throws Exception {
final AtomicInteger concurrencyMax = new AtomicInteger(0);
final int[] result = parallelSendTest(Executors.newCachedThreadPool(), Executors.newCachedThreadPool(), concurrencyMax);
assertEquals(2, concurrencyMax.get());
assertEquals(1, result[0]);
assertEquals(1, result[1]);
assertEquals(2, result[2]);
assertEquals(2, result[3]);
}
}