Quickfix/n, most efficient way to extract message Type?
Asked Answered
N

3

8

What is the most efficient way in Quickfix/n 1.4 to extract the message type as defined here: http://www.fixprotocol.org/FIXimate3.0/en/FIX.5.0SP2/messages_sorted_by_type.html

I currently use var msgType = Message.GetMsgType(message.ToString()); which results in "A" for a Logon message. Is there a better way? I try to determine the message type within ToAdmin(...) in order to catch an outgoing Logon request message so I can add the username and password.

I would love to do it through MessageCracker but so far I have not found a way to implement a catch-all-remaining message types in case I have not implemented all OnMessage overloads. (Please see related question: Quickfix, Is there a "catch-all" method OnMessage to handle incoming messages not handled by overloaded methods?).

Thanks

Neaten answered 31/7, 2013 at 9:11 Comment(5)
This is premature optimization. Make it work first. Put off performance investigations until you know you need it.Amen
Sorry, my comment was overly blunt. What I meant to say was that if you have performance issues, this little string parsing thing is not likely to be your primary cause. Don't worry about optimizing things until you have evidence that it's a problem.Amen
This is entirely unrelated to performance. I need to check whether the outgoing message is a logon in order to add username and password.Neaten
I guess I interpreted your use of "efficient" to mean performance. My bad.Amen
No worries maybe the term "efficient" was misleading. Thanks for your answerNeaten
A
7

Not your title question, but a key part of it:

I try to determine the message type within ToAdmin(...) in order to catch an outgoing Logon request message so I can add the username and password.

Here's a blob of code that pretty much nails it (taken from this post to the QF/n mailing list):

    public void ToAdmin(Message message, SessionID sessionID)
    {
        // Check message type
        if (message.Header.GetField(Tags.MsgType) == MsgType.LOGON)
        {
            // Yes it is logon message
            // Check if local variables YourUserName and YourPassword are set
            if (!string.IsNullOrEmpty(YourUserName) && !string.IsNullOrEmpty(YourPassword))
            {
                // Add Username and Password fields to logon message
                ((Logon) message).Set(new Username(YourUserName));
                ((Logon) message).Set(new Password(YourPassword));
            }
        }
    }
Amen answered 31/7, 2013 at 13:50 Comment(1)
Perfect. Exactly what I was looking for. I was unaware of the Tag class. Thanks a lot.Neaten
V
2

Here is another approach using the idea I've mentioned in the other post. It's actually very simple to split the Crack method in two methods and have OnMessageTo and OnMessageFrom handlers. The modified implementation of the MessageCracker helper class above is aware about the message direction, though certainly can be improved.

In your Application, implement like this (not complete code):

public class MyFixApplication: DirectedMessageCracker, Application
{
...

public void FromAdmin(Message msg, SessionID sessionId)
{
    CrackFrom(msg, sessionId);
}

public void ToAdmin(Message msg, SessionID sessionId)
{
    CrackTo(msg, sessionId);
}

public void OnMessageTo(Logon msg, SessionID sessionId)
{
    //Treat the outgoing message, set user, password, etc
}

public void OnMessageFrom(Allocation msg, SessionID sessionId)
{
    //Treat the incoming Allocation message
}
...and so on

And the modified MessageCracker:

using System;
using System.Collections.Generic;
using System.Reflection;

namespace QuickFix
{
    /// <summary>
    /// Helper class for delegating message types for various FIX versions to
    /// type-safe OnMessage methods, supports handling of incoming and outgoing messages separatelly
    /// </summary>
    public abstract class DirectedMessageCracker
    {
        private readonly Dictionary<Type, MethodInfo> _toHandlerMethods = new Dictionary<Type, MethodInfo>();
        private readonly Dictionary<Type, MethodInfo> _fromHandlerMethods = new Dictionary<Type, MethodInfo>();

        protected DirectedMessageCracker()
        {
            Initialize(this);
        }

        private void Initialize(Object messageHandler)
        {
            var handlerType = messageHandler.GetType();

            var methods = handlerType.GetMethods();
            foreach (var m in methods)
            {
                if (IsToHandlerMethod(m))
                    _toHandlerMethods[m.GetParameters()[0].ParameterType] = m;
                else if (IsFromHandlerMethod(m))
                    _fromHandlerMethods[m.GetParameters()[0].ParameterType] = m;
            }
        }

        static public bool IsToHandlerMethod(MethodInfo m)
        {
            return IsHandlerMethod("OnMessageTo", m);
        }

        static public bool IsFromHandlerMethod(MethodInfo m)
        {
            return IsHandlerMethod("OnMessageFrom", m);
        }

        static public bool IsHandlerMethod(string searchMethodName, MethodInfo m) 
        {
            return (m.IsPublic
                && m.Name.StartsWith(searchMethodName)
                && m.GetParameters().Length == 2
                && m.GetParameters()[0].ParameterType.IsSubclassOf(typeof(Message))
                && typeof(SessionID).IsAssignableFrom(m.GetParameters()[1].ParameterType)
                && m.ReturnType == typeof(void));
        }

        /// <summary>
        /// Process ("crack") a FIX message and call the registered handlers for that type, if any
        /// </summary>
        /// <param name="handlerMethods"></param>
        /// <param name="message"></param>
        /// <param name="sessionID"></param>
        private void Crack(IDictionary<Type, MethodInfo> handlerMethods, Message message, SessionID sessionID)
        {
            var messageType = message.GetType();
            MethodInfo handler;

            if (handlerMethods.TryGetValue(messageType, out handler))
                handler.Invoke(this, new object[] { message, sessionID });
            else
                throw new UnsupportedMessageType();
        }

        /// <summary>
        /// Process ("crack") an INCOMING FIX message and call the registered handlers for that type, if any
        /// </summary>
        /// <param name="message"></param>
        /// <param name="sessionID"></param>
        public void CrackFrom(Message message, SessionID sessionID)
        {
            Crack(_fromHandlerMethods, message, sessionID);
        }

        /// <summary>
        /// Process ("crack") an OUTGOING FIX message and call the registered handlers for that type, if any
        /// </summary>
        /// <param name="message"></param>
        /// <param name="sessionID"></param>
        public void CrackTo(Message message, SessionID sessionID)
        {
            Crack(_toHandlerMethods, message, sessionID);
        }
    }
}

You can also skip the crack instead of throwing an exception in case of you don't want to implement all possible message handlers by just removing the throw new UnsupportedMessageType(); from the crack method.

Another idea is to split cracking Admin/App messages.

Vaudevillian answered 1/8, 2013 at 19:55 Comment(1)
Thanks a lot for the idea. However, I put the whole thought of using MessageCracker on hold because the builder currently seems unable to create code for the added xml data dictionary custom message types. Without it I cannot use MessageCracker either and rather just build the messages myself. Still playing around with it but so far it does not look as if the message methods are correctly added to the respective FIXxx namespace.Neaten
V
1

In this case you could just do this inside ToAdmin:

var logonMessage = msg as Logon;
if (logonMessage != null)
{
    //Treat the logon message as you want
}

Or use the MessageCracker as explained in the other answer that you mentioned.

Hope it helps.

Vaudevillian answered 31/7, 2013 at 13:40 Comment(1)
Thanks but I found Grant's answer slightly more elegant. Thanks though. +1Neaten

© 2022 - 2024 — McMap. All rights reserved.