com.crankuptheamps.client.FIXProtocolParser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of amps-client Show documentation
Show all versions of amps-client Show documentation
AMPS Java client by 60East Technologies, Inc.
////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2010-2022 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;
}
}