- 37. Do not shadow or obscure identifiers in subscopes
- 38. Do not declare more than one variable per declaration
- 39. Use meaningful symbolic constants to represent literal values in program logic
- 40. Properly encode relationships in constant definitions
- 41. Return an empty array or collection instead of a null value for methods that return an array or collection
- 42. Use exceptions only for exceptional conditions
- 43. Use a try-with-resources statement to safely handle closeable resources
- 44. Do not use assertions to verify the absence of runtime errors
- 45. Use the same type for the second and third operands in conditional expressions
- 46. Do not serialize direct handles to system resources
- 47. Prefer using iterators over enumerations
- 48. Do not use direct buffers for short-lived, infrequently used objects
- 49. Remove short-lived objects from long-lived container objects
43. Use a try-with-resources statement to safely handle closeable resources
The Java Development Kit 1.7 (JDK 1.7) introduced the try-with-resources statement (see the JLS, §14.20.3, “try-with-resources” [JLS 2013]), which simplifies correct use of resources that implement the java.lang.AutoCloseable interface, including those that implement the java.io.Closeable interface.
Using the try-with-resources statement prevents problems that can arise when closing resources with an ordinary try-catch-finally block, such as failing to close a resource because an exception is thrown as a result of closing another resource, or masking an important exception when a resource is closed.
Use of the try-with-resources statement is also illustrated in The CERT® Oracle® Secure Coding Standard for Java™ [Long 2012], “ERR05-J. Do not let checked exceptions escape from a finally block,” “FIO03-J. Remove temporary files before termination,” and “FIO04-J. Close resources when they are no longer needed.”
Noncompliant Code Example
This noncompliant code example uses an ordinary try-catch-finally block in an attempt to close two resources.
public void processFile(String inPath, String outPath) throws IOException{ BufferedReader br = null; BufferedWriter bw = null; try { br = new BufferedReader(new FileReader(inPath)); bw = new BufferedWriter(new FileWriter(outPath)); // Process the input and produce the output } finally { try { if (br != null) { br.close(); } if (bw != null) { bw.close(); } } catch (IOException x) { // Handle error } } }
However, if an exception is thrown when the BufferedReader br is closed, then the BufferedWriter bw will not be closed.
Compliant Solution (Second finally Block)
This compliant solution uses a second finally block to guarantee that bw is properly closed even when an exception is thrown while closing br.
public void processFile(String inPath, String outPath) throws IOException { BufferedReader br = null; BufferedWriter bw = null; try { br = new BufferedReader(new FileReader(inPath)); bw = new BufferedWriter(new FileWriter(outPath)); // Process the input and produce the output } finally { if (br != null) { try { br.close(); } catch (IOException x) { // Handle error } finally { if (bw != null) { try { bw.close(); } catch (IOException x) { // Handle error } } } } } }
Compliant Solution (try-with-resources)
This compliant solution uses a try-with-resources statement to manage both br and bw:
public void processFile(String inPath, String outPath) throws IOException{ try (BufferedReader br = new BufferedReader(new FileReader(inPath)); BufferedWriter bw = new BufferedWriter(new FileWriter(outPath));) { // Process the input and produce the output } catch (IOException ex) { // Print out all exceptions, including suppressed ones System.err.println("thrown exception: " + ex.toString()); Throwable[] suppressed = ex.getSuppressed(); for (int i = 0; i < suppressed.length; i++) { System.err.println("suppressed exception: " + suppressed[i].toString()); } } }
This solution preserves any exceptions thrown during the processing of the input while still guaranteeing that both br and bw are properly closed, regardless of what exceptions occur. Finally, this code demonstrates how to access every exception that may be produced from the try-with-resources block.
If only one exception is thrown, during opening, processing, or closing of the files, the exception will be printed after "thrown exception:". If an exception is thrown during processing, and a second exception is thrown while trying to close either file, the second exception will be printed after "thrown exception:", and the first exception will be printed after "suppressed exception:".
Applicability
Failing to correctly handle all failure cases when working with closeable resources may result in some resources not being closed or in important exceptions being masked, possibly resulting in a denial of service. Note that failure to use a try-with-resources statement cannot be considered a security vulnerability in and of itself, because it is possible to write a correctly structured group of nested try-catch-finally blocks guarding the resources that are in use (see “ERR05-J. Do not let checked exceptions escape from a finally block” [Long 2012]). That said, failure to correctly handle such error cases is a common source of vulnerabilities. Use of a try-with-resources statement mitigates this issue by guaranteeing that the resources are managed correctly and that exceptions are never masked.
Bibliography
[JLS 2013] |
§14.20.3, “try-with-resources” |
[Long 2012] |
ERR05-J. Do not let checked exceptions escape from a finally block |
FIO03-J. Remove temporary files before termination |
|
FIO04-J. Close resources when they are no longer needed |
|
[Tutorials 2013] |
The try-with-resources Statement |