package org.marketcetera.client; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.hasEntry; import static org.junit.Assert.*; import static org.marketcetera.trade.TypesTestBase.*; import java.beans.ExceptionListener; import java.lang.reflect.Method; import java.math.BigDecimal; import java.util.*; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicLong; import org.apache.log4j.Level; import org.hamcrest.Matchers; import org.junit.After; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.marketcetera.client.brokers.BrokerStatus; import org.marketcetera.client.jms.OrderEnvelope; import org.marketcetera.client.users.UserInfo; import org.marketcetera.core.LoggerConfiguration; import org.marketcetera.core.Util; import org.marketcetera.core.VersionInfo; import org.marketcetera.core.notifications.ServerStatusListener; import org.marketcetera.core.position.PositionKey; import org.marketcetera.core.position.PositionKeyFactory; import org.marketcetera.module.ExpectedFailure; import org.marketcetera.quickfix.FIXDataDictionaryManager; import org.marketcetera.quickfix.FIXVersion; import org.marketcetera.trade.*; import org.marketcetera.util.log.ActiveLocale; import org.marketcetera.util.log.SLF4JLoggerProxy; import org.marketcetera.util.misc.ClassVersion; import org.marketcetera.util.test.TestCaseBase; import org.marketcetera.util.ws.stateless.Node; import quickfix.field.BusinessRejectReason; import quickfix.field.ClOrdID; import quickfix.field.OrdStatus; import quickfix.field.OrigClOrdID; /* $License$ */ /** * Tests the client functionality including transmission of trades, * reports, and broker status to and from a mock server over JMS. * * @author anshul@marketcetera.com * @version $Id: ClientTest.java 16878 2014-04-14 22:55:48Z colin $ * @since 1.0.0 */ @ClassVersion("$Id: ClientTest.java 16878 2014-04-14 22:55:48Z colin $") //$NON-NLS-1$ public class ClientTest extends TestCaseBase { /* * This value can be set to a much higher value to assess * performance of jms roundtrip communications. * Keep the value greater than one to ensure that changes to * the unit test do not negatively impact the ability to * repeatedly carry out the round trips. */ private static final int NUM_REPEAT = 5; @BeforeClass public static void setup() throws Exception { LoggerConfiguration.logSetup(); FIXDataDictionaryManager.initialize(FIXVersion.FIX42, FIXVersion.FIX42.getDataDictionaryURL()); initServer(); } @AfterClass public static void closeServer() throws Exception { if (sServer != null) { sServer.close(); sServer = null; } } @After public void closeClient() { if(mClient != null) { mClient.close(); } clearAll(); } @Test public void versionTest() throws Exception { assertEquals(ClientVersion.APP_ID_VERSION, new VersionInfo(Util.getVersion(ClientVersion.APP_ID))); } @Test public void connect() throws Exception { initClient(); assertNotNull(ClientManager.getInstance()); } @Test public void connectFailure() throws Exception { //Null URL new ExpectedFailure<ConnectionException>( Messages.CONNECT_ERROR_NO_URL){ protected void run() throws Exception { ClientManager.init(new ClientParameters("you", "why".toCharArray(), null, Node.DEFAULT_HOST, Node.DEFAULT_PORT)); } }; //Empty URL new ExpectedFailure<ConnectionException>( Messages.CONNECT_ERROR_NO_URL){ protected void run() throws Exception { ClientManager.init(new ClientParameters("you", "why".toCharArray(), " ", Node.DEFAULT_HOST, Node.DEFAULT_PORT)); } }; //null user name new ExpectedFailure<ConnectionException>( Messages.CONNECT_ERROR_NO_USERNAME){ protected void run() throws Exception { ClientManager.init(new ClientParameters(null, "why".toCharArray(), "tcp://whatever:404", Node.DEFAULT_HOST, Node.DEFAULT_PORT)); } }; //empty user name new ExpectedFailure<ConnectionException>( Messages.CONNECT_ERROR_NO_USERNAME){ protected void run() throws Exception { ClientManager.init(new ClientParameters(" ", "why".toCharArray(), "tcp://whatever:404", Node.DEFAULT_HOST, Node.DEFAULT_PORT)); } }; //null hostname new ExpectedFailure<ConnectionException>( Messages.CONNECT_ERROR_NO_HOSTNAME){ protected void run() throws Exception { ClientManager.init(new ClientParameters(DEFAULT_CREDENTIAL, DEFAULT_CREDENTIAL.toCharArray(), MockServer.URL, null, Node.DEFAULT_PORT)); } }; //empty hostname new ExpectedFailure<ConnectionException>( Messages.CONNECT_ERROR_NO_HOSTNAME){ protected void run() throws Exception { ClientManager.init(new ClientParameters(DEFAULT_CREDENTIAL, DEFAULT_CREDENTIAL.toCharArray(), MockServer.URL, " ", Node.DEFAULT_PORT)); } }; //invalid port number, lower bound new ExpectedFailure<ConnectionException>( Messages.CONNECT_ERROR_INVALID_PORT, -1){ protected void run() throws Exception { ClientManager.init(new ClientParameters(DEFAULT_CREDENTIAL, DEFAULT_CREDENTIAL.toCharArray(), MockServer.URL, Node.DEFAULT_HOST, -1)); } }; //invalid port number, upper bound new ExpectedFailure<ConnectionException>( Messages.CONNECT_ERROR_INVALID_PORT, 65536){ protected void run() throws Exception { ClientManager.init(new ClientParameters(DEFAULT_CREDENTIAL, DEFAULT_CREDENTIAL.toCharArray(), MockServer.URL, Node.DEFAULT_HOST, 65536)); } }; //no server at port final ClientParameters noServerAtPort = new ClientParameters(DEFAULT_CREDENTIAL, DEFAULT_CREDENTIAL.toCharArray(), MockServer.URL, Node.DEFAULT_HOST, Node.DEFAULT_PORT + 1); new ExpectedFailure<ConnectionException>( Messages.ERROR_CONNECT_TO_SERVER, noServerAtPort.getURL(), noServerAtPort.getUsername(), Node.DEFAULT_HOST, Node.DEFAULT_PORT + 1){ protected void run() throws Exception { ClientManager.init(noServerAtPort); } }; //auth failure final ClientParameters parameters = new ClientParameters(DEFAULT_CREDENTIAL, "game".toCharArray(), MockServer.URL, Node.DEFAULT_HOST, Node.DEFAULT_PORT); new ExpectedFailure<ConnectionException>( Messages.ERROR_CONNECT_TO_SERVER, parameters.getURL(), parameters.getUsername(),Node.DEFAULT_HOST, Node.DEFAULT_PORT){ protected void run() throws Exception { ClientManager.init(parameters); } }; //Use the correct password but incorrect port number final ClientParameters wrongPort = new ClientParameters( parameters.getUsername(), DEFAULT_CREDENTIAL.toCharArray(), "tcp://localhost:61617", Node.DEFAULT_HOST, Node.DEFAULT_PORT); new ExpectedFailure<ConnectionException>( Messages.ERROR_CONNECT_TO_SERVER, wrongPort.getURL(), wrongPort.getUsername(), Node.DEFAULT_HOST, Node.DEFAULT_PORT){ protected void run() throws Exception { ClientManager.init(wrongPort); } }; //Make sure null & empty passwords are accepted final ClientParameters nullPass = new ClientParameters( parameters.getUsername(), null, MockServer.URL, Node.DEFAULT_HOST, Node.DEFAULT_PORT); new ExpectedFailure<ConnectionException>( Messages.ERROR_CONNECT_TO_SERVER, nullPass.getURL(), nullPass.getUsername(), Node.DEFAULT_HOST, Node.DEFAULT_PORT){ protected void run() throws Exception { ClientManager.init(nullPass); } }; // Incompatible client and server versions. final ClientParameters incompatVersions = new ClientParameters( MockAuthenticator.VERSION_MISMATCH_USER, DEFAULT_CREDENTIAL.toCharArray(), MockServer.URL, Node.DEFAULT_HOST, Node.DEFAULT_PORT); new ExpectedFailure<ConnectionException>( Messages.ERROR_CONNECT_INCOMPATIBLE_DIRECT, ClientVersion.APP_ID, MockAuthenticator.VERSION_MISMATCH_SERVER_VERSION){ protected void run() throws Exception { ClientManager.init(incompatVersions); } }; final ClientParameters emptyPass = new ClientParameters( parameters.getUsername(), " ".toCharArray(), MockServer.URL, Node.DEFAULT_HOST, Node.DEFAULT_PORT); new ExpectedFailure<ConnectionException>( Messages.ERROR_CONNECT_TO_SERVER, emptyPass.getURL(), emptyPass.getUsername(), Node.DEFAULT_HOST, Node.DEFAULT_PORT){ protected void run() throws Exception { ClientManager.init(emptyPass); } }; } @Test public void credentialsMatch() throws Exception { initClient(); assertFalse(ClientManager.getInstance().isCredentialsMatch(null, null)); assertFalse(ClientManager.getInstance().isCredentialsMatch( DEFAULT_CREDENTIAL, null)); assertFalse(ClientManager.getInstance().isCredentialsMatch(null, DEFAULT_CREDENTIAL.toCharArray())); assertFalse(ClientManager.getInstance().isCredentialsMatch("", DEFAULT_CREDENTIAL.toCharArray())); String otherUser = "you"; assertFalse(ClientManager.getInstance().isCredentialsMatch(otherUser, DEFAULT_CREDENTIAL.toCharArray())); assertFalse(ClientManager.getInstance().isCredentialsMatch( DEFAULT_CREDENTIAL, "".toCharArray())); assertFalse(ClientManager.getInstance().isCredentialsMatch( DEFAULT_CREDENTIAL, otherUser.toCharArray())); assertFalse(ClientManager.getInstance().isCredentialsMatch( otherUser, otherUser.toCharArray())); assertTrue(ClientManager.getInstance().isCredentialsMatch( DEFAULT_CREDENTIAL, DEFAULT_CREDENTIAL.toCharArray())); //reconnect with different credentials ClientParameters parms = new ClientParameters(otherUser, otherUser.toCharArray(), MockServer.URL, Node.DEFAULT_HOST, Node.DEFAULT_PORT); ClientManager.getInstance().reconnect(parms); //verify that old credentials don't work assertFalse(ClientManager.getInstance().isCredentialsMatch( DEFAULT_CREDENTIAL, DEFAULT_CREDENTIAL.toCharArray())); //and the new ones do. assertTrue(ClientManager.getInstance().isCredentialsMatch( otherUser, otherUser.toCharArray())); } @Test public void webServices() throws Exception { initClient(); List<BrokerStatus> ds = getClient().getBrokersStatus().getBrokers(); assertEquals(2,ds.size()); BrokerStatus d = ds.get(0); assertEquals("N1",d.getName()); assertEquals("ID1",d.getId().getValue()); d = ds.get(1); assertEquals("N2",d.getName()); assertEquals("ID2",d.getId().getValue()); // existing user. MockServiceImpl.sActive = false; UserID id = new UserID(2); UserInfo info = getClient().getUserInfo(id, true); assertEquals("bob", info.getName()); assertEquals(id, info.getId()); assertFalse(info.getActive()); assertFalse(info.getSuperuser()); assertNull(info.getUserData()); MockServiceImpl.sActive = true; // cache contains old value. assertFalse(getClient().getUserInfo(id, true).getActive()); // bypass cache. assertTrue(getClient().getUserInfo(id, false).getActive()); // cache has been updated. assertTrue(getClient().getUserInfo(id, true).getActive()); // nonexistent user. new ExpectedFailure<ConnectionException>( Messages.ERROR_REMOTE_EXECUTION){ protected void run() throws Exception { getClient().getUserInfo(null, true); } }; new ExpectedFailure<ConnectionException>( Messages.ERROR_REMOTE_EXECUTION){ protected void run() throws Exception { getClient().getUserInfo(null, false); } }; // set user data getClient().setUserData(null); assertNull(getClient().getUserData()); Properties userData = new Properties(); getClient().setUserData(userData); assertNull(getClient().getUserData()); userData.setProperty("key1", "value1"); userData.setProperty("key2", "value2"); getClient().setUserData(userData); assertEquals(userData, getClient().getUserData()); Factory f=Factory.getInstance(); BrokerID dID=new BrokerID("me"); quickfix.fix44.ExecutionReport er=new quickfix.fix44.ExecutionReport(); er.set(new OrigClOrdID("42")); quickfix.fix44.OrderCancelReject ocr=new quickfix.fix44.OrderCancelReject(); ocr.set(new OrigClOrdID("43")); ExecutionReportImpl reportImpl = (ExecutionReportImpl) f.createExecutionReport(er, dID, Originator.Server, null, null); //Add report ID to test its serialization ReportID reportID = new ReportID(1234); ReportBaseImpl.assignReportID(reportImpl, reportID); OrderCancelRejectImpl reject = (OrderCancelRejectImpl) f.createOrderCancelReject(ocr, dID, Originator.Server, null, null); ReportID rejectID = new ReportID(2345); ReportBaseImpl.assignReportID(reject, rejectID); MockServiceImpl.sReports = new ReportBaseImpl[] { reportImpl, reject }; ReportBase[] rs = getClient().getReportsSince(new Date()); assertEquals(2,rs.length); ExecutionReport report = (ExecutionReport)rs[0]; assertEquals(dID,report.getBrokerID()); assertEquals(Originator.Server,report.getOriginator()); assertEquals("42",report.getOriginalOrderID().getValue()); assertEquals(reportID, report.getReportID()); OrderCancelReject crreport = (OrderCancelReject)rs[1]; assertEquals(dID,crreport.getBrokerID()); assertEquals("43",crreport.getOriginalOrderID().getValue()); assertEquals(rejectID, crreport.getReportID()); MockServiceImpl.sReports = new ReportBaseImpl[0]; rs = getClient().getReportsSince(new Date()); assertEquals(0,rs.length); MockServiceImpl.sReports = null; rs = getClient().getReportsSince(new Date()); assertEquals(0,rs.length); assertEquals(BigDecimal.TEN,getClient().getEquityPositionAsOf (new Date(10),null)); assertEquals(MockServiceImpl.POSITIONS, getClient(). getAllEquityPositionsAsOf(new Date())); //getFuturePositionAsOf assertEquals(BigDecimal.TEN, getClient().getFuturePositionAsOf(new Date(10), null)); assertEquals(MockServiceImpl.FUTURES_POSITIONS, getClient().getAllFuturePositionsAsOf(new Date())); //getOptionPositionAsOf() Option option = new Option("XYZ", "20101010", BigDecimal.TEN, OptionType.Call); Date date = new Date(); assertEquals(BigDecimal.TEN.add(BigDecimal.valueOf(date.getTime())), getClient().getOptionPositionAsOf(date, option)); //getAllOptionPositionsAsOf() Map<PositionKey<Option>, BigDecimal> positions = getClient(). getOptionPositionsAsOf(date, "XYZ", "PQR"); assertEquals(2, positions.size()); assertThat(positions, allOf(hasEntry( PositionKeyFactory.createOptionKey("XYZ", option.getExpiry(), option.getStrikePrice(), option.getType(), "acc", "tra"), BigDecimal.valueOf(date.getTime())), hasEntry( PositionKeyFactory.createOptionKey("PQR", option.getExpiry(), option.getStrikePrice(), option.getType(), "acc", "tra"), BigDecimal.valueOf(date.getTime())))); assertEquals(0, getClient().getOptionPositionsAsOf(date, new String[0]).size()); //getAllOptionPositionsAsOf() positions = getClient().getAllOptionPositionsAsOf(date); assertEquals(1, positions.size()); assertThat(positions, Matchers.hasEntry( PositionKeyFactory.createOptionKey("OPT", option.getExpiry(), option.getStrikePrice(), option.getType(), "acc", "tra"), BigDecimal.valueOf(date.getTime()))); //getUnderlying() assertEquals(null, getClient().getUnderlying(null)); assertEquals("", getClient().getUnderlying("")); String symbol = "symbol"; String underlying = getClient().getUnderlying(symbol); assertEquals(symbol, underlying); //verify caching assertSame(underlying, getClient().getUnderlying(symbol)); //getOptionRoots() Collection<String> roots = getClient().getOptionRoots(null); assertEquals(null, roots); //verify caching sServer.getServiceImpl().resetServiceInvoked(); assertEquals(null, getClient().getOptionRoots(null)); assertFalse(sServer.getServiceImpl().isServiceInvoked()); //Verify empty collections are received as null roots = getClient().getOptionRoots(""); assertEquals(null, roots); //verify caching sServer.getServiceImpl().resetServiceInvoked(); assertEquals(null, getClient().getOptionRoots("")); assertFalse(sServer.getServiceImpl().isServiceInvoked()); roots = getClient().getOptionRoots(symbol); assertEquals(symbol.length(), roots.size()); assertThat(roots, Matchers.hasItem(symbol)); //verify caching sServer.getServiceImpl().resetServiceInvoked(); assertSame(roots, getClient().getOptionRoots(symbol)); assertFalse(sServer.getServiceImpl().isServiceInvoked()); //reconnect and verify that the cache gets flushed getClient().reconnect(); sServer.getServiceImpl().resetServiceInvoked(); assertNotSame(underlying, getClient().getUnderlying(symbol)); assertTrue(sServer.getServiceImpl().isServiceInvoked()); sServer.getServiceImpl().resetServiceInvoked(); assertNotSame(roots, getClient().getOptionRoots(symbol)); assertTrue(sServer.getServiceImpl().isServiceInvoked()); } @Test(timeout=60000) public void heartbeats() throws Exception { initClient(SHORT_INTERVAL); assertTrue(getClient().isServerAlive()); int count=sServer.getServiceImpl().getHeartbeatCount(); while (true) { // Keep trying, in case the heartbeat thread is // experiencing starvation. Thread.sleep(SHORT_INTERVAL*2); assertTrue(getClient().isServerAlive()); if (sServer.getServiceImpl().getHeartbeatCount()>count) { break; } } count=sServer.getServiceImpl().getHeartbeatCount(); closeClient(); assertFalse(getClient().isServerAlive()); Thread.sleep(SHORT_INTERVAL*3); assertTrue(sServer.getServiceImpl().getHeartbeatCount()<=count+1); assertFalse(getClient().isServerAlive()); } @Test public void sendOrderSingle() throws Exception { //Initialize a client initClient(); for (int i = 0; i < NUM_REPEAT; i++) { //Create order OrderSingle order = Factory.getInstance().createOrderSingle(); order.setAccount("my account"); Map<String,String> map = new HashMap<String,String>(); map.put("101","value1"); map.put("201","value2"); order.setCustomFields(map); order.setBrokerID(new BrokerID("brokerA")); order.setOrderID(new OrderID("ord1")); order.setOrderType(OrderType.Limit); order.setPrice(new BigDecimal("83.43")); order.setQuantity(new BigDecimal("823.443")); order.setSide(Side.Buy); order.setInstrument(new Equity("IBM")); order.setTimeInForce(TimeInForce.AtTheClose); //Create an execution report for the mock server to send back ExecutionReport report = createExecutionReport(); sServer.getHandler().addToSend(report); //Send an order getClient().sendOrder(order); if(mListener.getException() != null) { SLF4JLoggerProxy.error(this, "Unexpected exception", mListener.getException()); } //Verify we got no exception when sending the order assertNull(mListener.getException()); //Verify transmitted order Object received = sServer.getHandler().removeReceived(); assertNotNull(received); assertTrue(received instanceof OrderEnvelope); assertEquals(((ClientImpl)getClient()).getSessionId(), ((OrderEnvelope)received).getSessionId()); received = ((OrderEnvelope)received).getOrder(); assertTrue(received instanceof OrderSingle); assertOrderSingleEquals(order, (OrderSingle)received); //Verify received report ReportBase receivedReport = mReplies.getReport(); assertTrue(receivedReport instanceof ExecutionReport); assertExecReportEquals(report, (ExecutionReport) receivedReport); } } @Test public void sendOrderReplace() throws Exception { initClient(); for (int i = 0; i < NUM_REPEAT; i++) { //Create order OrderReplace order = createOrderReplace(); //Create an execution report for the mock server to send back ExecutionReport report = createExecutionReport(); sServer.getHandler().addToSend(report); //Send an order getClient().sendOrder(order); //Verify we got no exception when sending the order if(mListener.getException() != null) { SLF4JLoggerProxy.error(this, "Unexpected exception", mListener.getException()); } //Verify no errors assertNull(mListener.getException()); //Verify transmitted order Object received = sServer.getHandler().removeReceived(); assertNotNull(received); assertTrue(received instanceof OrderEnvelope); assertEquals(((ClientImpl)getClient()).getSessionId(), ((OrderEnvelope)received).getSessionId()); received = ((OrderEnvelope)received).getOrder(); assertTrue(received instanceof OrderReplace); assertOrderReplaceEquals(order, (OrderReplace)received); //Verify received report ReportBase receivedReport = mReplies.getReport(); assertTrue(receivedReport instanceof ExecutionReport); assertExecReportEquals(report, (ExecutionReport) receivedReport); } } @Test public void sendOrderCancel() throws Exception { initClient(); for (int i = 0; i < NUM_REPEAT; i++) { //Create cancel order OrderCancel order = createOrderCancel(); //Create a reject for the mock server to send back OrderCancelReject report = createCancelReject(); sServer.getHandler().addToSend(report); //Send an order getClient().sendOrder(order); //Verify we got no exception when sending the order if(mListener.getException() != null) { SLF4JLoggerProxy.error(this, "Unexpected exception", mListener.getException()); } //Verify no errors assertNull(mListener.getException()); //Verify transmitted order Object received = sServer.getHandler().removeReceived(); assertNotNull(received); assertTrue(received instanceof OrderEnvelope); assertEquals(((ClientImpl)getClient()).getSessionId(), ((OrderEnvelope)received).getSessionId()); received = ((OrderEnvelope)received).getOrder(); assertTrue(received instanceof OrderCancel); assertOrderCancelEquals(order, (OrderCancel)received); //Verify received report ReportBase receivedReport = mReplies.getReport(); assertTrue(receivedReport instanceof OrderCancelReject); assertCancelRejectEquals(report, (OrderCancelReject) receivedReport); } } @Test public void sendAnyGetFIXResponse() throws Exception { initClient(); ActiveLocale.setProcessLocale(Locale.ROOT); String category=ClientImpl.TradeMessageReceiver.class.getName(); setDefaultLevel(Level.OFF); setLevel(category,Level.WARN); getAppender().clear(); for (int i = 0; i < NUM_REPEAT; i++) { //Create a response for the mock server to send back FIXResponse response = createFIXResponse(); sServer.getHandler().addToSend(response); //Send an order getClient().sendOrderRaw(createOrderFIX()); //Wait for responses to arrive. Thread.sleep(5000); //Verify received response (in the form of a logged message) assertNull(mReplies.peekReport()); } } @Test public void sendOrderFIX() throws Exception { initClient(); for (int i = 0; i < NUM_REPEAT; i++) { //Create FIX order FIXOrder order = createOrderFIX(); //Create a report for the mock server to send back ExecutionReport report = createExecutionReport(); sServer.getHandler().addToSend(report); //Send an order getClient().sendOrderRaw(order); //Verify we got no exception when sending the order if(mListener.getException() != null) { SLF4JLoggerProxy.error(this, "Unexpected exception", mListener.getException()); } //Verify no errors assertNull(mListener.getException()); //Verify transmitted order Object received = sServer.getHandler().removeReceived(); assertNotNull(received); assertTrue(received instanceof OrderEnvelope); assertEquals(((ClientImpl)getClient()).getSessionId(), ((OrderEnvelope)received).getSessionId()); received = ((OrderEnvelope)received).getOrder(); assertTrue(received instanceof FIXOrder); assertOrderFIXEquals(order, (FIXOrder)received); //Verify received report ReportBase receivedReport = mReplies.getReport(); assertTrue(receivedReport instanceof ExecutionReport); assertExecReportEquals(report, (ExecutionReport) receivedReport); } } @Test public void reportListening() throws Exception { initClient(); //Create our own report listener ReplyListener chitChat = new ReplyListener(); //Add it to the client getClient().addReportListener(chitChat); try { ExecutionReport report = sendVanillaOrder(); //Verify our listener got it. ReportBase receivedReport = chitChat.getReport(); assertTrue(receivedReport instanceof ExecutionReport); assertExecReportEquals(report, (ExecutionReport) receivedReport); //Now set our reply listener to fail chitChat.setFail(true); //Send the order and verify that the main test listener got it //this verifies that exceptions from listener do not impact //notifications to other listeners report = sendVanillaOrder(); //Verify our listener got it. receivedReport = chitChat.getReport(); assertTrue(receivedReport instanceof ExecutionReport); assertExecReportEquals(report, (ExecutionReport) receivedReport); //Now remove our listener. getClient().removeReportListener(chitChat); chitChat.clear(); assertNull(chitChat.peekReport()); //Send another order sendVanillaOrder(); //Verify our listener didn't get it assertNull(chitChat.peekReport()); } finally { getClient().removeReportListener(chitChat); } } @Test public void statusListening() throws Exception { initClient(); //Create our own broker status listener BrokerStatusReplyListener chitChat = new BrokerStatusReplyListener(); //Add it to the client getClient().addBrokerStatusListener(chitChat); try { BrokerStatus status = triggerBrokerStatus(); //Verify our listener got it. BrokerStatus receivedStatus = chitChat.getStatus(); assertEquals(status.toString(), receivedStatus.toString()); //Now set our reply listener to fail chitChat.setFail(true); //Trigger receipt of the status and verify that the main //test listener got it. This verifies that exceptions from //listener do not impact notifications to other listeners. status = triggerBrokerStatus(); //Verify our listener got it. receivedStatus = chitChat.getStatus(); assertEquals(status.toString(), receivedStatus.toString()); //Now remove our listener. getClient().removeBrokerStatusListener(chitChat); chitChat.clear(); assertNull(chitChat.peekStatus()); //Send another status triggerBrokerStatus(); //Verify our listener didn't get it assertNull(chitChat.peekStatus()); } finally { getClient().removeBrokerStatusListener(chitChat); } } @Test public void serverListening() throws Exception { initClient(LONG_INTERVAL); //Create our own server status listener ServerStatusReplyListener chitChat = new ServerStatusReplyListener(); //Add it to the client getClient().addServerStatusListener(chitChat); try { triggerServerStatus(); boolean status = triggerServerStatus(); //Verify our listener got it. boolean receivedStatus = chitChat.getStatus(); assertEquals(status, receivedStatus); //Now set our reply listener to fail chitChat.setFail(true); //Trigger receipt of the status and verify that the main //test listener got it. This verifies that exceptions from //listener do not impact notifications to other listeners. status = triggerServerStatus(); //Verify our listener got it. receivedStatus = chitChat.getStatus(); assertEquals(status, receivedStatus); //Now remove our listener. getClient().removeServerStatusListener(chitChat); chitChat.clear(); assertNull(chitChat.peekStatus()); //Send another status triggerServerStatus(); //Verify our listener didn't get it assertNull(chitChat.peekStatus()); } finally { getClient().removeServerStatusListener(chitChat); } } /** * Verifies the client's behavior after it's been closed. * * @throws Exception if there were errors. */ @Test public void closedBehavior() throws Exception { initClient(); final Client client = ClientManager.getInstance(); client.close(); String expectedMsg = Messages.CLIENT_CLOSED.getText(); new ExpectedFailure<IllegalStateException>(expectedMsg){ protected void run() throws Exception { client.addExceptionListener(null); } }; new ExpectedFailure<IllegalStateException>(expectedMsg){ protected void run() throws Exception { client.addBrokerStatusListener(null); } }; new ExpectedFailure<IllegalStateException>(expectedMsg){ protected void run() throws Exception { client.addServerStatusListener(null); } }; new ExpectedFailure<IllegalStateException>(expectedMsg){ protected void run() throws Exception { client.addReportListener(null); } }; new ExpectedFailure<IllegalStateException>(expectedMsg){ protected void run() throws Exception { client.getLastConnectTime(); } }; new ExpectedFailure<IllegalStateException>(expectedMsg){ protected void run() throws Exception { client.getParameters(); } }; new ExpectedFailure<IllegalStateException>(expectedMsg){ protected void run() throws Exception { client.getEquityPositionAsOf(null, null); } }; new ExpectedFailure<IllegalStateException>(expectedMsg){ protected void run() throws Exception { client.getAllEquityPositionsAsOf(null); } }; new ExpectedFailure<IllegalStateException>(expectedMsg){ protected void run() throws Exception { client.getReportsSince(null); } }; new ExpectedFailure<IllegalStateException>(expectedMsg){ protected void run() throws Exception { client.getBrokersStatus(); } }; new ExpectedFailure<IllegalStateException>(expectedMsg){ protected void run() throws Exception { client.getUserInfo(null, true); } }; new ExpectedFailure<IllegalStateException>(expectedMsg){ protected void run() throws Exception { client.getOptionPositionAsOf(null, null); } }; new ExpectedFailure<IllegalStateException>(expectedMsg){ protected void run() throws Exception { client.getAllOptionPositionsAsOf(null); } }; new ExpectedFailure<IllegalStateException>(expectedMsg){ protected void run() throws Exception { client.getOptionPositionsAsOf(null, "symbol"); } }; new ExpectedFailure<IllegalStateException>(expectedMsg){ protected void run() throws Exception { client.getUnderlying(null); } }; new ExpectedFailure<IllegalStateException>(expectedMsg){ protected void run() throws Exception { client.getOptionRoots(null); } }; new ExpectedFailure<IllegalStateException>(expectedMsg){ protected void run() throws Exception { client.reconnect(); } }; new ExpectedFailure<IllegalStateException>(expectedMsg){ protected void run() throws Exception { client.reconnect(null); } }; new ExpectedFailure<IllegalStateException>(expectedMsg){ protected void run() throws Exception { client.removeExceptionListener(null); } }; new ExpectedFailure<IllegalStateException>(expectedMsg){ protected void run() throws Exception { client.removeReportListener(null); } }; new ExpectedFailure<IllegalStateException>(expectedMsg){ protected void run() throws Exception { client.removeServerStatusListener(null); } }; new ExpectedFailure<IllegalStateException>(expectedMsg){ protected void run() throws Exception { client.removeBrokerStatusListener(null); } }; new ExpectedFailure<IllegalStateException>(expectedMsg){ protected void run() throws Exception { client.sendOrder(createOrderSingle()); } }; new ExpectedFailure<IllegalStateException>(expectedMsg){ protected void run() throws Exception { client.sendOrder(createOrderReplace()); } }; new ExpectedFailure<IllegalStateException>(expectedMsg){ protected void run() throws Exception { client.sendOrder(createOrderCancel()); } }; new ExpectedFailure<IllegalStateException>(expectedMsg){ protected void run() throws Exception { client.sendOrderRaw(createOrderFIX()); } }; //we can call close again client.close(); } private ExecutionReport sendVanillaOrder() throws Exception { //Clean up any dirty state from previous failures clearAll(); //Create a report for the mock server to send back ExecutionReport report = createExecutionReport(); if (sServer != null) { sServer.getHandler().addToSend(report); } //Send an order getClient().sendOrder(createOrderReplace()); //Verify we got no exception when sending the order if(mListener.getException() != null) { SLF4JLoggerProxy.error(this, "Unexpected exception", mListener.getException()); } //Verify no errors assertNull(mListener.getException()); //Verify received report ReportBase receivedReport = mReplies.getReport(); assertTrue(receivedReport instanceof ExecutionReport); assertExecReportEquals(report, (ExecutionReport) receivedReport); return report; } private BrokerStatus triggerBrokerStatus() throws Exception { //Clean up any dirty state from previous failures clearAll(); //Create a status for the mock server to send back BrokerStatus status = new BrokerStatus("me",new BrokerID("myID"),true); sServer.getHandler().addToSendStatus(status); //Send the status sServer.getStatusSender().convertAndSend(status); //Verify received status BrokerStatus receivedStatus = mBrokerStatusReplies.getStatus(); assertEquals(status.toString(),receivedStatus.toString()); return status; } private boolean triggerServerStatus() throws Exception { //Clean up any dirty state from previous failures clearAll(); //Alter the server's status. boolean status = sServer.getServiceImpl().toggleServerStatus(); //Sleep until the client issues the next hearbeat (and either //succeeds or fails to do so, and thus captures the server's //status). Thread.sleep(SHORT_INTERVAL*2); //Verify received status boolean receivedStatus = mServerStatusReplies.getStatus(); assertEquals(status,receivedStatus); return status; } @Test public void exceptionListening() throws Exception { initClient(); //Create our own exception listener ErrorListener earl = new ErrorListener(); //Add it to the client getClient().addExceptionListener(earl); try { //Send a plain order without any errors. sendVanillaOrder(); //Verify the listener is not notified. assertNull(earl.getException()); //Close client internally to generate errors Method m = getClient().getClass().getDeclaredMethod("internalClose"); m.setAccessible(true); m.invoke(getClient()); //Verify sending order fails ConnectionException exception = new ExpectedFailure<ConnectionException>( Messages.ERROR_SEND_MESSAGE) { protected void run() throws Exception { sendVanillaOrder(); } }.getException(); //Verify we got the exception assertEquals(exception, earl.getException()); //Verify that the main listener got it as well. assertEquals(exception, mListener.getException()); //Set our listener to fail earl.setFail(true); //Send order and verify that both listeners got the exception //in spite of throwing an exception exception = new ExpectedFailure<ConnectionException>( Messages.ERROR_SEND_MESSAGE) { protected void run() throws Exception { sendVanillaOrder(); } }.getException(); assertEquals(exception, earl.getException()); assertEquals(exception, mListener.getException()); //Now remove our listener getClient().removeExceptionListener(earl); earl.clear(); //Send order again, verify that our listener doesn't get notified exception = new ExpectedFailure<ConnectionException>( Messages.ERROR_SEND_MESSAGE) { protected void run() throws Exception { sendVanillaOrder(); } }.getException(); assertNull(earl.getException()); assertEquals(exception, mListener.getException()); } finally { getClient().removeExceptionListener(earl); } } /** * Verifies the interplay between client initialization, reconnect & close. * * @throws Exception if there were errors */ @Test public void lifecycle() throws Exception { initClient(); assertTrue(ClientManager.isInitialized()); //Verify that attempt to re-init the client fails new ExpectedFailure<ClientInitException>( Messages.CLIENT_ALREADY_INITIALIZED){ protected void run() throws Exception { initClient(); } }; //Close client and verify that we init it again closeClient(); assertFalse(ClientManager.isInitialized()); initClient(); assertTrue(ClientManager.isInitialized()); //Shutdown the server closeServer(); //get reconnect to fail new ExpectedFailure <ConnectionException>(Messages.ERROR_CONNECT_TO_SERVER){ protected void run() throws Exception { getClient().reconnect(); } }; assertTrue(ClientManager.isInitialized()); //Verify that we cannot init client new ExpectedFailure<ClientInitException>( Messages.CLIENT_ALREADY_INITIALIZED){ protected void run() throws Exception { initClient(); } }; //Close the client closeClient(); assertFalse(ClientManager.isInitialized()); //Verify that we can now attempt to reconnect but it fails because //server is not up new ExpectedFailure<ConnectionException>( Messages.ERROR_CONNECT_TO_SERVER){ protected void run() throws Exception { initClient(); } }; assertFalse(ClientManager.isInitialized()); //Restart the server initServer(); //verify that we can init the client now initClient(); assertTrue(ClientManager.isInitialized()); } @Test public void reconnect() throws Exception { initClient(); Date connectTime = getClient().getLastConnectTime(); //Test a round trip sendVanillaOrder(); //Sleep to ensure we have different connect time Thread.sleep(100); //do a reconnect getClient().reconnect(); assertTrue(getClient().getLastConnectTime().compareTo(connectTime) > 0); connectTime = getClient().getLastConnectTime(); //Test another round trip sendVanillaOrder(); //Shutdownn the server closeServer(); //Verify failure sending an order ConnectionException exception = new ExpectedFailure <ConnectionException>(Messages.ERROR_SEND_MESSAGE){ protected void run() throws Exception { sendVanillaOrder(); } }.getException(); //Verify we got the exception assertNotNull(mListener.getException()); assertEquals(exception, mListener.getException()); assertEquals(connectTime, getClient().getLastConnectTime()); //Verify reconnect fails new ExpectedFailure <ConnectionException>(Messages.ERROR_CONNECT_TO_SERVER){ protected void run() throws Exception { getClient().reconnect(); } }; //Verify that the time is unchanged assertEquals(connectTime, getClient().getLastConnectTime()); //Verify that the client is still initialized assertTrue(ClientManager.isInitialized()); //Restart the server initServer(); //Verify order still fails exception = new ExpectedFailure <ConnectionException>(Messages.ERROR_SEND_MESSAGE) { protected void run() throws Exception { sendVanillaOrder(); } }.getException(); assertEquals(new ClientInitException(Messages.NOT_CONNECTED_TO_SERVER), exception.getCause()); //Verify we got the exception assertNotNull(mListener.getException()); assertEquals(exception, mListener.getException()); //Sleep to ensure we have different connect time Thread.sleep(100); //Now reconnect getClient().reconnect(); assertTrue(getClient().getLastConnectTime().compareTo(connectTime) > 0); //Verify order goes through sendVanillaOrder(); } @Test public void reconnectParameters() throws Exception { initClient(); Date connectTime = getClient().getLastConnectTime(); //Test a round trip sendVanillaOrder(); ClientParameters oldParms = getClient().getParameters(); //Sleep to ensure we have different connect time Thread.sleep(100); //Now reconnect the client using a different parameters ClientParameters parms = new ClientParameters("you", "you".toCharArray(), MockServer.URL, Node.DEFAULT_HOST, Node.DEFAULT_PORT); getClient().reconnect(parms); assertCPEquals(parms, getClient().getParameters()); assertFalse(oldParms.getUsername().equals(parms.getUsername())); assertTrue(getClient().getLastConnectTime().compareTo(connectTime) > 0); //Test a round trip sendVanillaOrder(); } /** * Creates a sample execution report for the mock server to send back. * * @return mock execution report. * * @throws Exception if there were errors creating the execution report. */ public static ExecutionReport createExecutionReport() throws Exception { return Factory.getInstance().createExecutionReport(FIXVersion.FIX42. getMessageFactory().newExecutionReport("ord1", "clord" + sCounter.getAndIncrement(), "exec1", OrdStatus.NEW, quickfix.field.Side.BUY, new BigDecimal("4343.49"), new BigDecimal("498.34"), new BigDecimal("783343.49"), new BigDecimal("598.34"), new BigDecimal("234343.49"), new BigDecimal("798.34"), new Equity("IBM"), "my acc", "some text"), new BrokerID("bro"), Originator.Broker, null, null); } /** * Creates a sample cancel reject report for the mock server to send back. * * @return sample cancel reject report. * * @throws Exception if there were errors creating the report. */ public static OrderCancelReject createCancelReject() throws Exception { return Factory.getInstance().createOrderCancelReject( FIXVersion.FIX42.getMessageFactory().newOrderCancelReject( new quickfix.field.OrderID("brok3"), new ClOrdID("clord" + sCounter.getAndIncrement()), new OrigClOrdID("origord1"), "what?", null), new BrokerID("bro"), Originator.Broker, null, null); } /** * Creates a sample FIX response for the mock server to send back. * * @return sample FIX response. * * @throws Exception if there were errors creating the response. */ public static FIXResponse createFIXResponse() throws Exception { return Factory.getInstance().createFIXResponse (FIXVersion.FIX42.getMessageFactory().newBusinessMessageReject ("QQ",BusinessRejectReason.UNSUPPORTED_MESSAGE_TYPE, "Bad message type"), new BrokerID("bro"), Originator.Broker, null, null); } public static OrderSingle createOrderSingle() { OrderSingle order = Factory.getInstance().createOrderSingle(); order.setOrderType(OrderType.Limit); order.setPrice(new BigDecimal("834.34")); order.setQuantity(new BigDecimal("833.343")); order.setSide(Side.Buy); order.setInstrument(new Equity("IBN")); return order; } public static OrderReplace createOrderReplace() throws Exception { OrderReplace order = Factory.getInstance().createOrderReplace( createExecutionReport()); order.setOrderType(OrderType.Limit); return order; } public static OrderCancel createOrderCancel() throws Exception { return Factory.getInstance().createOrderCancel( createExecutionReport()); } public static FIXOrder createOrderFIX() throws MessageCreationException { return Factory.getInstance().createOrder( FIXVersion.FIX42.getMessageFactory().newLimitOrder("clOrd1", quickfix.field.Side.BUY, new BigDecimal("8934.234"), new Equity("IBM"), new BigDecimal("9834.23"), quickfix.field.TimeInForce.DAY, "no"), new BrokerID("bro")); } /** * Compares the client parameters instances ignoring the password value. * * @param inParms1 the first parameter. * @param inParms2 the second parameter. */ static void assertCPEquals(ClientParameters inParms1, ClientParameters inParms2) { assertEquals(inParms1.getHostname(), inParms2.getHostname()); assertEquals(inParms1.getIDPrefix(), inParms2.getIDPrefix()); assertEquals(inParms1.getPort(), inParms2.getPort()); assertEquals(inParms1.getURL(), inParms2.getURL()); assertEquals(inParms1.getUsername(), inParms2.getUsername()); } private void clearAll() { mListener.clear(); mReplies.clear(); mBrokerStatusReplies.clear(); mServerStatusReplies.clear(); if (sServer != null) { sServer.getHandler().clear(); } } private Client getClient() { if(mClient == null) { throw new NullPointerException("Call initClient() first"); } return mClient; } private void initClient(int heartbeatInterval) throws ConnectionException, ClientInitException { Date currentTime = new Date(); ClientParameters parameters = new ClientParameters(DEFAULT_CREDENTIAL, DEFAULT_CREDENTIAL.toCharArray(), MockServer.URL, Node.DEFAULT_HOST, Node.DEFAULT_PORT, null, heartbeatInterval); ClientManager.init(parameters); mClient = ClientManager.getInstance(); mClient.addExceptionListener(mListener); mClient.addReportListener(mReplies); mClient.addBrokerStatusListener(mBrokerStatusReplies); mClient.addServerStatusListener(mServerStatusReplies); assertCPEquals(parameters, mClient.getParameters()); assertNotNull(mClient.getLastConnectTime()); assertTrue(mClient.getLastConnectTime().compareTo(currentTime) >= 0); } private void initClient() throws ConnectionException, ClientInitException { initClient(60000); } private static void initServer() { if (sServer == null) { sServer = new MockServer(); } } private final ErrorListener mListener = new ErrorListener(); private final ReplyListener mReplies = new ReplyListener(); private final BrokerStatusReplyListener mBrokerStatusReplies = new BrokerStatusReplyListener(); private final ServerStatusReplyListener mServerStatusReplies = new ServerStatusReplyListener(); private static MockServer sServer; private Client mClient; private static final AtomicLong sCounter = new AtomicLong(); private static class ErrorListener implements ExceptionListener { public void exceptionThrown(Exception e) { SLF4JLoggerProxy.debug(this, e); mException = e; if (mFail) { throw new IllegalArgumentException("Test Failure"); } } public Exception getException() { return mException; } public void clear() { mException = null; mFail = false; } public void setFail(boolean isFail) { mFail = isFail; } private boolean mFail = false; private Exception mException; } private static class ReplyListener implements ReportListener { public void receiveExecutionReport(ExecutionReport inReport) { //Use add() instead of put() as these need to be non-blocking. mReports.add(inReport); if (mFail) { throw new IllegalArgumentException("Test Failure"); } } public void receiveCancelReject(OrderCancelReject inReport) { //Use add() instead of put() as these need to be non-blocking. mReports.add(inReport); if (mFail) { throw new IllegalArgumentException("Test Failure"); } } public ReportBase getReport() throws InterruptedException { //Use take as we should block until a message is available. return mReports.take(); } public ReportBase peekReport() { return mReports.peek(); } public void setFail(boolean isFail) { mFail = isFail; } public void clear() { mReports.clear(); mFail = false; } private boolean mFail = false; private BlockingQueue<ReportBase> mReports = new LinkedBlockingQueue<ReportBase>(); } private static class BrokerStatusReplyListener implements BrokerStatusListener { public void receiveBrokerStatus(BrokerStatus inStatus) { //Use add() instead of put() as these need to be non-blocking. mStatus.add(inStatus); if (mFail) { throw new IllegalArgumentException("Test Failure"); } } public BrokerStatus getStatus() throws InterruptedException { //Use take as we should block until a message is available. return mStatus.take(); } public BrokerStatus peekStatus() { return mStatus.peek(); } public void setFail(boolean isFail) { mFail = isFail; } public void clear() { mStatus.clear(); mFail = false; } private boolean mFail = false; private BlockingQueue<BrokerStatus> mStatus = new LinkedBlockingQueue<BrokerStatus>(); } private static class ServerStatusReplyListener implements ServerStatusListener { public void receiveServerStatus(boolean inStatus) { //Use add() instead of put() as these need to be non-blocking. mStatus.add(inStatus); if (mFail) { throw new IllegalArgumentException("Test Failure"); } } public Boolean getStatus() throws InterruptedException { //Use take as we should block until a message is available. return mStatus.take(); } public Boolean peekStatus() { return mStatus.peek(); } public void setFail(boolean isFail) { mFail = isFail; } public void clear() { mStatus.clear(); mFail = false; } private boolean mFail = false; private BlockingQueue<Boolean> mStatus = new LinkedBlockingQueue<Boolean>(); } private static final String DEFAULT_CREDENTIAL = "name"; private static final int SHORT_INTERVAL = 2000; private static final int LONG_INTERVAL = 60000; }