The main question is whether you want to re-use the native library on its own, or always bundle it with a Java wrapper.
Very often, a Java wrapper makes sense on its own, independent of the exceptions issue, it makes the native functionality look more Java-like.
Role of the exception object
An exception object is meant to communicate to a caller (somewhere up the call stack, where the exception is caught) the reason why some method call failed. To typical code, that reason is irrelevant (1), it’s enough to know about the failure and be able to produce a sensible log entry plus a message to the user.
Communicating failure from native code
You chose to communicate failure from native code by having the C++ top layer create and throw Java exceptions, and that creates a dependency to the Java exception system and the specific exception class you chose to use.
I see a few options:
- Use a Java standard exception like
RuntimeException
. We can trust that it will always exist, so that dependency that will not create any problems. - Use your own exception type like
MyWonderfulLibraryException
. I’d recommend against such a naming. This doesn’t describe a reason for failure but the location of the failure. You have to make sure that the exception class is available in your Java wrapper library. - Use your own exception type like
NativeCppException
. Technically, it’s the same as the previous choice, but IMHO better describes the failure reason as something that cannot appropriately be described inside the Java computation model. - Communicate failure from native code without creating a Java exception, e.g. by special failure return values. That might be easier (and more performant, and creating less code dependencies) than your current approach.
Communicating failure to user code
User code should see an exception in case of failure, one that describes the reason of the failure (mainly for logging purposes).
In your case, the reason is hidden somewhere inside the text coming from the C++ runtime_error
. You might be tempted to map that into different appropriate Java exception types, but “You Ain’t Gonna Need It (YAGNI)”.
My preferred choice would be something like NativeCppException
, summarizing everything that can happen in the C++ world. Some caller might be brave enough to catch such an exception, probably only if he has a non-native alternative available.
(Footnote 1)
I know that there are diverging opinions on the importance of individual exception types, but I have yet to find a convincing argument to warrant the exceedingly complex exception type hierarchies often seen in the wild.
Exception types are created to be used by some part of your code, otherwise it’s a classical YAGNI case.
Regarding failure of internal calls, methods typically fall into three categories:
- Method does not have a fallback strategy, so in case of some internal failure, the whole method fails. Typically, these methods let exceptions ripple through without intervening.
- Method has a fallback strategy, allowing to succeed even after failure of some internal calls, typically by retrying, or by switching to an alternative path of execution. If such a fallback strategy exists, it typically makes sense to use it, independent of the failure reason. These methods catch all exceptions arising within specific blocks, and then activate the fallback, independent of the failure reason.
- Method has a fallback strategy that can only be applied in specific cases, and the cases can be distinguished by the exception type. These methods catch only some specific exception types, and then activate the appropriate fallback.
The vast majority of methods falls into the first category (or should fall into the category, weren’t they over-engineered).
For a few methods, developers create fallback strategies. Most often it doesn’t harm to try that strategy not only for specific failures, but for any failure.
In a very small percentage of cases, the failure reason matters for selecting a fallback that would otherwise be inappropriate, e.g. if the database tells me by means of an exception that my password has expired, the code can then redirect me to the password-renewal procedure and then continue (a somewhat contrived example).
The actual exception type only matters in the third method type, and I bet only a very small percentage of exception types ever gets used this way, so we have a classical case of YAGNI.
CLICK HERE to find out more related problems solutions.