I start right with the code. This one invokes a method using reflection
try {
Method method = states.getClass().getDeclaredMethod(
getCurrentStateId() + "_" + request.getEvent());
states.setData(request, dataManager);
method.invoke(states);
} catch (NoSuchMethodException e) {
logger.debug("Method " + getCurrentStateId() + "_" + request.getEvent()
+ " cannot be found - invocation not performed.", e);
} catch (IllegalArgumentException e) {
throw new InternalException("Method invocation with reflection failed.", e);
} catch (IllegalAccessException e) {
throw new InternalException("Method invocation with reflection failed.", e);
} catch (InvocationTargetException e) {
throw new InternalException("Method invocation with reflection failed.", e);
}
and calls the method with the following code which throws a PropertiesDontMatchException (runtime).
...
if (totalCredits < minimumCredits || totalCredits > maximumCredits) {
throw new PropertiesDontMatchException("Minimum amount of credits=" + minimumCredits
+ ", maximum amount of credits=" + maximumCredits + ". Your amount of credits=" + totalCredits + ". You have to modify your set of subjects.");
}
...
The thing is that my runtime exception is being wrapped into InvocationTargetException and caught in the first code snippet. This is not what I want. But according to the documentation, it’s the correct behaviour however.
So I came up with this solution
...
} catch (InvocationTargetException e) {
if (e.getCause() instanceof PropertiesDontMatchException) {
throw (PropertiesDontMatchException) e.getCause();
}
throw new InternalException("Method invocation with reflection failed.", e);
}
...
Is this the proper way how to propagate my runtime exception or is there any better solution of this problem?
Yes, this is the correct error handling in this case. I would only extend your test for any
RuntimeException:Or use
Throwablesutility class from guava instead if you don’t mind usingRuntimeExceptioninstead of customInternalExceptionfor checked exceptions:The extra wrapping is necessary to distinguish between your e.g. your method throwing
IllegalAccessExceptionand reflection mechanism itself throwing it.Similar API design choice can be observed with
Future.get()– in case of exception thrown from asynchronous jobExecutionExceptionis thrown wrapping the actual exception.