Skip to content

Exceptions

Revxrsal edited this page Jun 8, 2022 · 4 revisions

Exceptions can be a nasty part of programming but could also be rather handy. Lamp allows you to protect yourself against the bad ones while also leveraging the benefits of good ones, while maintaining upmost flexibility, All done through the following classes:

  • CommandExceptionHandler: A bare-boned interface for handling any sort of exceptions thrown during command invocation. This includes built-in ones, ones returned from resolvers, conditions, validators, etc., as well as command errors/exceptions from the command logic itself
  • CommandExceptionAdapter: A rich implementation of CommandExceptionHandler that provides direct, overridable methods for each individual exception, with default (and overridable) messages implemented for each one.
  • Platform-specific subclasses of CommandExceptionAdapter:
  • SelfHandledException for exceptions that wish to handle themselves
  • CommandExceptionHandlers must be registered with CommandHandler#setExceptionHandler(CommandExceptionHandler).

In most cases, you may not need more than extending your platform-specific exception adapter and overriding methods with more dynamic messages, however you also have the ability to control the full command handling process by implementing the bare-boned CommandExceptionHandler.

Stack trace sanitization

One of Java's powerful features is tracking the invocation order of the exception from the direct location and going until native methods. However many times we only need the actual, useful couple of lines of the error and not the whole bunch of trace that comes along with it.

This usually becomes a problem with reflectively calling methods, overloading, and delegating, as this is where the trace begins to get relatively long with little value.

To solve this problem, Lamp includes a default stack trace sanitizer that strips down the usually irrelevant paths of classes and only includes the actual, useful error trace.

We can assess the difference between sanitized trace and un-sanitized ones.

@Command("error")  
public void alwaysError() {  
    System.out.println(1 / 0);  
}

Before:

An error occurred while executing the command.
java.lang.ArithmeticException: / by zero
	at our.commands.Test.alwaysError(TestCLI.java:21)
	at java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:627)
	at java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:649)
	at revxrsal.commands.core.reflect.MethodHandlesCallerFactory$1.call(MethodHandlesCallerFactory.java:35)
	at revxrsal.commands.core.reflect.MethodCaller.lambda$bindTo$0(MethodCaller.java:29)
	at revxrsal.commands.core.BaseCommandDispatcher.execute(BaseCommandDispatcher.java:80)
	at revxrsal.commands.core.BaseCommandDispatcher.eval(BaseCommandDispatcher.java:32)
	at revxrsal.commands.core.BaseCommandHandler.dispatch(BaseCommandHandler.java:302)
	at revxrsal.commands.core.BaseCommandHandler.dispatch(BaseCommandHandler.java:306)
	at revxrsal.commands.cli.core.CLIHandler.pollInput(CLIHandler.java:42)
	at our.commands.TestCLI.main(TestCLI.java:16)

After:

java.lang.ArithmeticException: / by zero
	at our.commands.TestCLI.alwaysError(TestCLI.java:20)

The error is relatively clean and straightforward, and at the same time, does not contain long but unhelpful error paths. However, it could inadvertently strip otherwise important traces if you tend to use MethodHandles in your own command logic or such, and in that case it is possible to disable this feature by using CommandHandler#disableStackTraceSanitizing().

Clone this wiki locally