0

I have built a Spring CLI app which communicates with a server in an async fashion. The server was given, I did not create it, basically my app is required to open a TCP socket and send a JSON through it, then it sends back a JSON. It is mandatory not to use CLI parameters, but instead in the callback of the request I want to show the user a set of options for which he needs to select by inserting the corresponding number on the CLI. Most probably I'm not doing right something, because after entering the command, I see spring> on the console (this is an expected behavior) and it will block the async callback unless I press something (nothing is printed to the CLI when I receive the callback unless I press a bunch of enters - this is unexpected). To read from the console so far I used JLine's command line, what I would like to achieve is that when I get the response from the server and the callback is served, the console is given to the thread on which the callback is running (I instantly print the contents of the callback to the console, and I'm able to read the input without any tricks).

Some code:

public void runReceiver(){
    receiverThread = new Thread(() -> {
        byte[] digit = null;
        int nb;
        Iterator<CommandListener> it;
        CommandListener listener;
        String message;
        List<CommandListener> listenersToRemove = new ArrayList<>();
        while (true) {
            try {
                nb = communicatorInput.readInt();
                digit = new byte[nb];
                communicatorInput.readFully(digit);
            } catch (IOException e) {
                e.printStackTrace();
            }

            it = listeners.iterator();
            while (it.hasNext()){
                listener = it.next();

                if (digit != null && digit.length > 0) {
                    message = new String(digit);
                    // the message was not acknowledged
                    if(message.contains("NACK")){
                        try {
                            listener.onError(message);
                            if (listener.isDone()) {
                                listenersToRemove.add(listener);
                            }
                        } catch (Exception e){
                            e.printStackTrace();
                        }
                    } else try {
                        listener.onCompleted(message);
                    } catch (InvalidObjectException e){
                        Main.logger.debug(String.format("Response could not be parsed as %s", listener.getCommandType()));
                    } catch (Exception e){
                        e.printStackTrace();
                    }

                    if (listener.isDone()) {
                        listenersToRemove.add(listener);
                    }
                }
            }
            listeners.removeAll(listenersToRemove);
        }
    }, "receiverThread");

    receiverThread.setDaemon(true);
    receiverThread.start();

Then a CLI command (it expects no input here):

@CliCommand(value="start", help = "Starts stuff")
public void start() throws IOException, InterruptedException {
    // this method is passed to the thread with the listener
    getAvailabilities().updateAvailabilities("all", "all", "all", someListener);
}

And the callback for that listener:

someListener = new CommandListener() {
            private String source = "Start some listener";
            @Override
            public void onCompleted(String r) throws IOException {
                System.out.println("Which would you like to start?");

                getAvailabilities().printAvailableBrands();

                String brandNumber = "";
                while(Objects.equals(brandNumber, "")){
                    System.out.println("Please enter the number of the Brand: ");
                //when the callback arrives here I still only see ">spring:" and I get nothing printed on the console
                    brandNumber = cr.readLine();
                    if(!isInputAllowed(brandNumber, getAvailabilities().AvailableBrands.size())){
                        brandNumber = "";
                    }
                }
                BrandName = getAvailabilities().AvailableBrands.get(Integer.parseInt(brandNumber) - 1);
                //updating the availabilities narrows down the things I list to the console, so I send an update after every selection
                getAvailabilities().updateAvailabilities("all", BrandName, "all", getInterfaceListener);
                done = true;
            }

This might slightly connect to the issue that sometimes while debugging the CLI in Idea, it gets whacky inputs, eg. when I insert start it says No such command as ar, and if I press enter again, it'll say (some of) the rest: No such command as stt.

pszent
  • 55
  • 1
  • 1
  • 7

1 Answers1

0

The problem is here:

if (listener.isDone()) {
    listenersToRemove.add(listener);
}

If you want your listeners to be executed asynchronously you should not check their completion right away on the same thread as it will most likely return false.

The issue you might be having is that your listeners schedule some task but have no time to finish it as you immediately remove them after the loop:

listeners.removeAll(listenersToRemove);

It is very hard to tell what your logic is but I guess in the next while iteration your list is empty.

GooKSL
  • 387
  • 2
  • 14