Friday 26 July 2013

Lombok’s Sneaky Throws

I finally found a real life scenario where I want to use “Sneaky Throws” Lombok’s feature. According to the documentation:

@SneakyThrows

To boldly throw checked exceptions where no one has thrown them before!

Overview

@SneakyThrows can be used to sneakily throw checked exceptions without actually declaring this in your method’s throws clause. This somewhat contentious ability should be used carefully, of course. The code generated by lombok will not ignore, wrap, replace, or otherwise modify the thrown checked exception; it simply fakes out the compiler. On the JVM (class file) level, all exceptions, checked or not, can be thrown regardless of the throws clause of your methods, which is why this works.
More info about this feature. here: http://projectlombok.org/features/SneakyThrows.html
The scenario is the following:
We have a helper class that retrieves a field using reflection and in turn uses the Apache Commons’ PropertyUtils class. 
The latter throws 4 checked exceptions we don’t want to propagate to the final user. 
In other words, if some of these exceptions are thrown, we want everything to explode (especially, because we use this helper only for unit tests). So, the options were:
  1. Wrap everything around a big try{} catch (Exception) block and throw a RuntimeException such as IllegalArgumentException.
    • Con: Sonar would complain that we are catching the generic class “Exception”.
  2. Throw all exceptions.
    • Con: ugly. User will have to explicitly catch all these exceptions. Not really an option.
  3. Use Lombok’s “Sneaky Throws” feature. This would throw all of the above mentioned exceptions but without forcing users to explicitly catch them. This is the approach:
@SneakyThrows(value = { SecurityException.class, NoSuchFieldException.class, IllegalArgumentException.class, IllegalAccessException.class })
public static <T> void setPrivateStaticFinalField(Class<T> targetClass, T targetObject, String fieldName, Object newValue) {
    Field logField = ReflectionUtils.findField(targetClass, fieldName);
    logField.setAccessible(true);
    Field modifiersField = Field.class.getDeclaredField("modifiers");
    modifiersField.setAccessible(true);
    modifiersField.setInt(logField, logField.getModifiers() & ~Modifier.FINAL);
    ReflectionUtils.setField(logField, targetObject, newValue);
}