Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to centralize exception handling in multiple methods of an API

This is a plain Java 8+ question, no frameworks used.

We are producing an API for a higher layer which deals with the presentation layer among other activities. We have and interface agreed with the invoker, so they are happy to receive some particular exceptions we throw.

At the same time, we are also using other APIs under the same agreement, so we can do stuff by ourselves and throw exceptions or we can invoke other APIs which throw the agreed exceptions. Currently, we do nothing with the exceptions thrown by the APIs we are invoking.

Thing is, we are the best positioned in this infrastructure to deal with intermediate activities while exceptions are thrown, so we need to capture both, our exceptions and the exceptions provided by those we are invoking; basically reporting the issue, raising system controls, etc, and then re-throw the original exception so the top layer keeps as it is now.

We have around 300 methods in the entry point class of our API:

public void method1 (arguments for method 1) {
...
}

...

public void method300 (arguments for method 300) {
...
}

I clearly understand that I can create a method to centralise the actions to be taken in the exception management, something like:

public void myExceptionHandler (Exception e) {
    if (e instanceOf X) {
    } else if ...
    ...
    throw particularExceptionAccordingTheCase 
}

But I'd also avoid modifying those 300 methods.

Any idea how to inject a try-catch in those 300 methods to send the Exception to myExceptionHandler without really adding a try-catch in each of them?

Any comments and ideas are much appreciated!

----------- After mprev0 suggestion -------------------------------

I tried this approach. It really catches the exception and so on, but I can't re-trow an Exception: I'm forced to catch it, but this goes against the requirement of re-sending the exception back to the top layer. While I can throw an Error, I got a compiler error at line throw new FileNotFoundException();

public class myExceptionHandler implements Thread.UncaughtExceptionHandler {

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println("gotcha!");
        if (e instanceof java.lang.Error) {
            System.out.println("AAT-CORE: Fatal Error");
            throw new java.lang.Error(e.getCause());

        } else if (e instanceof java.lang.Exception) {
            System.out.println("AAT-CORE: Exception Error");
            throw new FileNotFoundException();
        }
    }

}

Any ideas?

------------ After some more digging, fixed with a decorator pattern -------

Previous class implementation does not work, as I can't change the signature of the method and I need to re-throw the java.lang.Exception.

Using a decorator and handling the interface there makes the trick. As a summary:

Top layer class:

public class TopLayer {
    public static void main (String[] args) {
        MiddleLayer m = new MiddleLayer();
        m.method1();
    }
}

Bottom layer class contains specific APIs and some implementation, the only interesting thing is that it contains java.lang.Exceptions uncontrolled, expecting the top layer to do this job. But, we are working in the middle and we will do this job:

public class MiddleLayer extends BottomLayer {

    public MiddleLayer () {
        final UncaughtExceptionHandler subclass = Thread.currentThread().getUncaughtExceptionHandler();
        Thread.currentThread().setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread thread, Throwable ex) {

                System.out.println("gotcha2!");
                // carry on with prior flow
                subclass.uncaughtException(thread, ex);
            }
        });

    }

}

In this way, I can get the system.out and the java.lang.Exception is propagated to the Top Layer.

Decorator inspiration came from here: Rethrow UncaughtExceptionHandler Exception after Logging It

Additional comments are welcome!

like image 773
JBC Avatar asked Oct 16 '22 15:10

JBC


1 Answers

You can solve this by implementing the java.lang.Thread.UncaughtExceptionHandler interface:

public class MyExceptionHandler implements Thread.UncaughtExceptionHandler {

    @Overrides
    public void uncaughtException(Thread t, Throwable e) {
            if (e instanceOf X) {
            } else if ...
            ...
            throw particularExceptionAccordingTheCase 
    }
}

Then you associate it to all threads as follows:

Thread.setDefaultUncaughtExceptionHandler(new MyExceptionHandler())

This will configure the exception handler to handle all uncaught exceptions in all threads of your application.

Note that this will only work for exceptions that aren't yet explicitly handled somewhere in your code and if there is no other handler configured for some particular thread (the uncaught exception handler can also be set for some specific thread).

EDIT: As discovered by @JBC, the above approach will not work for checked exceptions since we are forced to catch them explicitly in our uncaughtException method (note that we cannot add a throws clause to an overridden method). While it will work without problems if we only want to re-throw subtypes of RuntimeException and Error, there is a little adaptation needed if we want to make it work - you can find it explained in @JBC's question.

like image 79
Marko Previsic Avatar answered Oct 18 '22 13:10

Marko Previsic