package org.marketcetera.modules.remote.emitter;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.io.NotSerializableException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.ConnectException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import javax.jms.JMSException;
import javax.security.auth.login.LoginException;
import org.apache.log4j.Level;
import org.junit.Test;
import org.marketcetera.client.ClientTest;
import org.marketcetera.core.Pair;
import org.marketcetera.event.EventTestBase;
import org.marketcetera.module.CopierModuleFactory;
import org.marketcetera.module.DataFlowID;
import org.marketcetera.module.DataRequest;
import org.marketcetera.module.ExpectedFailure;
import org.marketcetera.modules.remote.receiver.ReceiverFactory;
import org.marketcetera.trade.Equity;
import org.marketcetera.trade.ExecutionReport;
import org.marketcetera.trade.FIXOrder;
import org.marketcetera.trade.OrderCancel;
import org.marketcetera.trade.OrderCancelReject;
import org.marketcetera.trade.OrderReplace;
import org.marketcetera.trade.OrderSingle;
import org.marketcetera.trade.TypesTestBase;
import org.marketcetera.util.misc.ClassVersion;
import org.marketcetera.util.test.LogTestAssist;
import org.springframework.beans.factory.BeanCreationException;
/* $License$ */
/**
* Tests {@link RemoteDataEmitter}.
*
* @author anshul@marketcetera.com
* @version $Id: RemoteDataEmitterTest.java 16614 2013-07-03 22:35:32Z colin $
* @since 2.0.0
*/
@ClassVersion("$Id: RemoteDataEmitterTest.java 16614 2013-07-03 22:35:32Z colin $")
public class RemoteDataEmitterTest extends RemoteEmitterTestBase {
/**
* Tests for null URL and adapter values.
*
* @throws Exception if there were unexpected failures.
*/
@Test
public void nulls() throws Exception {
//null URL
new ExpectedFailure<NullPointerException>(){
@Override
protected void run() throws Exception {
new RemoteDataEmitter(null, "bla", "bla", new MyAdapter());
}
};
//null adapter
new ExpectedFailure<NullPointerException>(){
@Override
protected void run() throws Exception {
new RemoteDataEmitter(DEFAULT_URL, "bla", "bla", null);
}
};
//null user name
new ExpectedFailure<NullPointerException>(){
@Override
protected void run() throws Exception {
new RemoteDataEmitter(DEFAULT_URL, null, "bla", new MyAdapter());
}
};
//null password
new ExpectedFailure<NullPointerException>(){
@Override
protected void run() throws Exception {
new RemoteDataEmitter(DEFAULT_URL, "bla", null, new MyAdapter());
}
};
}
/**
* Verifies connection failure when invalid address is specified.
*
* @throws Exception if there were unexpected failures.
*/
@Test
public void connectFailure() throws Exception {
final MyAdapter adapter = new MyAdapter();
Throwable jmsException = new ExpectedFailure<Exception>(){
@Override
protected void run() throws Exception {
new RemoteDataEmitter("tcp://localhost:101", "admin", "admin", adapter);
}
}.getException().getCause().getCause();
assertTrue(jmsException.toString(),
jmsException instanceof JMSException);
assertTrue(jmsException.getCause().toString(),
jmsException.getCause() instanceof ConnectException);
assertTrue(adapter.hasNoObjects());
assertTrue(adapter.hasNoStatus());
//invalid URL
new ExpectedFailure<Exception>() {
@Override
protected void run() throws Exception {
new RemoteDataEmitter("this is not a URL", "admin", "admin", adapter);
}
};
assertTrue(adapter.hasNoObjects());
assertTrue(adapter.hasNoStatus());
}
/**
* Connects to the receiver and disconnects
*
* @throws Exception if there were unexpected failures.
*/
@Test
public void connectAndClose() throws Exception {
//Init manager to create the receiver.
initManager();
//Now connect to it
MyAdapter adapter = new MyAdapter();
RemoteDataEmitter emitter = new RemoteDataEmitter(DEFAULT_URL,
DEFAULT_CREDENTIAL, DEFAULT_CREDENTIAL, adapter);
//Test whatever we can
assertTrue(emitter.isConnected());
assertNull(emitter.getLastFailure());
assertTrue(adapter.toString(), adapter.hasNoObjects());
assertFalse(adapter.toString(), adapter.hasNoStatus());
assertEquals(new Pair<Boolean,Boolean>(false,true), adapter.getNextStatus());
//Now close and test things
emitter.close();
assertFalse(emitter.isConnected());
assertNull(emitter.getLastFailure());
assertTrue(adapter.toString(), adapter.hasNoObjects());
assertFalse(adapter.toString(), adapter.hasNoStatus());
assertEquals(new Pair<Boolean,Boolean>(true,false), adapter.getNextStatus());
//Do close again and verify that it makes no difference
emitter.close();
assertFalse(emitter.isConnected());
assertNull(emitter.getLastFailure());
assertTrue(adapter.toString(), adapter.hasNoObjects());
assertTrue(adapter.toString(), adapter.hasNoStatus());
}
/**
* Tests authentication failures when connecting to the strategy agent.
*
* @throws Exception if there were unexpected failures.
*/
@Test
public void authFailure() throws Exception {
//Init manager to create the receiver.
initManager();
//Now verify different auth failures
//empty user
verifyAuthFailure("", DEFAULT_CREDENTIAL);
//empty password
verifyAuthFailure(DEFAULT_CREDENTIAL, "");
//invalid user name password combination
verifyAuthFailure("yes", "no");
}
/**
* Receives data from the receiver.
*
* @throws Exception if there were unexpected failures.
*/
@Test
public void receiveData() throws Exception {
//Init manager to create the receiver.
initManager();
//Now connect to it
MyAdapter adapter = new MyAdapter();
RemoteDataEmitter emitter = new RemoteDataEmitter(DEFAULT_URL,
DEFAULT_CREDENTIAL, DEFAULT_CREDENTIAL, adapter);
//Test whatever we can
assertTrue(emitter.isConnected());
assertFalse(adapter.toString(), adapter.hasNoStatus());
assertEquals(new Pair<Boolean,Boolean>(false,true), adapter.getNextStatus());
//Now create a data flow to supply objects to the receiver
//The data to send
Object [] data = {
EventTestBase.generateEquityAskEvent(1, 2, new Equity("asym"), "ex", BigDecimal.ONE, BigDecimal.TEN),
EventTestBase.generateEquityBidEvent(3, 4, new Equity("bsym"), "ex", BigDecimal.ONE, BigDecimal.TEN),
EventTestBase.generateEquityTradeEvent(5, 6, new Equity("csym"), "ex", BigDecimal.ONE, BigDecimal.TEN),
new NonSerializable(),
ClientTest.createOrderSingle(),
ClientTest.createOrderReplace(),
ClientTest.createOrderCancel(),
ClientTest.createOrderFIX(),
ClientTest.createCancelReject(),
ClientTest.createExecutionReport(),
org.marketcetera.core.notifications.Notification.high(
"Subject", "body", "test.notification"),
BigInteger.ONE,
"Test String"
};
//Now setup a data flow into the receiver.
DataFlowID rFlowID = mManager.createDataFlow(new DataRequest[]{
new DataRequest(CopierModuleFactory.INSTANCE_URN, data),
new DataRequest(ReceiverFactory.INSTANCE_URN)
}, false);
Object actual;
for(Object expected: data) {
if(expected instanceof NonSerializable) {
//non serializable object shouldn't have been transmitted
continue;
}
actual = adapter.getNextObject();
if(expected instanceof OrderSingle) {
TypesTestBase.assertOrderSingleEquals((OrderSingle)expected,
(OrderSingle)actual);
} else if(expected instanceof OrderReplace) {
TypesTestBase.assertOrderReplaceEquals((OrderReplace)expected,
(OrderReplace)actual);
} else if(expected instanceof OrderCancel) {
TypesTestBase.assertOrderCancelEquals((OrderCancel)expected,
(OrderCancel)actual);
} else if(expected instanceof FIXOrder) {
TypesTestBase.assertOrderFIXEquals((FIXOrder)expected,
(FIXOrder)actual);
} else if(expected instanceof OrderCancelReject) {
TypesTestBase.assertCancelRejectEquals((OrderCancelReject)expected,
(OrderCancelReject)actual);
} else if(expected instanceof ExecutionReport) {
TypesTestBase.assertExecReportEquals((ExecutionReport)expected,
(ExecutionReport)actual);
} else if(expected instanceof org.marketcetera.core.notifications.Notification) {
assertEquals(expected.toString(), actual.toString());
} else {
assertEquals(expected, actual);
}
}
//Verify we have no extra objects
assertTrue(adapter.toString(), adapter.hasNoObjects());
//Verify we didn't get any failures etc.
assertNull(emitter.getLastFailure());
assertTrue(adapter.toString(), adapter.hasNoStatus());
mManager.cancel(rFlowID);
}
/**
* Tests connection status notifications when the receiver disconnects.
*
* @throws Exception if there were unexpected errors.
*/
@Test
public void connectionStatusNotify() throws Exception {
mAssist.resetAppender();
//Init manager to create the receiver.
initManager();
//Now connect to it
MyAdapter adapter = new MyAdapter();
RemoteDataEmitter emitter = new RemoteDataEmitter(DEFAULT_URL,
DEFAULT_CREDENTIAL, DEFAULT_CREDENTIAL, adapter);
//Test whatever we can
assertTrue(emitter.isConnected());
assertNull(emitter.getLastFailure());
assertTrue(adapter.toString(), adapter.hasNoObjects());
assertFalse(adapter.toString(), adapter.hasNoStatus());
assertEquals(new Pair<Boolean,Boolean>(false,true), adapter.getNextStatus());
//Stop the receiver to terminate the connection
mManager.stop(ReceiverFactory.INSTANCE_URN);
//wait for some time for the failure to be detected
Thread.sleep(3000);
//we should've received a connection failure notification by now
assertFalse(adapter.toString(), adapter.hasNoStatus());
assertEquals(new Pair<Boolean,Boolean>(true,false), adapter.getNextStatus());
//And the emitter should be disconnected.
assertFalse(emitter.isConnected());
assertNotNull(emitter.getLastFailure());
//Now close things and start again
emitter.close();
assertFalse(emitter.isConnected());
assertNotNull(emitter.getLastFailure());
assertTrue(adapter.toString(), adapter.hasNoObjects());
assertTrue(adapter.toString(), adapter.hasNoStatus());
//Start receiver
mManager.start(ReceiverFactory.INSTANCE_URN);
//and connect to it.
adapter = new MyAdapter();
emitter = new RemoteDataEmitter(DEFAULT_URL,
DEFAULT_CREDENTIAL, DEFAULT_CREDENTIAL, adapter);
//Verify that we are connected in every sense of the word.
assertTrue(emitter.isConnected());
assertNull(emitter.getLastFailure());
assertTrue(adapter.toString(), adapter.hasNoObjects());
assertFalse(adapter.toString(), adapter.hasNoStatus());
assertEquals(new Pair<Boolean,Boolean>(false,true), adapter.getNextStatus());
//Close the connection and verify that we get a notification
emitter.close();
assertFalse(emitter.isConnected());
assertNull(emitter.getLastFailure());
assertTrue(adapter.toString(), adapter.hasNoObjects());
assertFalse(adapter.toString(), adapter.hasNoStatus());
assertEquals(new Pair<Boolean,Boolean>(true,false), adapter.getNextStatus());
}
/**
* Tests failures when deserializing objects.
*
* @throws Exception if there were unexpected errors.
*/
@Test
public void nonDeserializables() throws Exception {
//Init manager to create the receiver.
initManager();
//Now connect to it
MyAdapter adapter = new MyAdapter();
RemoteDataEmitter emitter = new RemoteDataEmitter(DEFAULT_URL,
DEFAULT_CREDENTIAL, DEFAULT_CREDENTIAL, adapter);
//Test whatever we can
assertTrue(emitter.isConnected());
assertFalse(adapter.toString(), adapter.hasNoStatus());
assertEquals(new Pair<Boolean,Boolean>(false,true), adapter.getNextStatus());
//Now create a data flow to supply objects to the receiver
//The data to send
Object [] data = {
"test",
new NonDeserializable(),
"once more"
};
//Now setup a data flow into the receiver.
DataFlowID rFlowID = mManager.createDataFlow(new DataRequest[]{
new DataRequest(CopierModuleFactory.INSTANCE_URN, data),
new DataRequest(ReceiverFactory.INSTANCE_URN)
}, false);
Object actual;
for(Object expected: data) {
if(expected instanceof NonDeserializable) {
//non serializable object shouldn't have been transmitted
continue;
}
actual = adapter.getNextObject();
assertEquals(expected, actual);
}
//Verify we have no extra objects
assertTrue(adapter.toString(), adapter.hasNoObjects());
//Verify that we did get any serialization failures etc.
assertNotNull(emitter.getLastFailure().toString(), emitter.getLastFailure());
assertTrue(emitter.getLastFailure().toString(),
emitter.getLastFailure().getCause() instanceof NotSerializableException);
//This failure should result in the client be marked as closed
assertFalse(emitter.isConnected());
//And we should see a connection status notification for that.
assertFalse(adapter.toString(), adapter.hasNoStatus());
assertEquals(new Pair<Boolean,Boolean>(true,false), adapter.getNextStatus());
mManager.cancel(rFlowID);
}
/**
* Verifies the authentication failure for the specified user name
* / password combination.
*
* @param inUsername the user name
* @param inPassword the password
*
* @throws Exception if there were unexpected failures.
*/
private void verifyAuthFailure(final String inUsername,
final String inPassword)
throws Exception {
final MyAdapter adapter = new MyAdapter();
Throwable failure = new ExpectedFailure<BeanCreationException>() {
@Override
protected void run() throws Exception {
new RemoteDataEmitter(DEFAULT_URL, inUsername, inPassword, adapter);
}
}.getException();
boolean isLoginError = false;
do {
if(failure instanceof LoginException) {
isLoginError = true;
break;
}
} while((failure = failure.getCause()) != null);
assertTrue(isLoginError);
assertTrue(adapter.hasNoObjects());
assertTrue(adapter.hasNoStatus());
}
/**
* Class for testing serialization failures.
*/
private static class NonSerializable {
}
/**
* Class for testing deserialization failures.
*/
private static class NonDeserializable implements Serializable {
private void readObject(ObjectInputStream in)throws IOException {
//cause deserialization to fail.
throw new NotSerializableException();
}
private static final long serialVersionUID = 1L;
}
/**
* Adapter implementation for testing.
*/
private static class MyAdapter implements EmitterAdapter {
@Override
public void receiveData(Object inObject) {
mData.add(inObject);
}
@Override
public void connectionStatusChanged(boolean inOldStatus, boolean inNewStatus) {
mStatus.add(new Pair<Boolean, Boolean>(inOldStatus, inNewStatus));
}
/**
* Returns the next received object. If no object is available, it
* waits until an object is received.
*
* @return the received object.
*
* @throws InterruptedException if the wait for object to arrive was
* interrupted.
*/
public Object getNextObject() throws InterruptedException {
return mData.take();
}
/**
* Returns the next status update notification value. If no such
* notification is available, the method wait untils such a
* notification is received.
*
* @return the next status notification as a pair of old and new values.
*
* @throws InterruptedException if the wait for status notification to
* arrive was interrupted.
*/
public Pair<Boolean, Boolean> getNextStatus() throws InterruptedException {
return mStatus.take();
}
/**
* Returns true if no objects have been received so far.
*
* @return true if no objects have been received so far.
*/
public boolean hasNoObjects() {
return mData.isEmpty();
}
/**
* Returns true if no status update notifications have been
* received so far.
*
* @return true, if no status update notifications have been
* received so far.
*/
public boolean hasNoStatus() {
return mStatus.isEmpty();
}
@Override
public String toString() {
return "MyAdapter{" +
"mData=" + mData +
", mStatus=" + mStatus +
'}';
}
private final BlockingQueue<Object> mData =
new LinkedBlockingQueue<Object>();
private final BlockingQueue<Pair<Boolean,Boolean>> mStatus =
new LinkedBlockingQueue<Pair<Boolean, Boolean>>();
}
private final LogTestAssist mAssist = new LogTestAssist(
RemoteDataEmitter.class.getName(), Level.WARN);
}