While writing a crypto utility class I ran into a problem with the following method:
public static void destroy(Key key) throws DestroyFailedException {
if(Destroyable.class.isInstance(key)) {
((Destroyable)key).destroy();
}
}
@Test
public void destroySecretKeySpec() {
byte[] rawKey = new byte[32];
new SecureRandom().nextBytes(rawKey);
try {
destroy(new SecretKeySpec(rawKey , "AES"));
} catch(DestroyFailedException e) {
Assert.fail();
}
}
In the particular case of javax.crypto.spec.SecretKeySpec
the above method would work just fine in java7
because SecretKeySpec (javadocs 7) does not implement Destroyable (javadocs 7)
Now with java8
the class SecretKeySpec (javadocs 8) has been made Destroyable (javadocs 8) and the method Destroyable#destroy is now default
which is fine according this statement
Default methods enable you to add new functionality to the interfaces of your libraries and ensure binary compatibility with code written for older versions of those interfaces.
then the code compiles without any problems despite the fact that the class ScretKeySpec
itself has not been changed, alone the interface SecretKey has been.
The problem is that in oracle's jdk8
the destroy
method has the following implementation:
public default void destroy() throws DestroyFailedException {
throw new DestroyFailedException();
}
which leads to an exception at run time.
So the binary compatibility might not have been broken, but existing code has been. The test above passes with java7
but does not with java8
So my questions are:
How to deal in general with default methods which might lead to exceptions - because not implemented or not supported - or unexpected behavior at run time? aside from doing
Method method = key.getClass().getMethod("destroy"); if(! method.isDefault()) { ((Destroyable)key).destroy(); }
which is only valid for java8 and which might not be correct in future releases, since the default method might get a meaningful implementation.
Would it not be better to leave this default method empty instead of throwing an exception (which IMO is misleading since aside from the legit call to destroy nothing has been attempted to effectively destroy the key, an UnsupportedOperationException would have been a better fit and you would know instantly what is going on)
Is my approach with (type check/cast/call)
if(Destroyable.class.isInstance(key)) ((Destroyable)key).destroy();
for determining whether to destroy or not wrong? What would be an alternative?
Is this a misconception or did they just forget to add meaningful implementation in
ScretKeySpec
?