Java tutorial
/* * Copyright 2011-2014 EPAM Systems/B2BITS (http://www.b2bits.com). * * This file is part of STAFF. * * STAFF is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * STAFF is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with STAFF. If not, see <http://www.gnu.org/licenses/>. */ package com.btobits.automator.fix.ant.task; import com.btobits.automator.ant.annotation.AutoParamBean; import com.btobits.automator.ant.annotation.AutoParamBool; import com.btobits.automator.ant.annotation.AutoParamEnum; import com.btobits.automator.ant.annotation.AutoParamStr; import com.btobits.automator.fix.ant.container.GroupDescription; import com.btobits.automator.fix.ant.filter.FilterFiledCondition; import com.btobits.automator.fix.utils.FixUtils; import com.btobits.automator.fix.utils.fix.FixMessageType; import org.apache.commons.lang.StringUtils; import org.apache.tools.ant.BuildException; import org.junit.Assert; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; /** * <p><h2> * fixField task accesses FIX message field. FIX groups are also supported. * </h2></p> * <p/> * <p> * <b>Sample:</b><br/> * <pre> * Set tag TransactTime of the message new_order_singleto the value of the new_order_trn_time propertty. * Note: in order to work with message fields, the message should be defined earlier. * <p/> * <!-- define message --> * <fixMessage id="new_order_single"> * <field name="MsgType" value="D" /> * <field name="HandlInst" value="1" /> * <field name="Symbol" value="ZXZZT" /> * <field name="Side" value="1" /> * <field name="OrderQty" value="20000" /> * <field name="OrdType" value="2" /> * <field name="ClOrdID" value="Order#1" /> * <field name="Price" value="34.7" /> * </fixMessage> * <!-- Generate transaction time and store to the new_order_trn_time property --> * <tstamp> * <format property="new_order_trn_time" pattern="yyyyMMdd-hh:mm:ss.SSS" locale="en,US" offset="-5" unit="hour" /> * </tstamp> * <!-- set field value --> * <fixField refid="new_order_single" action="set" name="TransactTime" property="new_order_trn_time" /> * Set tag values of two entries of NoMDEntries group of the hand_made_msg message. * <p/> * <fixMessage id="hand_made_msg"> * <field name="MsgType" value="X" /> * </fixMessage> * <p/> * <property name="px_1" value="34.7" /> * <property name="px_2" value="34.6" /> * <p/> * <fixField refid="hand_made_msg" action="set" group="NoMDEntries:1" name="MDUpdateAction" value="0" /> * <fixField refid="hand_made_msg" action="set" group="NoMDEntries:1" name="MDEntryType" value="3" /> * <fixField refid="hand_made_msg" action="set" group="NoMDEntries:1" name="MDEntrySize" value="190" /> * <fixField refid="hand_made_msg" action="set" group="NoMDEntries:1" name="MDEntryPx" property="px_1" /> * <fixField refid="hand_made_msg" action="set" group="NoMDEntries:1" name="Symbol" value="CSCO" /> * <p/> * <fixField refid="hand_made_msg" action="set" group="NoMDEntries:2" name="MDUpdateAction" value="0" /> * <fixField refid="hand_made_msg" action="set" group="NoMDEntries:2" name="MDEntryType" value="3" /> * <fixField refid="hand_made_msg" action="set" group="NoMDEntries:2" name="MDEntrySize" value="250" /> * <fixField refid="hand_made_msg" action="set" group="NoMDEntries:2" name="MDEntryPx" value="${px_2}" /> * <fixField refid="hand_made_msg" action="set" group="NoMDEntries:2" name="Symbol" value="ZVZZT" /> * <p/> * Get values of tags from the received exec_report message and print them to console. * <p/> * <!-- receive the message --> * <fixReceive refid="simple_client" sender="TRGT" target="SNDR" type="FIX.4.4" repeat="1" count="1" timeout="10"> * <fixMessage id="exec_report"> * <field name="MsgType" value="8" /> * <field name="SendingTime" /> * <field name="CheckSum" /> * <field name="ExecID" /> * <field name="OrderID" /> * </fixMessage> * </fixReceive> * <!-- get tags' values --> * <fixField refid="exec_report" action="get" name="SendingTime" property="er_time" /> * <fixField refid="exec_report" action="get" name="CheckSum" property="er_checksum" /> * <fixField refid="exec_report" action="get" name="ExecID" property="er_exec_id" /> * <fixField refid="exec_report" action="get" name="OrderID" property="er_order_id" /> * <!-- print the values --> * <echo>Execution Report OrderID: ${er_order_id}</echo> * <echo>Execution Report ExecID: ${er_exec_id}</echo> * <echo>Execution Report CheckSum: ${er_checksum}</echo> * <echo>Execution Report SendingTime: ${er_time}</echo> * </pre> * </p> * <p/> * <p/> * * @author Kirill_Mukhoiarov */ @AutoParamBean public final class FixFieldTask extends BasicAntTask { public final static String svnSignature = "$$Rev: 67588 $$ $$Date: 2014-10-28 14:02:42 +0200 (, 28 2014) $$ $$LastChangedBy: Alexander_Sereda $$"; /** * Any project element can be assigned an identifier using * its id attribute. In most cases the element can subsequently * be referenced by specifying the refid attribute on an * element of the same type. */ @AutoParamStr(xmlAttr = "refid") public String refId = null; /** * Name of the fixField to operate. Both tag name or tag number can be used as the value. */ @AutoParamStr(xmlAttr = "name") public String name = null; /** * Property name to be passed to action. Only one of property / value should be specified. */ @AutoParamStr(xmlAttr = "property") public String property = null; /** * Property name to be passed to action. Only one of property / value should be specified. */ @AutoParamStr(xmlAttr = "value") public String value = null; /** * TODO: add description */ @AutoParamStr(xmlAttr = "group") public String groupName = null; /** * TODO: add description */ @AutoParamBool(xmlAttr = "nullable") public Boolean nullable = false; /** * Action to be done with fixField value. Possible values are: get | set | getGroupCount */ @AutoParamEnum(xmlAttr = "action", enumClass = ACTION_TYPE.class) public ACTION_TYPE action; private LinkedList<GroupDescription> group; private FixMessageType papaMessage; public enum ACTION_TYPE { GET, SET, GETGROUPCOUNT; } @Override protected void validate() throws Exception { Assert.assertTrue("Reference to FIX session is not specified", StringUtils.isNotBlank(refId)); final Object obj = getProject().getReference(refId); Assert.assertNotNull("RefId[" + refId + "]. Failed to get FIX session.", obj); Assert.assertTrue( "RefId[" + refId + "]. Referred object is not FIX message: " + obj.getClass().getSimpleName(), (obj instanceof FixMessageType)); papaMessage = (FixMessageType) obj; // Assert.assertTrue("Required 'groupName' attribute is not specified", StringUtils.isNotBlank(groupName)); if (groupName != null) { setGroupName(groupName); // TODO: make check list 'group' - must be not empty } Assert.assertNotNull("fixFiled attribute 'action' is required parameter. " + "Valid value list: get||set||getGroupCount.", action); } @Override protected void runTestInstructions() throws Exception { switch (action) { case GET: getField(); break; case SET: setField(); break; case GETGROUPCOUNT: getGroupCount(); break; } } public void addText(String inValue) { value = inValue; } private void getField() { String filedValue = getFiledValue(whereLookValue()); if (filedValue != null) { writeProperty(filedValue); } else { if (!nullable) { throw new BuildException("Field '" + name + "' was not found in referenced message"); } } } private void getGroupCount() { int groupCount = getGroupCount(whereLookValue()); writeProperty(Integer.toString(groupCount)); } private List<FixMessageType.Field> whereLookValue() { if (isGroupAssigned()) { LinkedList<FixMessageType.Field> groupFields = getGroupFields(papaMessage.getFields(), false); if (groupFields != null) { return groupFields; } else { throw new BuildException("Group is assigned, but can't find in parent message. Grope(s): " + groupName + ". " + "FixMessage: " + papaMessage); } } else { return papaMessage.getFields(); } } private boolean isGroupAssigned() { return group == null ? false : !group.isEmpty(); } private LinkedList<FixMessageType.Field> getGroupFields(final LinkedList<FixMessageType.Field> msgBody, boolean canCreate) { LinkedList<FixMessageType.Field> curGroupFields = msgBody; for (final GroupDescription desc : group) { FixMessageType.Group innerGroup = getGroup(curGroupFields, desc, canCreate); if (innerGroup == null) { throw new BuildException("Group '" + groupName + "' not found"); } else { curGroupFields = innerGroup.getFields(); } } return curGroupFields; } private FixMessageType.Group getGroup(final LinkedList<FixMessageType.Field> inFields, final GroupDescription inDesc, boolean canCreate) { int curGroupIndex = 0; int leadingTagId = FixUtils.getFieldId(inDesc.getName()); for (final FixMessageType.Field f : inFields) { int id1 = FixUtils.getFieldId(f.getName()); if (f.isGroup() && id1 == leadingTagId) { ++curGroupIndex; if (curGroupIndex == inDesc.getIndex()) { return (FixMessageType.Group) f; } } } FixMessageType.Group group = null; if (canCreate) { while (curGroupIndex != inDesc.getIndex()) { group = new FixMessageType.Group(); group.setName(inDesc.getName()); inFields.add(group); ++curGroupIndex; } } return group; } private void writeProperty(final String value) { getProject().setProperty(StringUtils.isNotBlank(property) ? property : name, value); } private String getFiledValue(final List<FixMessageType.Field> inFields) { String result = null; for (final FixMessageType.Field field : inFields) { int id1 = FixUtils.getFieldId(field.getName()); int id2 = FixUtils.getFieldId(name); if (id1 == id2) { if (field.getValue() != null) { result = field.getValue(); break; } } } return result; } private int getGroupCount(final List<FixMessageType.Field> inFields) { int count = 0; for (final FixMessageType.Field field : inFields) { int id1 = FixUtils.getFieldId(field.getName()); int id2 = FixUtils.getFieldId(name); if (id1 == id2 && field.isGroup()) { ++count; } } return count; } private void setField() throws BuildException { final LinkedList<FixMessageType.Field> msgBody = papaMessage.getFields(); if (isGroupAssigned()) { LinkedList<FixMessageType.Field> curGroupFields = msgBody; for (final GroupDescription desc : group) { FixMessageType.Group innerGroup = getGroup(curGroupFields, desc, true); if (innerGroup == null) { throw new BuildException("Can't create group '" + desc + "'"); } else { curGroupFields = innerGroup.getFields(); } } setField(curGroupFields); } else { setField(msgBody); } } private void setField(final LinkedList<FixMessageType.Field> fields) { for (final FixMessageType.Field f : fields) { int id1 = FixUtils.getFieldId(f.getName()); int id2 = FixUtils.getFieldId(name); if (id1 == id2) { fields.remove(f); break; } } final String fieldValue = StringUtils.isNotBlank(value) ? value : StringUtils.isNotBlank(property) ? getProject().getProperty(property) : null; if (StringUtils.isNotBlank(fieldValue)) { final FixMessageType.Field field = new FixMessageType.Field(); field.setName(name); field.setValue(fieldValue); fields.add(field); } } protected void setGroupName(final String inStr) { group = new LinkedList<GroupDescription>(); final String[] groups = StringUtils.split(inStr, '/'); for (final String rawDesc : groups) { final String[] items = StringUtils.split(rawDesc, ':'); boolean isGood = true; if (items.length != 2 || items[0].length() <= 0) { isGood = false; } int index = 0; if (isGood) { index = Integer.parseInt(items[1]); if (index < 1) { isGood = false; } } if (!isGood) { throw new BuildException("Bad group format specified: " + inStr); } final GroupDescription desc = new GroupDescription(); desc.setName(items[0]); desc.setIndex(index); group.add(desc); } } }