Finer
Finer

Reputation: 19

java Catch exception inside a async callback

I have a callback which may throw a custom exception. I'm trying to throw it, but it's not being catched on the outer scope, nor the compiler let me catch it, it says: "Exception is never thrown is the corresponding try block", even though it is.

this is my code:

public void openAsync(MessageAsyncCallback callback) {
        try {
            this.sendChannelOpen(this.getChannel(), getChannelOpenData().getFlags(), new MessageAsyncCallback() {
                @Override
                public void onComplete() throws NanoException {
//                INanoPacket message = transport.getMessageByClassName(AudioServerHandshake.class.getName());
                    INanoPacket message = transport.getMessageByClassName(AudioClientHandshake.class.getName());
                    Log.info("Got audio server handshake, trying to client-handshake it");
                    sendClientHandshakeAsync((AudioServerHandshake) message, callback);
                }
            });
        } catch (NanoException e) {
            System.exit(-2);
        }
    }

and it doesn't let me catch NanoException

EDIT: inside transport.getMessageByClassName I throw a NanoException.

EDIT2: this is the method who invokes the exception:

public INanoPacket getMessageByClassName(String destClassName) throws NanoException {//} throws NanoException {
        long startTime = System.currentTimeMillis(); // fetch starting time
        INanoPacket message = this.getMessageFromTCPQueue();

        while (!(message.getClass().getName().equals(destClassName)) && isRuntimeValid(startTime)) {
            this.insertToTCPQueue(message); // put message back in queue
            message = this.getMessageFromTCPQueue();
        }

        if (!(message.getClass().getName().equals(destClassName))) {
            // timeout...
            throw new NanoException("Couldn't find destination message: " + destClassName);
        }

        return message;
    }

and I want to handle the exception not even in openAsync but on the method that calls openAsync. why? because I'm handling messages coming from a remote device, this is why it's async. and I'm using some kind of timeout to wait for a specific message, and if the message isn't coming I want to restart the whole program.

Upvotes: 0

Views: 2944

Answers (1)

Michał Schielmann
Michał Schielmann

Reputation: 1382

Please notice that in your code you are not invoking onComplete method, you are defining it.

The exception would be thrown in a separate part of the code, possibly separate Thread (as it seems to be async). Therefore the "Exception is never thrown is the corresponding try block" message is right, as the exception will never be thrown when invoking this.sendChannelOpen(...) method.

Your try-catch statement needs to wrap the place where you invoke the onComplete method. As only by invoking onComplete method can you expect NanoException.

EDIT based on comments: If you need to handle the exception throw in getMessageByClassName you can do it in onComplete method and not rethrow it. If you want to handle it somewhere else, you'd need to provide us the code of sendChannelOpen method or a place where the callback is invoked.

EDIT2 (based on question edits): Please see the code below, as an example of how you can communicate between threads. I've used Latch, but there are other classes in java.util.concurrent that you may find useful. BTW, I'm not going into the discussion why you want to restart the whole app on your NanoException, although there might be other options worth considering for recovering from that Exception.

import java.util.concurrent.CountDownLatch;

class NanoException extends Exception {}

interface MessageAsyncCallback {
    void onComplete() throws NanoException;
}

public class AsyncApp {
    private static final CountDownLatch errorLatch = new CountDownLatch(1);
    public static void main(String[] args) {
        new AsyncApp().run();
    }

    void run() {
        sendChannelOpen("something", new MessageAsyncCallback() {
            @Override
            public void onComplete() throws NanoException {
                // the whole try-catch-sleep is not really needed, just to wait a bit before exception is thrown
                try {
                    // not needed, just to wait a bit before exception is thrown
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    throw new NanoException();
                }
                throw new NanoException();
            }
        });

        try {
            System.out.println("This is a main thread and we wait here, while the other thread executes...");
            errorLatch.await();
            System.out.println("Latch has reached 0, will now exit.");
            System.exit(-2);
        } catch (InterruptedException e) {
            System.out.println("Error in main thread.");
            System.exit(-1);
        }
    }

    void sendChannelOpen(String notImportant, MessageAsyncCallback troublesomeCallback) {
        runSomethingInSeparateThread(troublesomeCallback);
    }

    void runSomethingInSeparateThread(MessageAsyncCallback troublesomeCallback) {
        new Thread(() -> {
            try {
                troublesomeCallback.onComplete();
            } catch (NanoException e) {
                System.out.println("You can catch it here, and do system exit here or synchronize with main Thread as below");
                errorLatch.countDown();
            }
        }).start();
    }
}

Upvotes: 1

Related Questions