@SuppressWarnings({"rawtypes", "unchecked"})
    public <T extends Throwable> void handleException(
            final @NonNull CommandContext<C> commandContext,
            final @NonNull T exception
    ) throws Throwable {
        final ExceptionContext<C, T> exceptionContext = this.exceptionContextFactory.createContext(commandContext, exception);

        Class<?> exceptionClass = exception.getClass();
        while (exceptionClass != Object.class) {
            final List<ExceptionHandlerRegistration<C, ?>> registrations = this.registrations(exceptionClass);
            for (final ExceptionHandlerRegistration<C, ?> registration : registrations) {
                if (!((Predicate) registration.exceptionFilter()).test(exception)) {
                    continue;
                }

                try {
                    ((ExceptionHandlerRegistration) registration).exceptionHandler().handle(exceptionContext);
                } catch (final Throwable throwable) {
                    if (throwable.equals(exception)) {
                        continue;
                    }
                    // We try to handle the new exception instead.
                    this.handleException(commandContext, throwable);
                }
                return;
            }
            exceptionClass = exceptionClass.getSuperclass();
        }

        // If nothing was able to handle the exception, then we re-throw.
        throw exception;
    }
Attempts to handle the given {@code exception} gracefully.
<p>
The controller will attempt to find exception handlers for the exception type, and any of its supertypes.
Only one exception handler will get to fully handle the exception.
<p>
If no exception handler was able to handle the exception, the exception will be re-thrown.
The thrown exception might be different from the initial exception in the case that
any of the exception handlers throws a new exception.

@param <T>            the exception type
@param commandContext the command context
@param exception      the exception
@throws Throwable any exception left unhandled is re-thrown and should be handled by the caller
cloud-core/src/main/java/org/incendo/cloud/exception/handling/ExceptionController.java