Java requires that each method address every checked exception that can be thrown during its execution either by handling the exception within a try-catch
block or by declaring that the exception can propagate out of the method (via the throws
clause). Unfortunately, there are a few techniques that permit undeclared checked exceptions to be thrown at runtime. Such techniques defeat the ability of caller methods to use the throws
clause to determine the complete set of checked exceptions that could propagate from an invoked method. Consequently, such techniques must not be used to throw undeclared checked exceptions.
Noncompliant Code Example (Class.newInstance()
)
This noncompliant code example throws undeclared checked exceptions. The undeclaredThrow()
method takes a Throwable
argument and invokes a function that will throw the argument without declaring it. Although undeclaredThrow()
catches any exceptions the function declares that it might throw, it nevertheless throws the argument it is given without regard to whether the argument is one of the declared exceptions. This noncompliant code example also violates ERR07-J. Do not throw RuntimeException, Exception, or Throwable. However, because of exception ERR08-J-EX0, it does not violate ERR08-J. Do not catch NullPointerException or any of its ancestors.
Any checked exception thrown by the default constructor of java.lang.Class.newInstance()
is propagated to the caller even though Class.newInstance()
declares that it throws only InstantiationException
and IllegalAccessException
. This noncompliant code example demonstrates one way to use Class.newInstance()
to throw arbitrary checked and unchecked exceptions:
public class NewInstance { private static Throwable throwable; private NewInstance() throws Throwable { throw throwable; } public static synchronized void undeclaredThrow(Throwable throwable) { // These exceptions should not be passed if (throwable instanceof IllegalAccessException || throwable instanceof InstantiationException) { // Unchecked, no declaration required throw new IllegalArgumentException(); } NewInstance.throwable = throwable; try { // Next line throws the Throwable argument passed in above, // even though the throws clause of class.newInstance fails // to declare that this may happen NewInstance.class.newInstance(); } catch (InstantiationException e) { /* Unreachable */ } catch (IllegalAccessException e) { /* Unreachable */ } finally { // Avoid memory leak NewInstance.throwable = null; } } } public class UndeclaredException { public static void main(String[] args) { // No declared checked exceptions NewInstance.undeclaredThrow( new Exception("Any checked exception")); } }
Noncompliant Code Example (Class.newInstance()
Workarounds)
When the programmer wishes to catch and handle the possible undeclared checked exceptions, the compiler refuses to believe that any can be thrown in the particular context. This noncompliant code example attempts to catch undeclared checked exceptions thrown by Class.newInstance()
. It catches Exception
and dynamically checks whether the caught exception is an instance of the possible checked exception (carefully rethrowing all other exceptions).
public static void main(String[] args) { try { NewInstance.undeclaredThrow( new IOException("Any checked exception")); } catch (Throwable e) { if (e instanceof IOException) { System.out.println("IOException occurred"); } else if (e instanceof RuntimeException) { throw (RuntimeException) e; } else { // Forward to handler } } }
Compliant Solution (Constructor.newInstance()
)
This compliant solution uses java.lang.reflect.Constructor.newInstance()
rather than Class.newInstance()
. The Constructor.newInstance()
method wraps any exceptions thrown from within the constructor into a checked exception called InvocationTargetException
.
public static synchronized void undeclaredThrow(Throwable throwable) { // These exceptions should not be passed if (throwable instanceof IllegalAccessException || throwable instanceof InstantiationException) { // Unchecked, no declaration required throw new IllegalArgumentException(); } NewInstance.throwable = throwable; try { Constructor constructor = NewInstance.class.getConstructor(new Class<?>[0]); constructor.newInstance(); } catch (InstantiationException e) { /* Unreachable */ } catch (IllegalAccessException e) { /* Unreachable */ } catch (InvocationTargetException e) { System.out.println("Exception thrown: " + e.getCause().toString()); } finally { // Avoid memory leak NewInstance.throwable = null; } }
Noncompliant Code Example (sun.misc.Unsafe
)
This noncompliant code example is insecure both because it can throw undeclared checked exceptions and because it uses the sun.misc.Unsafe
class. All sun.*
classes are unsupported and undocumented because their use can cause portability and backward compatibility issues.
Classes loaded by the bootstrap class loader have the permissions needed to call the static factory method Unsafe.getUnsafe()
. Arranging to have an arbitrary class loaded by the bootstrap class loader without modifying the sun.boot.class.path
system property can be difficult. However, an alternative way to gain access is to change the accessibility of the field that holds an instance of Unsafe
through the use of reflection. This approach works only when permitted by the current security manager (which would violate ENV03-J. Do not grant dangerous combinations of permissions). Given access to Unsafe
, a program can throw an undeclared checked exception by calling the Unsafe.throwException()
method.
import java.io.IOException; import java.lang.reflect.Field; import sun.misc.Unsafe; public class UnsafeCode { public static void main(String[] args) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); Unsafe u = (Unsafe) f.get(null); u.throwException(new IOException("No need to declare this checked exception")); } }
Noncompliant Code Example (Generic Exception)
An unchecked cast of a generic type with parameterized exception declaration can also result in unexpected checked exceptions. All such casts are diagnosed by the compiler unless the warnings are suppressed.
interface Thr<EXC extends Exception> { void fn() throws EXC; } public class UndeclaredGen { static void undeclaredThrow() throws RuntimeException { @SuppressWarnings("unchecked") // Suppresses warnings Thr<RuntimeException> thr = (Thr<RuntimeException>)(Thr) new Thr<IOException>() { public void fn() throws IOException { throw new IOException(); } }; thr.fn(); } public static void main(String[] args) { undeclaredThrow(); } }
Noncompliant Code Example (Thread.stop(Throwable)
)
According to the Java API [API 2014], class Thread
:
[
Thread.stop()
] This method was originally designed to force a thread to stop and throw a givenThrowable
as an exception. It was inherently unsafe (seeThread.stop()
for details), and furthermore could be used to generate exceptions that the target thread was not prepared to handle.
For example, the following method is behaviorally identical to Java's throw operation but circumvents the compiler's attempts to guarantee that the calling method has declared all of the checked exceptions that it may throw.
static void sneakyThrow(Throwable t) { Thread.currentThread().stop(t); }
Note that the Thread.stop()
methods are deprecated, so this code also violates MET02-J. Do not use deprecated or obsolete classes or methods.
Noncompliant Code Example (Bytecode Manipulation)
It is also possible to disassemble a class, remove any declared checked exceptions, and reassemble the class so that checked exceptions are thrown at runtime when the class is used [Roubtsov 2003]. Compiling against a class that declares the checked exception and supplying at runtime a class that lacks the declaration can also result in undeclared checked exceptions. Undeclared checked exceptions can also be produced through crafted use of the sun.corba.Bridge
class. All of these practices are violations of this rule.
Risk Assessment
Failure to document undeclared checked exceptions can result in checked exceptions that the caller is unprepared to handle, consequently violating the safety property.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
ERR06-J | Low | Unlikely | High | P1 | L3 |
Related Guidelines
Bibliography
[API 2014] | Thread.stop(Throwable) |
Item 2, "Consider a Builder When Faced with Many Constructor Parameters" | |
| |
[JLS 2015] | |
| |
| |
"Scalability of Checked Exceptions" |
9 Comments
Robert Seacord (Manager)
The following compliant solution:
Refrain from employing code that is capable of throwing undeclared checked exceptions (legitimate or hostile). If the source code can throw them, explicitly document the behavior. Prefer
Constructor.newInstance
overClass.newInstance
. Finally, do not use deprecated methods such asThread.stop()
.Suggests to me that there are three separate recommendations here.
I think these two sentences should be in the definition of the definition of the rule:
Refrain from employing code that is capable of throwing undeclared checked exceptions (legitimate or hostile). If the source code can throw them, explicitly document the behavior.
Constructor.newInstance
overClass.newInstance
should not be mentioned here for the first time. I can't tell from reading the text what the issue is.Thread.stop()
sounds like a different but related guideline. I would think you could create a corresponding compliant solution?Dhruv Mohindra
This would indicate that the summary of all the CSs must make it to the introduction, right?
Do we need to replicate the compliant solution for
Thread.stop
? Can we just cross reference THI05-J. Do not use Thread.stop() to terminate threads which actually is an alternative toThread.stop
.In general, shall we keep NCCEs with no corresponding CSs and (where possible) just generalize the CS to say 'document it when you do it' as was apt for this recommendation?
Robert Seacord (Manager)
The introduction only needs to be clear on what the intent of the rule is. This is necessary, among other things, to allow source code analysis tool developers to determine what constitutes a violation of the guideline, and what would is likely to be considered a "false positive" by a developer.
In general, I would think it is better to pair NCE/CS. This is in general.
If there are specific cases where one compliant solution addressed multiple NCEs, this is OK. We are trying to preserve some flexibility in how guidelines are written as to not make them overly rigid and proscriptive.
Kirk Sayre
Does a discussion of wrapping checked exceptions belong in this recommendation? It seems to me that the wrapping of exceptions is not directly related to undeclared checked exceptions. Is the intent of including a discussion of wrapped exceptions in this recommendation to provide an alternate strategy to someone who wants to use undeclared checked expressions to avoid many many annoying redundant catch blocks?
If so, rather than wrapping a specific exception in a more generic exception, designing your exception classes so they inherit from 1 or more more generic exceptions works quite well (this is what we did in the FX system code). If we have exceptions X1, X2, and X3 that inherit from X, we can write method foo() that throws detailed exceptions X1, X2, and X3, but if needed we can just write a catch block to catch exception X when calling foo(), eliminating some unnecessary catch blocks. However, since we have not wrapped X1, X2, or X3 in a more general exception (X1, X3, or X3 are still thrown), if needed the caller of foo() can still choose to catch the specific exceptions.
Dhruv Mohindra
This guideline does not advocate wrapping checked exceptions in unchecked ones, especially when the exceptional conditions are recoverable. It also discourages undeclared checked exceptions. Some may violate these and use the two techniques to avoid the catch-block clutter. The guideline recommends wrapping a checked exception into a broader checked exception if it is really desired to reduce the number of
catch
blocks. This enables you to ensure that the caller is prepared to deal with the condition, at least at a higher level.Your idea of throwing specific exceptions from the method and catching the exception superclass in the caller is acceptable. However, it is not always the case that X1, X2 and X3 will derive from the same exception class (especially when you are using pre-existing APIs). Even if you are able to use
instanceof
within the single catch block of the high level caller to check which exception subclass was thrown, it is often not possible to recover at the higher level.The exception translation idiom suggests that higher levels should catch lower level exceptions and throw higher level exceptions. Exception chaining is another acceptable way. Recovery may still be impossible. But again, the point of this recommendation is to focus on undeclared checked exceptions and why they should not be used to surprise the caller. Other described mechanisms may be used to reduce the catch-clutter.
EDIT: The discussion you pointed out is there to limit the practice of using as well as designing future APIs that would favor undeclared checked exceptions.
David Svoboda
I believe that Java 1.7's try-with-resources statement might constitute an exception. If such a statement creates an object, then the object's close might throw an exception which is not explicitly declared since the close() statement becomes invisible.
Not sure if this is actually possible, for two reasons:
Dhruv Mohindra
David Svoboda
The NCCE still behaves as advertised. I've updated the text. I think the code samples fall under EX0 of ERR14.
That's because it is a menagerie of techniques to throw undeclared checked exceptions. It's not the only rule that doesn't end in a CS. Personall I think a final CS would be trivial and kinda not worth the effort.
Yozo TODA
at the last NCCE(Bytecode Manipulation), the second sentence("Compiling against...") says almost the same thing as the first sentence. how about removing it?