From 4bc599bde671253f52348315967f316b44a7eb18 Mon Sep 17 00:00:00 2001 From: MartinAdams Date: Mon, 17 Nov 2014 14:41:02 +0000 Subject: [PATCH 1/2] Add new Application Callback (non-breaking) for early intercept of inbound messages. Add an IApplicationExt interface that extends IApplication and facilitates early intercept of inbound messages on the part of the Application. This is a bit similar to the late outbound intercept afforded by ToApp/ToAdmin, and permits alteration of field values prior to their being validated by the engine. The difference is that, with the new interface, only one callback is provided for both App and Admin messages. Usage of the existing IApplication interface is unaffected. --- QuickFIXn/IApplicationExt.cs | 20 ++++++++++ QuickFIXn/QuickFix.csproj | 3 +- QuickFIXn/Session.cs | 5 +++ UnitTests/SessionTest.cs | 74 +++++++++++++++++++++++++++++++++++- 4 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 QuickFIXn/IApplicationExt.cs diff --git a/QuickFIXn/IApplicationExt.cs b/QuickFIXn/IApplicationExt.cs new file mode 100644 index 000000000..35d34d484 --- /dev/null +++ b/QuickFIXn/IApplicationExt.cs @@ -0,0 +1,20 @@ +using System; + +namespace QuickFix +{ + /// + /// This is the optional extension interface for processing session messages, and facilitates early interception of inbound messages. + /// 'Early', in this context, means after structure, length and checksum have been validated, but before any further validation has been performed. + /// + public interface IApplicationExt : IApplication + { + /// + /// This callback provides early notification of when an administrative or application message is sent from a counterparty to your FIX engine. + /// This can be useful for doing pre-processing of an inbound message after its structure, checksum and length have been validated, but before + /// any further validation has been performed on it. + /// + /// received message + /// session on which message received + void FromEarlyIntercept(Message message, SessionID sessionID); + } +} diff --git a/QuickFIXn/QuickFix.csproj b/QuickFIXn/QuickFix.csproj index c0ef8831b..90f45af6a 100644 --- a/QuickFIXn/QuickFix.csproj +++ b/QuickFIXn/QuickFix.csproj @@ -67,6 +67,7 @@ + @@ -141,4 +142,4 @@ --> - + \ No newline at end of file diff --git a/QuickFIXn/Session.cs b/QuickFIXn/Session.cs index 470be3acd..a5295c1e1 100755 --- a/QuickFIXn/Session.cs +++ b/QuickFIXn/Session.cs @@ -22,6 +22,7 @@ public class Session : IDisposable private SessionSchedule schedule_; private SessionState state_; private IMessageFactory msgFactory_; + private bool appDoesEarlyIntercept_; private static readonly HashSet AdminMsgTypes = new HashSet() { "0", "A", "1", "2", "3", "4", "5" }; #endregion @@ -211,6 +212,7 @@ public Session( this.DataDictionaryProvider = new DataDictionaryProvider(dataDictProvider); this.schedule_ = sessionSchedule; this.msgFactory_ = msgFactory; + appDoesEarlyIntercept_ = app is IApplicationExt; this.SenderDefaultApplVerID = senderDefaultApplVerID; @@ -547,6 +549,9 @@ public void Next(Message message) return; } + if (appDoesEarlyIntercept_) + ((IApplicationExt)Application).FromEarlyIntercept(message, this.SessionID); + if (IsNewSession) state_.Reset("New session (detected in Next(Message))"); diff --git a/UnitTests/SessionTest.cs b/UnitTests/SessionTest.cs index 328b684cf..b88cc143d 100755 --- a/UnitTests/SessionTest.cs +++ b/UnitTests/SessionTest.cs @@ -112,8 +112,49 @@ public void OnLogout(QuickFix.SessionID sessionID) public void OnLogon(QuickFix.SessionID sessionID) { } + #endregion + } + + class MockApplicationExt : QuickFix.IApplicationExt + { + public HashSet InterceptedMessageTypes = new HashSet(); + + #region Application Members + + public void ToAdmin(QuickFix.Message message, QuickFix.SessionID sessionID) + { + } + + public void FromAdmin(QuickFix.Message message, QuickFix.SessionID sessionID) + { + } + + public void ToApp(QuickFix.Message message, QuickFix.SessionID sessionId) + { + } + + public void FromApp(QuickFix.Message message, QuickFix.SessionID sessionID) + { + } + + public void OnCreate(QuickFix.SessionID sessionID) + { + } + + public void OnLogout(QuickFix.SessionID sessionID) + { + } + public void OnLogon(QuickFix.SessionID sessionID) + { + } + + public void FromEarlyIntercept(QuickFix.Message message, QuickFix.SessionID sessionID) + { + InterceptedMessageTypes.Add(message.Header.GetString(QuickFix.Fields.Tags.MsgType)); + } #endregion + } [TestFixture] @@ -126,6 +167,7 @@ public class SessionTest MockApplication application = null; QuickFix.Session session = null; QuickFix.Session session2 = null; + QuickFix.Dictionary config = null; int seqNum = 1; Regex msRegex = new Regex(@"\.[\d]{1,3}$"); @@ -137,7 +179,7 @@ public void setup() application = new MockApplication(); settings = new QuickFix.SessionSettings(); - QuickFix.Dictionary config = new QuickFix.Dictionary(); + config = new QuickFix.Dictionary(); config.SetBool(QuickFix.SessionSettings.PERSIST_MESSAGES, false); config.SetString(QuickFix.SessionSettings.CONNECTION_TYPE, "initiator"); config.SetString(QuickFix.SessionSettings.START_TIME, "00:00:00"); @@ -719,5 +761,35 @@ public void TestToAppResendDoNotSend() SendResendRequest(1, 0); Assert.False(SENT_NOS()); } + + + [Test] + public void TestApplicationExtension() + { + var mockApp = new MockApplicationExt(); + session = new QuickFix.Session(mockApp, new QuickFix.MemoryStoreFactory(), sessionID, + new QuickFix.DataDictionaryProvider(), new QuickFix.SessionSchedule(config), 0, new QuickFix.ScreenLogFactory(settings), new QuickFix.DefaultMessageFactory(), "blah"); + session.SetResponder(responder); + session.CheckLatency = false; + + Logon(); + QuickFix.FIX42.NewOrderSingle order = new QuickFix.FIX42.NewOrderSingle( + new QuickFix.Fields.ClOrdID("1"), + new QuickFix.Fields.HandlInst(QuickFix.Fields.HandlInst.MANUAL_ORDER), + new QuickFix.Fields.Symbol("IBM"), + new QuickFix.Fields.Side(QuickFix.Fields.Side.BUY), + new QuickFix.Fields.TransactTime(), + new QuickFix.Fields.OrdType(QuickFix.Fields.OrdType.LIMIT)); + + order.Header.SetField(new QuickFix.Fields.TargetCompID(sessionID.SenderCompID)); + order.Header.SetField(new QuickFix.Fields.SenderCompID(sessionID.TargetCompID)); + order.Header.SetField(new QuickFix.Fields.MsgSeqNum(2)); + + session.Next(order); + + Assert.That(mockApp.InterceptedMessageTypes.Count, Is.EqualTo(2)); + Assert.True(mockApp.InterceptedMessageTypes.Contains(QuickFix.Fields.MsgType.LOGON)); + Assert.True(mockApp.InterceptedMessageTypes.Contains(QuickFix.Fields.MsgType.NEWORDERSINGLE)); + } } } From 5820c4e488885348cd993f50dee4221d04cc796a Mon Sep 17 00:00:00 2001 From: MartinAdams Date: Wed, 13 May 2015 19:04:22 +0100 Subject: [PATCH 2/2] Changes as requested to tab/indents and comments --- QuickFIXn/IApplicationExt.cs | 32 +++++++++++++++++++------------- UnitTests/SessionTest.cs | 18 +++++++++--------- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/QuickFIXn/IApplicationExt.cs b/QuickFIXn/IApplicationExt.cs index 35d34d484..2555472cc 100644 --- a/QuickFIXn/IApplicationExt.cs +++ b/QuickFIXn/IApplicationExt.cs @@ -3,18 +3,24 @@ namespace QuickFix { /// - /// This is the optional extension interface for processing session messages, and facilitates early interception of inbound messages. - /// 'Early', in this context, means after structure, length and checksum have been validated, but before any further validation has been performed. + /// This is the optional extension interface for processing inbound messages, + /// and facilitates early interception of such messages. 'Early', in this context, + /// means after structure, length and checksum have been validated, but before any + /// further validation has been performed. + /// This interface will not normally be required, and it should be used only with caution: + /// it allows modfications to be made to irregular inbound messages that would otherwise + /// fail validation against the Fix dictionary, an provides an alternative to dictionary + /// customisation as a means of dealing with such messages. /// - public interface IApplicationExt : IApplication - { - /// - /// This callback provides early notification of when an administrative or application message is sent from a counterparty to your FIX engine. - /// This can be useful for doing pre-processing of an inbound message after its structure, checksum and length have been validated, but before - /// any further validation has been performed on it. - /// - /// received message - /// session on which message received - void FromEarlyIntercept(Message message, SessionID sessionID); - } + public interface IApplicationExt : IApplication + { + /// + /// This callback provides early notification of when an administrative or application message is sent from a counterparty to your FIX engine. + /// This can be useful for doing pre-processing of an inbound message after its structure, checksum and length have been validated, but before + /// any further validation has been performed on it. + /// + /// received message + /// session on which message received + void FromEarlyIntercept(Message message, SessionID sessionID); + } } diff --git a/UnitTests/SessionTest.cs b/UnitTests/SessionTest.cs index b88cc143d..444140cc8 100755 --- a/UnitTests/SessionTest.cs +++ b/UnitTests/SessionTest.cs @@ -119,7 +119,7 @@ class MockApplicationExt : QuickFix.IApplicationExt { public HashSet InterceptedMessageTypes = new HashSet(); - #region Application Members + #region Application Members public void ToAdmin(QuickFix.Message message, QuickFix.SessionID sessionID) { @@ -768,18 +768,18 @@ public void TestApplicationExtension() { var mockApp = new MockApplicationExt(); session = new QuickFix.Session(mockApp, new QuickFix.MemoryStoreFactory(), sessionID, - new QuickFix.DataDictionaryProvider(), new QuickFix.SessionSchedule(config), 0, new QuickFix.ScreenLogFactory(settings), new QuickFix.DefaultMessageFactory(), "blah"); + new QuickFix.DataDictionaryProvider(), new QuickFix.SessionSchedule(config), 0, new QuickFix.ScreenLogFactory(settings), new QuickFix.DefaultMessageFactory(), "blah"); session.SetResponder(responder); session.CheckLatency = false; Logon(); QuickFix.FIX42.NewOrderSingle order = new QuickFix.FIX42.NewOrderSingle( - new QuickFix.Fields.ClOrdID("1"), - new QuickFix.Fields.HandlInst(QuickFix.Fields.HandlInst.MANUAL_ORDER), - new QuickFix.Fields.Symbol("IBM"), - new QuickFix.Fields.Side(QuickFix.Fields.Side.BUY), - new QuickFix.Fields.TransactTime(), - new QuickFix.Fields.OrdType(QuickFix.Fields.OrdType.LIMIT)); + new QuickFix.Fields.ClOrdID("1"), + new QuickFix.Fields.HandlInst(QuickFix.Fields.HandlInst.MANUAL_ORDER), + new QuickFix.Fields.Symbol("IBM"), + new QuickFix.Fields.Side(QuickFix.Fields.Side.BUY), + new QuickFix.Fields.TransactTime(), + new QuickFix.Fields.OrdType(QuickFix.Fields.OrdType.LIMIT)); order.Header.SetField(new QuickFix.Fields.TargetCompID(sessionID.SenderCompID)); order.Header.SetField(new QuickFix.Fields.SenderCompID(sessionID.TargetCompID)); @@ -790,6 +790,6 @@ public void TestApplicationExtension() Assert.That(mockApp.InterceptedMessageTypes.Count, Is.EqualTo(2)); Assert.True(mockApp.InterceptedMessageTypes.Contains(QuickFix.Fields.MsgType.LOGON)); Assert.True(mockApp.InterceptedMessageTypes.Contains(QuickFix.Fields.MsgType.NEWORDERSINGLE)); - } + } } }