All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.crankuptheamps.client.FIXProtocolParser Maven / Gradle / Ivy

////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2010-2020 60East Technologies Inc., All Rights Reserved.
//
// This computer software is owned by 60East Technologies Inc. and is
// protected by U.S. copyright laws and other laws and by international
// treaties.  This computer software is furnished by 60East Technologies
// Inc. pursuant to a written license agreement and may be used, copied,
// transmitted, and stored only in accordance with the terms of such
// license agreement and with the inclusion of the above copyright notice.
// This computer software or any other copies thereof may not be provided
// or otherwise made available to any other person.
//
// U.S. Government Restricted Rights.  This computer software: (a) was
// developed at private expense and is in all respects the proprietary
// information of 60East Technologies Inc.; (b) was not developed with
// government funds; (c) is a trade secret of 60East Technologies Inc.
// for all purposes of the Freedom of Information Act; and (d) is a
// commercial item and thus, pursuant to Section 12.212 of the Federal
// Acquisition Regulations (FAR) and DFAR Supplement Section 227.7202,
// Government's use, duplication or disclosure of the computer software
// is subject to the restrictions set forth by 60East Technologies Inc..
//
////////////////////////////////////////////////////////////////////////////

package com.crankuptheamps.client;

import java.nio.ByteBuffer;

import com.crankuptheamps.client.exception.StreamException;

    /**
    * Implements the ProtocolParser interface. Parses a byte stream
    * using FIX protocol. 
    */

public class FIXProtocolParser implements ProtocolParser
{
    private static final byte   LATIN1_EQUALS_BYTE    = 61;
    private static final byte   LATIN1_ZERO_CHAR_BYTE = 48;
    private static final String LATIN1                = "ISO-8859-1";

    private FIXProtocol     messageType    = null;
    private FIXMessage         message        = null;
    private ByteBuffer         buffer         = null;
    private int                remainingBytes = 0;

    private enum StreamState
    {
        start, in_sow, end
    }
    private StreamState state;

    public FIXProtocolParser(FIXProtocol messageType)
    {
        this.messageType   = messageType;
        this.message       = messageType.allocateMessage();
    }

    public void process(
        ByteBuffer buffer,
        int remainingBytes,
        MessageHandler listener) throws StreamException
    {
        this.buffer = buffer;
        this.remainingBytes = remainingBytes;
        this.state = StreamState.start;

        message.reset();
        message.setBuffer(buffer.array());

        while (read(message))
        {
            listener.invoke(message);
        }
    }

    final private void extractFieldValue(FIXMessage message) throws StreamException
    {
        if(remainingBytes < 6)
        {
            throw new StreamException("Stream corrupted: premature end of message header");
        }

        int tag = 0;
        for(int n = 0; n < 5; ++n)
        {
            byte c = getByte();
            tag = tag * 10 + (c-LATIN1_ZERO_CHAR_BYTE);
        }
        // Now, read the '=' sign
        byte e = getByte();
        if(e != LATIN1_EQUALS_BYTE)
        {
            throw new StreamException("Stream corrupted: missing equals sign after FIX tag (found = " + e + ")");
        }

        int valueStart = buffer.position();
        int valueStop  = -1;

        for(int n = valueStart; n < buffer.limit(); ++n)
        {
            if(getByte() == messageType.fieldSeparator)
            {
                valueStop = n;
                break;
            }
        }

        if(valueStop == -1)
        {
            throw new StreamException("Stream corrupted: FIX value wasn't terminated with a field separator");
        }
        //String value = null;
        switch(tag-20000)
        {
        case 0:
            message._Command.set(this.buffer.array(),valueStart,valueStop-valueStart);
            break;
        case 1:
            message._CommandId.set(this.buffer.array(),valueStart,valueStop-valueStart);
            break;
        case 2:
            message._ClientName.set(this.buffer.array(),valueStart,valueStop-valueStart);
            break;
        case 3:
            message._UserId.set(this.buffer.array(),valueStart,valueStop-valueStart);
            break;
        case 4:
            message._Timestamp.set(this.buffer.array(),valueStart,valueStop-valueStart);
            break;
        case 5:
            message._Topic.set(this.buffer.array(),valueStart,valueStop-valueStart);
            break;
        case 6:
            message._Filter.set(this.buffer.array(),valueStart,valueStop-valueStart);
            break;
        case 8:
            message._AckType.set(this.buffer.array(),valueStart,valueStop-valueStart);
            break;
        case 9:
            message._SubId.set(this.buffer.array(),valueStart,valueStop-valueStart);
            break;
        case 11:
            message._Version.set(this.buffer.array(),valueStart,valueStop-valueStart);
            break;
        case 12:
            message._Expiration.set(this.buffer.array(),valueStart,valueStop-valueStart);
            break;
//        case 15: message._Heartbeat.set(this.buffer.array(),valueStart,valueStop-valueStart); break;
            //case 16: message._TimeoutInterval.set(this.buffer.array(),valueStart,valueStop-valueStart); break
         case 17:
            message._LeasePeriod.set(this.buffer.array(),valueStart,valueStop-valueStart);
            break;
        case 18:
            message._Status.set(this.buffer.array(),valueStart,valueStop-valueStart);
            break;
        case 19:
            message._QueryId.set(this.buffer.array(),valueStart,valueStop-valueStart);
            break;
        case 23:
            message._BatchSize.set(this.buffer.array(),valueStart,valueStop-valueStart);
            break;
        case 25:
            message._TopN.set(this.buffer.array(),valueStart,valueStop-valueStart);
            break;
            //case 30: message._BatchId.set(this.buffer.array(),valueStart,valueStop-valueStart); break;
            //case 32: message._SowKeys.set(this.buffer.array(),valueStart,valueStop-valueStart); break;
        case 35:
            message._CorrelationId.set(this.buffer.array(),valueStart,valueStop-valueStart);
            break;
        case 36:
            message._Sequence.set(this.buffer.array(),valueStart,valueStop-valueStart);
            break;
        case 37:
            message._Bookmark.set(this.buffer.array(),valueStart,valueStop-valueStart);
            break;
        case 38:
            message._Password.set(this.buffer.array(),valueStart,valueStop-valueStart);
            break;
        case 39:
            message._Options.set(this.buffer.array(),valueStart,valueStop-valueStart);
            break;

        case 52:
            message._RecordsInserted.set(this.buffer.array(),valueStart,valueStop-valueStart);
            break;
        case 53:
            message._RecordsUpdated.set(this.buffer.array(),valueStart,valueStop-valueStart);
            break;
        case 54:
            message._RecordsDeleted.set(this.buffer.array(),valueStart,valueStop-valueStart);
            break;
        case 55:
            message._RecordsReturned.set(this.buffer.array(),valueStart,valueStop-valueStart);
            break;
        case 56:
            message._TopicMatches.set(this.buffer.array(),valueStart,valueStop-valueStart);
            break;
        case 57:
            message._Matches.set(this.buffer.array(),valueStart,valueStop-valueStart);
            break;
        case 58:
            message._Length.set(this.buffer.array(),valueStart,valueStop-valueStart);
            break;
        case 59:
            message._SowKey.set(this.buffer.array(),valueStart,valueStop-valueStart);
            break;
        case 60:
            message._GroupSeqNo.set(this.buffer.array(),valueStart,valueStop-valueStart);
            break;
        case 61:
            message._SubIds.set(this.buffer.array(),valueStart,valueStop-valueStart);
            break;
        case 62:
            message._Reason.set(this.buffer.array(),valueStart,valueStop-valueStart);
            break;

            // Ignored values
        default:
            break;
        }
    }

    /* Commented since not used.
    private void dumpBuffer(String prefix)
    {
        System.err.print(prefix);
        for(int j = buffer.position(); j < buffer.limit(); ++j)
        {
            byte c = buffer.get(j);
            if(c == messageType.fieldSeparator)
                System.err.print("{fs}");
            else if(c == messageType.headerSeparator)
                System.err.print("{hs}");
            else if(c == messageType.messageSeparator)
                System.err.print("{ms}");
            else
                try
                {
                    System.err.print(new String(buffer.array(),j,1,LATIN1));
                }
                catch(Exception e)
                {
                    System.err.print("{error}");
                }

        }
        System.err.println();
    }
    */

    private final byte getByte()
    {
        --remainingBytes;
        return buffer.get();
    }

    private final byte peekByte()
    {
        return buffer.get(buffer.position());
    }

    private boolean read(FIXMessage m) throws StreamException
    {
        if(remainingBytes <= 0)
        {
            return false;
        }

        if(state != StreamState.in_sow) {
            m.setRawBufferOffset(buffer.position());
        }

        // read header properties
        while(remainingBytes > 0 &&
                peekByte() != messageType.headerSeparator)
        {
            extractFieldValue(m);
            while(peekByte() == messageType.fieldSeparator)
            {
                getByte();
            }
        }

        if(remainingBytes > 0)
        {
            // must be at the header separator
            byte checkb = getByte();
            if( checkb != messageType.headerSeparator ) // burn the header sep
            {
                throw new StreamException("stream corruption: expected header separator, but received byte: " + Integer.toString(checkb));
            }
        }

        // If the command type is "sow", then we have 2 headers
        //   (1) for the message, (2) for the record data
        int messageLength;
        int command = m.getCommand();
        if(state == StreamState.start &&
                (command == Message.Command.Publish ||
                 command == Message.Command.SOW ||
                 command == Message.Command.OOF ))
        {
            while(remainingBytes > 0 &&
                    peekByte() != messageType.headerSeparator)
            {
                extractFieldValue(m);
                while(peekByte() == messageType.fieldSeparator)
                {
                    getByte();
                }
            }
            if(remainingBytes > 0)
            {
                // must be at the header separator
                byte checkb = getByte();
                if( checkb != messageType.headerSeparator ) // burn the header sep
                {
                    throw new StreamException("stream corruption: expected header separator, but received byte: " + Integer.toString(checkb));
                }
            }

            if(command == Message.Command.Publish ||
                    command == Message.Command.OOF)
            {
                // Can't trust length for publish messages
                //  (this was a bug in older servers w/deltas)
                messageLength = remainingBytes;
            }
            else // SOW
            {
                state = StreamState.in_sow;
                messageLength = m._Length.getValue();
            }
        }
        else if(state == StreamState.in_sow)
        {
            messageLength = m._Length.getValue();
        }
        else
        {
            messageLength = remainingBytes;
        }

        // Now, we have to deal with the data
        m._Data.set(this.buffer.array(),buffer.position(),messageLength);
        remainingBytes -= messageLength;
        buffer.position(buffer.position() + messageLength);

        m.setRawBufferLength(buffer.position()-m.getRawBufferOffset());

        if(remainingBytes > 0)
        {
            // must be at the message separator
            byte checkb = getByte();
            if( checkb != messageType.messageSeparator ) // burn the message sep
            {
                throw new StreamException("stream corruption: expected message separator, but received byte: " + Integer.toString(checkb));
            }
        }
//((FIXMessage)message).dumpBuffer("READING> ", buffer.array(), m.getRawBufferOffset(),buffer.limit()-m.getRawBufferOffset());
        return true;
    }
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy