Example 2

The second example show how to create two distinct connections at the same FastTrack MetaMarket service. Two subscriptions are opened on the first connection: these two subscriptions are a few complex because they use Mask, partial keys and refreshEntity. Instead on the second connection many transactions will be started and/or queried automatically.

This example is more complex even because it handles and trace errors and exceptions in a sophisticated way. In addition all LifeCycle activities are handled at two levels: at the higher level all errors and common actions are handled, at the lower level the specific actions for the specific activity is taken.

/*

Description
===========
  This application example open two connections with a MetaMarket service
  of a given FastTrack server, and then:

    - Two subscriptions will be started on the first connection:
        - The first one is a non masked subscription
          on FT_C_ORDER EntityClass:
            - All received entities will be written on standard output.
            - All received entities matching a given operator will be written on files.
          This subscription is never stopped.
        - The second one is a partial and masked subscription
          on FT_C_TRADING_STATE EntityClass:
            - Only the ExchangeID and Phase fields
              are subscribed and written on standard output when received.
            - In addition many refreshEntity will be requested for each received
              entity with Phase field that matches a given phase. 
              This is done in order to discover and print all fields of these entities.
          This subscription is close when all received non masked entities
          match the corresponding requested refreshEntity.

    - All files *.order and *.order.pending of a given directory are listed.
      Each *.order file contains all 14 mandatory fields of FT_C_ORDER EntityClass.
      Foreach *.order file (if there is not a corresponding *.order.pending file)
        a transaction is sent to the server in order to add a FT_C_ORDER.
        Once the transaction is sent its TransactionID is stored
        on a corresponding *.order.pending file and this file remains untouched
        until the transaction is committed or aborted.
        Once the transaction is sent and it's still flying the command-line option "-x"
        controls if the transaction must be immediately queried for its status
        or if this query must be postponed to the next run of this application.
      Foreach *.order file that is couple with a corresponding *.order.pending file
        a query for the pending transaction is sent to the server in order
        to discover if it is still pending or else it is committed or aborted.
        In the first case the application retry the query after a while.
        In the two latter cases the file *.order.pending is renamed *.order.done.blabla
        because the corresponding transaction is finished.
      There are convenient command-line options to control the delay between
      two subsequents queries and two subsequents scan of *.order files.

  The application terminates when all *.order files are been analyzed.
  Please note that this may happens before the two subscriptions terminate
  the print of their historical data: i.e. not all data may been received.

*/
/*

Example Usage
=============

  To compile this example remember to put in the classpath:
     - The path of JDK 1.4.x (or following)
     - The path of your library JFTApi.jar 
     - The path of the directory where the metamarket package reside

  To launch this example type:
     java Example2 options...
  where options are: [-h host]             # FastTrack server TCP/IP host
                     [-p port]             # FastTrack server TCP/IP port
                     [-n serviceName]      # FastTrack service name
                     [-o opName]           # operator's name
                     [-w opPassword]       # operator's password
                     [-l licPathName]      # license file pathname
                     [-d dirPathName]      # directory with xxx.order[.pending] files
                     [-t traceLevel]       # 0<= traceLevel <= 5
                     [-v ON/OFF]           # trace verbose
                     [-s scanDelay]        # scan delay (in seconds)
                     [-q queryDelay]       # query delay (in seconds)
                     [-x ON/OFF]           # request to make a query after a send
  E.g.:
     java Example2 -h 194.91.195.1 -p 1234 -o dario -w dario -d c:\tmp -s 5 -q 15 -x ON
  requests
     - to talk with FastTrack server on host 194.91.195.1 and port 1234.
     - without specifying any service name (there is no "-n ..." option).
     - with operator name and password both equals to dario.
     - not using any license file (there is no "-l ..." option).
     - listing the directory C:\tmp.
     - using the default trace level TRACE_LEVEL_FATAL=5 (there is no "-t ..." option).
     - using the default non verbose trace (there is no "-v ..." option).
     - reading a new *.order and/or *.order.pending file every 5 seconds:
       i.e. every 5 seconds a send (if there is not a *.order.pending file)
                        or a query (if there is a *.order.pending file)
       will be sent to the server.
     - re-sending other query for the same transaction 15 seconds
       after a preceding flying (i.e. no commit and no abort) result.
     - requesting to automatically send a query after a send.


Additional Classes (in metamarket package)
==========================================

  In order to profitably use this example there is need for some additional
  Java classes in the metamarket package. These classes are:
    - MetaMarket         It contains global constants (entityClassIDs, keyIDs, etc...)
                         for all data structures of FastTrack MetaMarket service.
    - FT_C_ORDER         Specific EntityClass for orders handled by MetaMarket.
                         You can see below a skeleton of this class.
    - FT_C_TRADING_STATE Specific EntityClass for the handled trading states.
                         You can see below a skeleton of this class.
    - FT_C_ERROR_INFO    Specific EntityClass for the transaction errors communications
                         between the server and the application.
    - FT_C_TRADING_PHASE The various enumeration values for fields of FT_C_TRADING_STATE
    etc...

*/
/*

MetaMarket
==========
  MetaMarket is the Java class that contains global constants
  for all data structures of FastTrack MetaMarket service.

  This class contains
    - a lot of constants (for all entytClassIDs, keyIDs, etc...)
      that may be used to access all data handled by MetaMarket service.
    - a method registerAll() that may be used to register all
      the EntityClasses of MetaMarket.

  In this example we use only 3 EntityClasses:
  FT_C_TRADING_STATE, FT_C_ORDER and FT_C_ERROR_INFO
  so the only used members of the class MetaMarket are:
        
  public static final int FT_C_TRADING_STATE_ID = 30010; // FT_C_TRADING_STATE id
  public static final int FT_C_TRADING_STATEKey = 1;     // FT_C_TRADING_STATE prim. key
  public static final int FT_C_ORDER_ID         = 30014; // FT_C_ORDER id
  public static final int FT_C_ORDERKey         = 1;     // FT_C_ORDER primary key
  public static void registerAll();       // to register all EntityClasses of MetaMarket


The FT_C_TRADING_STATE EntityClass
==================================
  Its entityClassID is FT_C_TRADING_STATE_ID = 30010.
  Its primary keyID is FT_C_TRADING_STATEKey = 1
      and it includes ExchangeID, MarketID and SectionID fields.
  Its structure is something like:

class FT_C_TRADING_STATE {
   String ExchangeID; // ID of the market place
   String MarketID;   // ID of the market
   String SectionID;  // ID of the section
   int    Phase;      // Phase of the security:
                      // 0 or FT_C_TRADING_PHASE.FT_C_TRADING_PHASE_UNDEF
                      // 1 or FT_C_TRADING_PHASE.FT_C_TRADING_PHASE_CLOSURE
                      // 2 or FT_C_TRADING_PHASE.FT_C_TRADING_PHASE_PRE_ISSUE
                      // 3 or FT_C_TRADING_PHASE.FT_C_TRADING_PHASE_ISSUE
                      // 4 or FT_C_TRADING_PHASE.FT_C_TRADING_PHASE_PRE_AUCTION
                      // 5 or FT_C_TRADING_PHASE.FT_C_TRADING_PHASE_AUCTION
                      // 6 or FT_C_TRADING_PHASE.FT_C_TRADING_PHASE_POST_AUCTION
                      // 7 or FT_C_TRADING_PHASE.FT_C_TRADING_PHASE_PRE_TRADING
                      // 8 or FT_C_TRADING_PHASE.FT_C_TRADING_PHASE_TRADING
                      // 9 or FT_C_TRADING_PHASE.FT_C_TRADING_PHASE_POST_TRADING
                      //10 or FT_C_TRADING_PHASE.FT_C_TRADING_PHASE_TRADING_AT_LAST
                      //11 or FT_C_TRADING_PHASE.FT_C_TRADING_PHASE_TRADING_AFTER_HOUR
                      //12 or FT_C_TRADING_PHASE.FT_C_TRADING_PHASE_FAST_MARKET
                      //13 or FT_C_TRADING_PHASE.FT_C_TRADING_PHASE_MANAGEMENT
                      //14 or FT_C_TRADING_PHASE.FT_C_TRADING_PHASE_NO_OPERATION
   int    Status;     // Status of the section
                      // 0 or FT_C_TRADING_STATUS.FT_C_TRADING_STATUS_Active
                      // 1 or FT_C_TRADING_STATUS.FT_C_TRADING_STATUS_Suspended
                      // 2 or FT_C_TRADING_STATUS.FT_C_TRADING_STATUS_Frozen
   String PhaseDescription; // Description of phase
   int    Time;       // Time (format: HHMMSSmmm) of last change
}

*/
/* 

The FT_C_ORDER EntityClass
==========================
  Its entityClassID is FT_C_ORDER_ID = 30014.
  Its primary keyID is FT_C_ORDERKey = 1
      and it includes FTSecID and OrderID fields.
  Its structure is something like:

class FT_C_ORDER {
  String FTSecID;         // ID of the security
  String OrderID;         // ID of the order given by the market
  String OperatorID;      // Operator ID
  String MrkOperatorID;   // ID of the operator on the destination market
  int    Verb;            // Verb of the order:
                          // 0 or FT_C_VERB.FT_C_VERB_Buy
                          // 1 or FT_C_VERB.FT_C_VERB_Sell
  int    OrderType;       // Type of the order
                          // 0 or FT_C_ORDER_TYPE.FT_C_ORDER_TYPE_Limit
                          // 1 or FT_C_ORDER_TYPE.FT_C_ORDER_TYPE_Market
                          // 2 or FT_C_ORDER_TYPE.FT_C_ORDER_TYPE_Market_to_limit
                          // 3 or FT_C_ORDER_TYPE.FT_C_ORDER_TYPE_Stop_market
                          // 4 or FT_C_ORDER_TYPE.FT_C_ORDER_TYPE_OpeningPrice
                          // 5 or FT_C_ORDER_TYPE.FT_C_ORDER_TYPE_Stop_limit
                          // 6 or FT_C_ORDER_TYPE.FT_C_ORDER_TYPE_Subscription
  int    QtyParameter;    // Parameter to choose if the whole quantity of the
                          // quantity must be matched at the same time
                          // 0 or FT_C_QTY_PARAMETER.FT_C_QTY_PARAMETER_Default
                          // 1 or FT_C_QTY_PARAMETER.FT_C_QTY_PARAMETER_All_or_None
  int    TimeInForce;     // Parameter to determine the life of the order
                          // 0 or FT_C_TIMEINFORCE.FT_C_TIMEINFORCE_Default
                          // 1 or FT_C_TIMEINFORCE.FT_C_TIMEINFORCE_Day
                          // 2 or FT_C_TIMEINFORCE.FT_C_TIMEINFORCE_Good_till_Date
                          // 3 or FT_C_TIMEINFORCE.FT_C_TIMEINFORCE_Good_till_Cancel
                          // 4 or FT_C_TIMEINFORCE.FT_C_TIMEINFORCE_Immediate_or_Cancel
                          // 5 or FT_C_TIMEINFORCE.FT_C_TIMEINFORCE_Good_till_Maturity
                          // 6 or FT_C_TIMEINFORCE.FT_C_TIMEINFORCE_Good_till_Hour
                          // 7 or FT_C_TIMEINFORCE.FT_C_TIMEINFORCE_Cancel_after_Filled
  int    ValidityDate;    // Date (format: AAAAMMDD) of the end of the order's validity
  int    Status;          // Status of the order
                          // 0 or FT_C_ORDER_STATUS.FT_C_ORDER_STATUS_Active
                          // 1 or FT_C_ORDER_STATUS.FT_C_ORDER_STATUS_PartFilled
                          // 2 or FT_C_ORDER_STATUS.FT_C_ORDER_STATUS_CompFilled
                          // 3 or FT_C_ORDER_STATUS.FT_C_ORDER_STATUS_Cancelled
                          // 4 or FT_C_ORDER_STATUS.FT_C_ORDER_STATUS_Suspended
                          // 5 or FT_C_ORDER_STATUS.FT_C_ORDER_STATUS_CancelledByGov
                          // 6 or FT_C_ORDER_STATUS.FT_C_ORDER_STATUS_Stopped
                          // 7 or FT_C_ORDER_STATUS.FT_C_ORDER_STATUS_Submitted
                          // 8 or FT_C_ORDER_STATUS.FT_C_ORDER_STATUS_Rejected
                          // 9 or FT_C_ORDER_STATUS.FT_C_ORDER_STATUS_DeletedByEdit
  double Price;           // Limit price of the order
  double Qty;             // Quantity of the order
  double StopPrice;       // Price that triggers a stop order
  int    TriggerMechanism;// Activaction rule for stop orders
                          // 0 or FT_C_STOP_TRIGGER_MECHANISM
                          //      .FT_C_STOP_TRIGGER_MECHANISM_BestPrice
                          // 1 or FT_C_STOP_TRIGGER_MECHANISM
                          //      .FT_C_STOP_TRIGGER_MECHANISM_LastPrice
}

*/
/* 

The FT_C_ERROR_INFO EntityClass
===============================
  Its entityClassID is FT_C_ERROR_INFO_ID = 30050.
  It does not have any key.
  Its structure is something like:

class FT_C_ERROR_INFO {
  int    ReasonCode;
  String ErrorString;
}

The FT_C_TRADING_PHASE class
============================
  This java class is not an EntityClass.
  This class contains
    - all enumeration value for the trading phase,
    - a method enumAsString that returns a displayable String for a given value.
  Its structure is something like:

class FT_C_TRADING_PHASE {
  public static final int FT_C_TRADING_PHASE_UNDEF              =  0;
  public static final int FT_C_TRADING_PHASE_CLOSURE            =  1;
  public static final int FT_C_TRADING_PHASE_PRE_ISSUE          =  2;
  public static final int FT_C_TRADING_PHASE_ISSUE              =  3;
  public static final int FT_C_TRADING_PHASE_PRE_AUCTION        =  4;
  public static final int FT_C_TRADING_PHASE_AUCTION            =  5;
  public static final int FT_C_TRADING_PHASE_POST_AUCTION       =  6;
  public static final int FT_C_TRADING_PHASE_PRE_TRADING        =  7;
  public static final int FT_C_TRADING_PHASE_TRADING            =  8;
  public static final int FT_C_TRADING_PHASE_POST_TRADING       =  9;
  public static final int FT_C_TRADING_PHASE_TRADING_AT_LAST    = 10;
  public static final int FT_C_TRADING_PHASE_TRADING_AFTER_HOUR = 11;
  public static final int FT_C_TRADING_PHASE_FAST_MARKET        = 12;
  public static final int FT_C_TRADING_PHASE_MANAGEMENT         = 13;
  public static final int FT_C_TRADING_PHASE_NO_OPERATION       = 14;

  public static final String enumAsString(int value) { return "an appropriate value"; }
}

*/


// Effective source-code Example2 starts here.

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.BufferedReader;
import java.io.PrintWriter;
import java.io.FileReader;
import java.io.File;
import it.list.jft.*;       // to use the JFT/Api library
import it.list.jft.event.*; // to use the JFT/Api library
import metamarket.*;        // to use MetaMarket, FT_C_ORDER, FT_C_TRADING_STATE, etc...

class Example2 extends UtilityExample implements Runnable {

  static final String      O_SUFFIX       = ".order";
  static final String      O_SUFFIX_READ  = O_SUFFIX + ".read";
  static final String      T_SUFFIX       = ".pending";
  static final String      T_SUFFIX_DONE  = ".done." + System.currentTimeMillis();
  static final String      EXCHANGE_ID    = "HDAT";
  static final int         SUBS_CLIENT_ID = 67890;
  static final int         TRANS_CLIENT_ID= 67891;
  static final int         PHASE_TRADING  = FT_C_TRADING_PHASE.
                                            FT_C_TRADING_PHASE_TRADING;
  static final ThreadGroup THREAD_GROUP   = new ThreadGroupExample();
  static       String      licPath        = null;
  static       String      dirPath        = "c:\\";
  static       String      myOperatorID   = "dario";
  static       String      myOperatorPass = "*";
  static       String      host           = "metamarket.fasttrack.com";
  static       String      service        = null;
  static       int         port           = 1234;
  static       int         delayScanSecs  = 10;
  static       int         delayQuerySecs = 3;
  static       int         traceLevel     = JFT.TRACE_LEVEL_FATAL;
  static       boolean     queryAfterSend = false;
  static       boolean     verbose        = false;
  static       Context     context        = null;

  public static void main(String[]args) {
    handleArgs(args);
    new Thread(THREAD_GROUP, new Example2(), "main").start();
  }


  public void run() {
    JFT.THIS.init(JFT.MODE_MULTI_THREAD);
    JFT.THIS.setTrace(true);
    JFT.THIS.setTraceLevel(traceLevel);
    JFT.THIS.setTraceMode(this);
    if(true) {
      JFT.THIS.register(new FT_C_ORDER());
      JFT.THIS.register(new FT_C_TRADING_STATE());
      JFT.THIS.register(new FT_C_ERROR_INFO());
    } else // as an expensive alternative
      MetaMarket.registerAll();
    JFT.THIS.start();
    context = JFT.THIS.makeContext();
    new ConnectionForSubscriptions();
    new ConnectionForTransactions();
  }


  // Just a remind on usage...

  static void usage() {
    final String[]usage=
      {"Usage: java Example options...",
       "options: [-h host]         # FastTrack server TCP/IP host",
       "         [-p port]         # FastTrack server TCP/IP port",
       "         [-n serviceName]  # FastTrack service name",
       "         [-o opName]       # operator's name",
       "         [-w opPassword]   # operator's password",
       "         [-l licPathName]  # license file pathname",
       "         [-d dirPathName]  # directory with xxx.order[.trans] files",
       "         [-t traceLevel]   # 0 <= traceLevel <= 5",
       "         [-v ON/OFF]       # trace verbose",
       "         [-s scanDelay]    # scan delay (in seconds)",
       "         [-q queryDelay]   # query delay (in seconds)",
       "         [-x ON/OFF]       # request to make a query after a send"};
    for(int i=0; i<usage.length; i++)
      System.out.println(usage[i]);
    System.exit(0);
  }
  // Parse command-line options.

  static void handleArgs(String[]args) {
    try {
      for(int i=0; i<args.length; i+=2)
        if(args[i].equals("-h"))
          host = args[i+1];
        else if(args[i].equals("-p"))
          port = Integer.parseInt(args[i+1]);
        else if(args[i].equals("-n"))
          service = args[i+1];
        else if(args[i].equals("-o"))
          myOperatorID = args[i+1];
        else if(args[i].equals("-w"))
          myOperatorPass = args[i+1];
        else if(args[i].equals("-l"))
          licPath = args[i+1];
        else if(args[i].equals("-t"))
          traceLevel = Integer.parseInt(args[i+1]);
        else if(args[i].equals("-d"))
          dirPath = args[i+1];
        else if(args[i].equals("-v"))
          verbose = args[i+1].compareToIgnoreCase("ON") == 0;
        else if(args[i].equals("-s"))
          delayScanSecs = Integer.parseInt(args[i+1]);
        else if(args[i].equals("-q"))
          delayQuerySecs = Integer.parseInt(args[i+1]);
        else if(args[i].equals("-x"))
          queryAfterSend = args[i+1].compareToIgnoreCase("ON") == 0;
        else
          throw new IllegalArgumentException();
      if(   args.length == 0
         || host.length() == 0
         || port <= 0
         || service != null && service.length() == 0
         || myOperatorID.length() == 0
         || myOperatorPass.length() == 0
         || traceLevel < JFT.TRACE_LEVEL_DEBUG
         || traceLevel > JFT.TRACE_LEVEL_FATAL
         || licPath != null && ! new File(licPath).canRead()
         || ! new File(dirPath).isDirectory()
         || delayScanSecs < 0
         || delayQuerySecs < 0)
        throw new IllegalArgumentException();
    } catch(Exception e) {
      usage();
    }
  }
}
// Common superclass for all connections.

abstract class ConnectionExample extends UtilityExample
                                 implements ConnectionListener {
         final Connection connection;

  ConnectionExample(int connectionUserType, int clientID) {
    ConnectionParam cp = Example2.context.makeConnectionParam();
    cp.setHost(Example2.host);
    cp.setPort(Example2.port);
    cp.setService(Example2.service);
    cp.setApplRevision(new int[]{0,0,0});
    cp.setApplSignature(12345);
    cp.setAuthFile(Example2.licPath == null ? null : new File(Example2.licPath));
    cp.setClientID(clientID);
    cp.setConnType(ConnectionParam.CONN_TYPE_TCP);
    cp.setUserName(Example2.myOperatorID);
    cp.setPassword(Example2.myOperatorPass);
    cp.setUserType(connectionUserType);
    connection = Example2.context.makeConnection(cp, this);
    int res = connection.open();
    trace(res);
    if(res != Connection.RESULT_OK)
      connection.release(); // good practice
  }

  public void onConnectionOpen(ConnectionOpenEvent ev) {
    int res = ev.getResult();
    trace(res);
    if(res == ev.RESULT_OK) {
      int[]mrkRev = ev.getMarketRevision();
      trace("csID: " + ev.getClientServiceID() + " bsID: " + ev.getBusinessServiceID()
            + " date: " + UtilityExample.sdf.format(ev.getSystemDateTime())
            + " FTID: " + ev.getFTID() + " env: " + ev.getEnvironment()
            + " mrkRev: " + mrkRev[0] + "." + mrkRev[1] + "." + mrkRev[2]);
    } else
      connection.release(); // good practice
  }

  public void onConnectionClose(ConnectionCloseEvent ev) {
    trace(ev.getResult());
    connection.release(); // good practice
  }

  public void onConnectionLost(ConnectionLostEvent ev) {
    trace(ev.getResult());
    connection.release(); // good practice
  }
}
// A specific connection: it handles many subscriptions.

class ConnectionForSubscriptions extends ConnectionExample {

  ConnectionForSubscriptions() {
    super(ConnectionParam.USER_TYPE_VIEW, Example2.SUBS_CLIENT_ID);
  }

  public void onConnectionOpen(ConnectionOpenEvent ev) {
    super.onConnectionOpen(ev); // call the overridden method
    if(ev.getResult() == ev.RESULT_OK) {
      new SubscriptionOrder(connection);
      new SubscriptionTradingState(connection);
    }
  }
}
// Common superclass for all subscriptions.

abstract class SubscriptionExample extends UtilityExample
                                   implements SubscriptionListener {
  final Subscription subscription;

  SubscriptionExample(Connection connection) {
    subscription = Example2.context.makeSubscription(connection,
                                                    makeSubscriptionParam(), this);
    int res = subscription.start();
    trace(res);
    if(res != Subscription.RESULT_OK) 
      subscription.release(); // good practice
  }

  abstract SubscriptionParam makeSubscriptionParam();

  abstract String entityAsString(Entity e);

  public void onSubscriptionStart(SubscriptionStartEvent ev){
    trace("Result=" + ev.getResult() +
          (ev.getResult() == ev.RESULT_OK ?
           " version: " + ev.getEntityClassVersionOnServer() +
           " reset: " + ev.isEntityClassReset() : ""));
    if(ev.getResult() != ev.RESULT_OK)
      subscription.release(); // good practice
  }

  public void onSubscriptionIdle(SubscriptionIdleEvent ev){
    trace(ev.getResult());
  }

  public void onSubscriptionNotify(SubscriptionNotifyEvent ev){
    switch(ev.getAction()) {
    case SubscriptionNotifyEvent.ACTION_ENTITY_ADD:
    case SubscriptionNotifyEvent.ACTION_ENTITY_RWT:
      trace((ev.getAction() == ev.ACTION_ENTITY_ADD ? "ADD" : "RWT")
            + " Masked: " + ev.isMasked() + " " + entityAsString(ev.getEntity()));
      break;
    case SubscriptionNotifyEvent.ACTION_ENTITY_DEL:
      trace("DEL KeyID: " + ev.getKeyID());
      break;
    case SubscriptionNotifyEvent.ACTION_ENTITY_KIL:
      if(ev.getKeyID() <= 0)
        trace("KIL ClassReset - New Version: " + ev.getTimeStamp().getDateTime());
      else
        trace("KIL KeyID: " + ev.getKeyID());
      break;
    }
  }

  public void onSubscriptionStop(SubscriptionStopEvent ev){
    trace(ev.getResult());
    subscription.release(); // good practice
  }
}
// A specific subscription: it handles FT_C_ORDER.

class SubscriptionOrder extends SubscriptionExample {

  SubscriptionOrder(Connection connection) {
    super(connection);
  }

  SubscriptionParam makeSubscriptionParam() {
    SubscriptionParam sp = Example2.context.makeSubscriptionParam();
    sp.setEntityClassID(MetaMarket.FT_C_ORDER_ID);
    return sp;
  }

  String entityAsString(Entity e) {
    FT_C_ORDER o = (FT_C_ORDER) e;
    return (o.OperatorID.equals(Example2.myOperatorID) ? "" : "NO_OWNER")
      + " OrderID: "      + o.OrderID
      + " FTSecID: "      + o.FTSecID
      + " OperatorID: "   + o.OperatorID
      + " Price: "        + o.Price
      + " Qty: "          + o.Qty
      + " Verb: "         + FT_C_VERB.enumAsString(o.Verb)
      + " ValidityDate: " + o.ValidityDate;
  }

  public void onSubscriptionNotify(SubscriptionNotifyEvent ev){
    super.onSubscriptionNotify(ev);  // call the overridden method
     FT_C_ORDER o = (FT_C_ORDER) ev.getEntity();
     if(! o.OperatorID.equals(Example2.myOperatorID))
       writeOrder(o);
  }

  void writeOrder(FT_C_ORDER o) {
    PrintWriter pw = null;
    try {
      pw = new PrintWriter(new FileOutputStream(new File(
                           Example2.dirPath, o.FTSecID + Example2.O_SUFFIX_READ)));
      pw.println(o.FTSecID);
      pw.println(o.OrderID);
      pw.println(o.OperatorID);
      pw.println(o.MrkOperatorID);
      pw.println(o.Verb);
      pw.println(o.OrderType);
      pw.println(o.QtyParameter);
      pw.println(o.TimeInForce);
      pw.println(o.ValidityDate);
      pw.println(o.Status);
      pw.println(o.Price);
      pw.println(o.Qty);
      pw.println(o.StopPrice);
      pw.println(o.TriggerMechanism);
      trace("order file written.");
    } catch(Exception e) {
      trace(e);
    } finally {
      try {pw.close();} catch(Exception e) {}
    }
  }
}
// Another specific subscription: it handles FT_C_TRADING_STATE.

class SubscriptionTradingState extends SubscriptionExample {

  int counter; // count the # of refreshEntity requested

  SubscriptionTradingState(Connection connection) {
    super(connection);
  }

  SubscriptionParam makeSubscriptionParam() {
    Mask m = JFT.THIS.makeEmptyMask(MetaMarket.FT_C_TRADING_STATE_ID);
    m.addFieldByName("ExchangeID"); // primary key field
    m.addFieldByName("MarketID");   // primary key field
    m.addFieldByName("SectionID");  // primary key field
    m.addFieldByName("Phase");      // what I'm searching!
    SubscriptionParam sp = Example2.context.makeSubscriptionParam();
    sp.setEntityClassID(MetaMarket.FT_C_TRADING_STATE_ID);
    sp.setMask(m);
    sp.setQueryType(sp.QUERY_TYPE_SET);
    FT_C_TRADING_STATE ts = new FT_C_TRADING_STATE();
    ts.ExchangeID = Example2.EXCHANGE_ID;
    sp.setEntityKey(ts.getPartialEntityKey(MetaMarket.FT_C_TRADING_STATEKey, 1));
    return sp;
  }

  String entityAsString(Entity e) {
    FT_C_TRADING_STATE ts = (FT_C_TRADING_STATE) e;
    return "ExchangeID: " + ts.ExchangeID // in mask and partial key subscribed
        + " MarketID: "   + ts.MarketID                              // in mask
        + " SectionID: "  + ts.SectionID                             // in mask
        + " Phase: "      + FT_C_TRADING_PHASE.enumAsString(ts.Phase)// in mask
        + " Status: "     + FT_C_TRADING_STATUS.enumAsString(ts.Status)
        + " PhaseDesc: "  + ts.PhaseDescription
        + " Time: "       + ts.Time;
  }

  public void onSubscriptionIdle(SubscriptionIdleEvent ev){
    super.onSubscriptionIdle(ev); // call the overridden method
    checkStop();
  }

  public void onSubscriptionNotify(SubscriptionNotifyEvent ev){
    super.onSubscriptionNotify(ev); // call the overridden method
    if(ev.isMasked()) {
      FT_C_TRADING_STATE ts = (FT_C_TRADING_STATE) ev.getEntity();
      if(ts.Phase == Example2.PHASE_TRADING) {
        int res = subscription.refreshEntity(ts.getFullEntityKey(ev.getKeyID()));
        trace(res);
        if(res == subscription.RESULT_OK)
          counter++;
      }
    } else {
      counter--;
      checkStop();
    }
  }
  void checkStop() {
    if(counter <= 0) { // Now I'm no more interested in this subscription
      int res = subscription.stop();
      trace(res);
      subscription.release(); // good practice
    }
  }
}


// Another specific connection: it handles many transactions.

class ConnectionForTransactions extends ConnectionExample
                                implements FilenameFilter, Runnable{
  String[]files;

  ConnectionForTransactions() {
    super(ConnectionParam.USER_TYPE_TRADER, Example2.TRANS_CLIENT_ID);
  }

  public void onConnectionOpen(ConnectionOpenEvent ev) {
    super.onConnectionOpen(ev);  // call the overridden method
    if(ev.getResult() == ev.RESULT_OK) {
      files = new File(Example2.dirPath).list(this);
      int n = (files == null) ? 0 : files.length;
      trace(n + " orders to be sent or monitored");
      if(n > 1)
        Arrays.sort(files);
      new Thread(Example2.THREAD_GROUP, this, "listing").start();
    }
  }

  public boolean accept(File dir, String name) {
    return name.endsWith(Example2.O_SUFFIX) && ! name.startsWith(Example2.O_SUFFIX);
  }

  public void run() {
    sleep(Example2.delayScanSecs);
    if(files != null)
      for(int i=0; i<files.length; i++)
        try {
          String filename = files[i] + Example2.T_SUFFIX;
          File f = new File(Example2.dirPath, filename);
          if(f.canRead()) {
            trace("analyzing file " + filename + ": pending trans. to be monitored");
            new TransactionPending(connection, files[i]);
          } else {
            trace("analyzing file " + files[i] + ": new transaction to be created");
            new TransactionNew(connection, files[i]);
          }
          sleep(Example2.delayScanSecs);
        } catch(Exception e) {
          trace(i + " -> " + e);
        }
    JFT.THIS.release();
    trace("JFT library released -> application going to die");
    // no need to explicitly call System.exit() here !
  }
}
// Common superclass for all transactions.

abstract class TransactionExample extends UtilityExample
                                  implements TransactionListener, Runnable {
  final Transaction transaction;
  final String      filename;
        int         counter;

  TransactionExample(Connection connection, String f) {
    super(f);
    filename = f;
    transaction = Example2.context.makeTransaction(connection,
                                                  makeTransactionParam(), this);
    tidWrite();
  }

  abstract TransactionParam makeTransactionParam();

  String reasonAsString(TransactionEvent ev) {
    int reason = ev.getReasonCode();
    FT_C_ERROR_INFO ei = (FT_C_ERROR_INFO) ev.getEntity();
    return reason == 0 ? "" :
      (" reason: " + reason + (ei == null ? "" : " -> " + ei.ErrorString));
  }

  void onTransaction(TransactionEvent ev, String whoami) {
    int st = transaction.getStatus();
    trace(whoami
          + ev.getResult()
          + (st == transaction.STATUS_FLYING ? " FLYING"
             : (st == transaction.STATUS_ABORTED ? " ABORTED"
                : (st == transaction.STATUS_COMMITTED ? " COMMITTED" : " " + st)))
          + reasonAsString(ev));
    if(st == transaction.STATUS_FLYING)
       if(Example2.queryAfterSend)
         queryDelayed();
       else
         transaction.release(); // Now I'm no more interested in this transaction
    else
      destroy();
  }

  public void onTransactionSend(TransactionSendEvent ev) {
    onTransaction(ev, "onTransactionSend -> ");
  }

  public void onTransactionQuery(TransactionQueryEvent ev) {
    onTransaction(ev, "onTransactionQuery -> ");
  }

  void destroy() {
    tidRemove();
    transaction.release(); // Now I'm no more interested in this transaction
  }

  void query() {
    int res = transaction.query();
    trace(res);
    if(res != transaction.RESULT_OK)
      destroy();
  }
  void queryDelayed() {
    new Thread(Example2.THREAD_GROUP, this,
               "query " + (++counter) + " on " + filename).start();
  }

  TransactionID tidRead() {
    BufferedReader in = null;
    try {
      in = new BufferedReader(new FileReader(new File(Example2.dirPath,
                                                      filename + Example2.T_SUFFIX)));
      TransactionID tid = JFT.THIS.makeTransactionID(
                              Integer.parseInt(in.readLine()),
                              Integer.parseInt(in.readLine()),
                              Integer.parseInt(in.readLine()),
                              JFT.THIS.makeTimeStamp(Integer.parseInt(in.readLine()),
                                                     Integer.parseInt(in.readLine())));
      trace("transaction file read.");
      return tid;
    } catch(Exception e) {
      trace(e);
      return null;
    } finally {
      try {in.close();} catch(Exception e) {}
    }
  }

  void tidWrite() {
    TransactionID tid = transaction.getTransactionID();
    PrintWriter   pw  = null;
    try {
      pw = new PrintWriter(new FileOutputStream(new File(Example2.dirPath,
                                                         filename+Example2.T_SUFFIX)));
      pw.println(tid.getClientID());
      pw.println(tid.getClientServiceID());
      pw.println(tid.getBusinessServiceID());
      pw.println(tid.getTimeStamp().getDateTime());
      pw.println(tid.getTimeStamp().getProg());
      trace("transaction file written.");
    } catch(Exception e) {
      trace(e);
    } finally {
      try {pw.close();} catch(Exception e) {}
    }
  }

  void tidRemove() {
      File oldF = new File(Example2.dirPath, filename + Example2.T_SUFFIX);
      File newF = new File(Example2.dirPath, filename + Example2.T_SUFFIX_DONE);
      boolean ok = oldF.renameTo(newF);
      trace("transaction file renamed: " + ok);
  }

  public void run(){
    try {
      sleep(Example2.delayQuerySecs);
      query();
    } catch(Exception e) {
      trace(e);
    }
  }
}
class TransactionNew extends TransactionExample { // specific new transaction

  TransactionNew(Connection connection, String f) {
    super(connection, f);
    int res = transaction.send();
    trace(res);
    if(res != transaction.RESULT_OK)
      destroy();
  }

  TransactionParam makeTransactionParam() {
    TransactionParam tp = Example2.context.makeTransactionParam();
    tp.setAction(TransactionParam.ACTION_ENTITY_ADD);
    tp.setEntity(readOrder());
    tp.setKeyID(MetaMarket.FT_C_ORDERKey);
    return tp;
  }

  FT_C_ORDER readOrder() {
    BufferedReader in = null;
    try {
      FT_C_ORDER order  = new FT_C_ORDER();
      in = new BufferedReader(new FileReader(new File(Example2.dirPath, filename)));
      order.FTSecID          = in.readLine();
      order.OrderID          = in.readLine();
      order.OperatorID       = in.readLine(); // overwritten below !
      order.MrkOperatorID    = in.readLine();
      order.Verb             = Integer.parseInt(in.readLine());
      order.OrderType        = Integer.parseInt(in.readLine());
      order.QtyParameter     = Integer.parseInt(in.readLine());
      order.TimeInForce      = Integer.parseInt(in.readLine());
      order.ValidityDate     = Integer.parseInt(in.readLine());
      order.Status           = Integer.parseInt(in.readLine());
      order.Price            = Double.parseDouble(in.readLine());
      order.Qty              = Double.parseDouble(in.readLine());
      order.StopPrice        = Double.parseDouble(in.readLine());
      order.TriggerMechanism = Integer.parseInt(in.readLine());
      order.OperatorID = Example2.myOperatorID; // overwritten with the right value !
      trace("file read");
      return order;
    } catch(Exception e) {
      trace(e);
      return null;
    } finally {
      try {in.close();} catch(Exception e) {}
    }
  }
}

class TransactionPending extends TransactionExample { // specific past pending transact.
  TransactionPending(Connection connection, String f) {
    super(connection, f);
    query();
  }

  TransactionParam makeTransactionParam() {
    TransactionParam tp = Example2.context.makeTransactionParam();
    tp.setPendingTransactionID(tidRead());
    return tp;
  }
}
// Common superclass for all classes of this example.

abstract class UtilityExample implements Tracer {
  static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
         final String           specificName;

  UtilityExample() {
    specificName = "";
  }

  UtilityExample(String name) {
    specificName = "<" + name + ">";
  }

  void internalTrace(String line) {
    String m = getClass().getName() + specificName;
    Throwable t = new Throwable();
    StackTraceElement[] ste = t.getStackTrace();
    if(ste != null && ste.length >= 3) {
      m += "." + ste[2].getMethodName();
      if(Example2.verbose) {
        String fn = ste[2].getFileName();
        int ln = ste[2].getLineNumber();
        m += "(" + (fn == null ? "" : fn) + (ln < 0 ? "" : ":" + ln) + ")";
      }
    }
    JFT.THIS.trace(m, JFT.TRACE_LEVEL_FATAL, line);
    // In this simple example the level is always TRACE_LEVEL_FATAL
    // so we will see our application trace
    // whichever "-t ..." command-line option was choosen.
  }

  void trace(String message) { internalTrace(message); }

  void trace(int res)        { internalTrace("Result=" + res); }

  void trace(Exception e)    { internalTrace("Exception: " + e); }

  public void onTrace(Date t, String m, int l, String ms) {
    String v = Example2.verbose ?
      sdf.format(t) + " " + l + " [" + Thread.currentThread().getName() + "] " : "";
    System.out.println(v + "[" + m + "] " + ms);
  }

  void sleep(int intervalSecs) {
    try {
      Thread.sleep(intervalSecs * 1000L);
    } catch(InterruptedException ie) {
      trace(ie);
    }
  }
}
// Specific ThreadGroup to handle in an uniform way all generated exceptions.

class ThreadGroupExample extends ThreadGroup {

  ThreadGroupExample() {
    super("Example");
  }

  public void uncaughtException(Thread t, Throwable e) {
    if(e instanceof ThreadDeath)
      super.uncaughtException(t, e); // call the overridden method
    else {
      System.out.println("Uncaught Exception in thread " + t.getName());
      e.printStackTrace(System.out);
      System.exit(0);
    }
  }
}