0
\$\begingroup\$

I am writing a program that utilizes a third party API. I attach a handler to an object and wait for a message back. the responses from the server then get saved into an array list. Unfortunately it takes time for my program to contact the server and so if I make reference to the array list before the server has time to get back to me, my program thinks the list is empty. I've tried multiple different approaches involving threads but so far the only thing that works is just having a pre-set wait time (e.g. 2 seconds).

This doesn't seem very elegant and I'm worried might be error prone - if it take more than two seconds some time. Also because my program depends heavily on time efficiency, as you can imagine any wasted time is not good. Are there any better ways of waiting until the ArrayList is populated that you can think of?

public class Trader implements IConnectionHandler, Runnable {

    static Trader INSTANCE = new Trader();
    static int port = 4001;
    static int clientId = 0;

    JFrame frame = new JFrame("Trader");
    Panel panel;
    JLabel connectionStatus = new JLabel("Disconnected");
    Dimension frameSize;

    JTextArea msgBox = new JTextArea();
    Logger logger = new Logger(msgBox);

    ApiController m_controller = new ApiController(this, logger, logger);

    public ArrayList<String> accountList() {
        return m_acctList;
    }

    public ApiController controller() {
        return m_controller;
    }

    public JFrame frame() {
        return frame;
    }

    final ArrayList<String> m_acctList = new ArrayList<String>();
    ArrayList<Quotes.Stock> stockList = new ArrayList();
    ArrayList<TradeHistory.Completed> tradeList = new ArrayList();
    ArrayList<OpenOrders.Orders> ordersList = new ArrayList();

    ExecutorService ex = Executors.newFixedThreadPool(1);

    public static void main(String[] args) {

        INSTANCE.start();
    }

    public synchronized void start() {

        frame.setSize(600, 400);
        frameSize = frame.getSize();
        panel = new Panel();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(panel);
        frame.setVisible(true);

        m_controller.connect(null, port, clientId);

        try {
            wait(1000);
        } catch (Exception ex) {
            msgBox.append("error with wait");
        }

        Quotes quotes = new Quotes();
        stockList = quotes.stockList;
        show("\n");

        try {
            wait(2000);
        } catch (Exception ex) {
            msgBox.append("error with wait");
        }
        msgBox.setText(null);

        TradeHistory tradeHistory = new TradeHistory();
        tradeList = tradeHistory.tradeHistory;
        show("\n");

        OpenOrders openOrders = new OpenOrders();
        ordersList = openOrders.m_orders;

        msgBox.append("Getting Live Orders: \n");
        ex.execute(this);
        try {
            wait();
        } catch (Exception ex) {
            msgBox.append("error with wait - test\n");
        }
        ex.shutdown();

        for (int i = 0; i < ordersList.size(); i++) {
            msgBox.append("Symbol: " + ordersList.get(i).m_contract.symbol() + " Order ID: "
                    + String.valueOf(ordersList.get(i).m_order.orderId()) + " Action: "
                    + ordersList.get(i).m_order.action() + " Quantity: "
                    + ordersList.get(i).m_order.totalQuantity() + "\n");
        }

        try {
            wait(2000);
        } catch (Exception ex) {
            msgBox.append("error with wait");
        }
        msgBox.setText(null);

        Ordering ordering = new Ordering();

    }

    @Override
    public synchronized void run() {
        boolean state = true;
        while (state == true) {
            if (!ordersList.isEmpty()) {
                notifyAll();
                state = !state;
            }
        }
    }
}

When I get rid of the executorservice and make wait() into a fixed time, it works fine. But as stated, I'm hoping there is a more elegant way like what I'm trying with threads which will know the moment the appropriate response is received.

I'll just show you the OpenOrders class, but all the other classes (Quotes and tradeHistory) work essentially the same way.

public class OpenOrders implements ILiveOrderHandler {

    ApiController m_controller = Trader.INSTANCE.m_controller;
    JTextArea msgBox = Trader.INSTANCE.msgBox;

    private HashMap<Long, Orders> m_map = new HashMap<Long, Orders>();
    ArrayList<Orders> m_orders = new ArrayList<Orders>();

    OpenOrders() {
        m_orders.clear();
        Trader.INSTANCE.m_controller.reqLiveOrders(this);

    }

    @Override
    public void openOrder(NewContract contract, NewOrder order, NewOrderState orderState) {
        Orders full = m_map.get(order.permId());

        if (full != null) {
            full.m_order = order;
            full.m_state = orderState;
        } else if (shouldAdd(contract, order, orderState)) {
            full = new Orders(contract, order, orderState);
            add(full);
            m_map.put(order.permId(), full);
        }
    }

    protected boolean shouldAdd(NewContract contract, NewOrder order, NewOrderState orderState) {
        return true;
    }

    protected void add(Orders full) {
        m_orders.add(full);
    }

    @Override
    public void openOrderEnd() {
    }

    @Override
    public void orderStatus(int orderId, OrderStatus status, int filled, int remaining,
            double avgFillPrice, long permId, int parentId, double lastFillPrice, int clientId,
            String whyHeld) {
        Orders full = m_map.get(permId);
        if (full != null) {
            full.m_state.status(status);
        }
    }

    @Override
    public void handle(int orderId, int errorCode, String errorMsg) {
        throw new UnsupportedOperationException("Not supported yet."); // To change body of
                                                                       // generated methods, choose
                                                                       // Tools | Templates.
    }

    static class Orders {
        NewContract m_contract;
        NewOrder m_order;
        NewOrderState m_state;

        Orders(NewContract contract, NewOrder order, NewOrderState state) {
            m_contract = contract;
            m_order = order;
            m_state = state;
        }
    }

    static class Key {
        int m_clientId;
        int m_orderId;

        Key(int clientId, int orderId) {
            m_clientId = clientId;
            m_orderId = orderId;
        }
    }
}
\$\endgroup\$
3
  • \$\begingroup\$ You should look at code.google.com/p/awaitility for such things. It might not exactly fit your needs but it's my preferred tool for doing such waits. \$\endgroup\$
    – evanchooly
    Aug 11, 2014 at 15:54
  • \$\begingroup\$ You want a SwingWorker. \$\endgroup\$
    – jtahlborn
    Aug 11, 2014 at 15:55
  • \$\begingroup\$ Do not edit your question to change the entire thing. You made all the existing answers nonsensical. Ask a new question if you have one. If you are question-banned, start working towards fixing that instead of attempting to circumvent it. \$\endgroup\$
    – Blorgbeard
    Aug 11, 2014 at 20:07

2 Answers 2

2
\$\begingroup\$

Since you're using an ExecutorService, you can call submit instead of execute. submit returns a Future object, which has a get method that will block until the Future finishes executing, is canceled (throws a CancellationException), or is interrupted (throws an InterrupedException).

Note that get will return null unless you use one of the other forms of submit.

\$\endgroup\$
4
  • \$\begingroup\$ I like the sound of this option but, I dont suppose youd be able to give more insight in how submit might be implemented differently than I was using just the plain run method? I do like the idea but because theres a lot of backend things going on with the API, I'm not sure I know what the future would be returning or what would be executed beyond the checking if the ArrayList is empty or not \$\endgroup\$
    – user3256725
    Aug 11, 2014 at 16:45
  • \$\begingroup\$ A little more info on what your aiming at would be greatly appreciated. Thanks again \$\endgroup\$
    – user3256725
    Aug 11, 2014 at 16:46
  • \$\begingroup\$ Right now you're calling the ExecutorService's execute command. Which just "Executes the given command at some time in the future. The command may execute in a new thread, in a pooled thread, or in the calling thread, at the discretion of the Executor implementation." \$\endgroup\$
    – Powerlord
    Aug 11, 2014 at 17:11
  • \$\begingroup\$ As opposed to the ExectorService's submit command. Which just "Submits a Runnable task for execution and returns a Future representing that task. The Future's get method will return null upon successful completion." One of these commands calls the other, it's just not clear as to which calls which (as that's an implementation detail). \$\endgroup\$
    – Powerlord
    Aug 11, 2014 at 17:13
-1
\$\begingroup\$

You could use the human strategy: we look at the console until the logger gives some message ".... initialized". So add your own Logger/Handler. That will send a signal (abstractly spoken). Maybe with a failure time-out.

Now you can start the work on receiving the signal (callback). Or if that work is main thing, wait on the signal (semaphore, Future.get).

\$\endgroup\$
3
  • \$\begingroup\$ Unfortunataly there will be no human element in the final design. it will run completely autonomously. Thank you for the input though. \$\endgroup\$
    – user3256725
    Aug 11, 2014 at 16:31
  • \$\begingroup\$ I meant you might let a self-written Logger/Handler wait till some log message comes saying the system is up-and-running. \$\endgroup\$
    – Joop Eggen
    Aug 11, 2014 at 16:42
  • \$\begingroup\$ ah I see. do you know of any Oracle Documentation that might help out with writing a custom Logger? I'm quite new to this java thing and am quite eager for helpful guidance \$\endgroup\$
    – user3256725
    Aug 11, 2014 at 16:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.