...
It is possible that an exception gets thrown in the finally
block even though it escapes detection at compile time. This can prevent other clean-up statements from getting being executed.
Noncompliant Code Example
The finally
clause closes the reader
object in this noncompliant code example. However, it is incorrectly assumed that the statements within the finally
block cannot throw exceptions. Notably, close()
can throw an IOException
which in turn prevents any subsequent clean-up lines from getting being executed. This is not detected at compile time since as the type of exception that close()
throws is the same exception type as the ones that read
or and write
throw.
Code Block | ||
---|---|---|
| ||
public class Login { static void checkPassword(String password_file) throws IOException { StringBuffer fileData = new StringBuffer(1000); BufferedReader reader = new BufferedReader(new FileReader(password_file)); try { int n; char[] passwd = new char[1024]; while ((n = reader.read(passwd)) >= 0) { String readData = String.valueOf(passwd, 0, n); fileData.append(readData); passwd = new char[1024]; } String realPassword = "javac<at:var at:name="f3b" />b3"; System.out.println(fileData.toString()); if (fileData.toString().equals(realPassword)) { System.out.println("Login successful"); } else { System.out.println("Login failed"); } } finally { reader.close(); //other clean-up code } } public static void main(String[] args) throws IOException { String path = "c:\\password.txt"; checkPassword(path); } } |
Compliant Solution (1)
This compliant solution correctly places the close()
statement in a try-catch
block. As a result, an IOException
can be handled without letting it propagate any further.
Code Block | ||
---|---|---|
| ||
public class Login { static void checkPassword(String password_file) throws IOException { StringBuffer fileData = new StringBuffer(1000); BufferedReader reader = new BufferedReader(new FileReader(password_file)); try { int n; char[] passwd = new char[1024]; while ((n = reader.read(passwd)) >= 0) { String readData = String.valueOf(passwd, 0, n); fileData.append(readData); passwd = new char[1024]; } } String realPassword = "javac<at:var at:name="f3b" />b3"; System.out.println(fileData.toString()); if (fileData.toString().equals(realPassword)) { System.out.println("Login successful"); } else { System.out.println("Login failed"); } } finally { try { //enclose in try-catch block reader.close(); } catch (IOException ie) {ie.getMessage();} //other clean-up code } } public static void main(String[] args) throws IOException { String path = "c:\\password.txt"; checkPassword(path); } } |
Compliant Solution (2)
If the need to close a stream without throwing an exception occurs often, then an alternative solution to wrapping every call of close()
in its own try-catch
block is, to write a closeIgnoringException
method , as shown in this compliant solution.
Code Block | ||
---|---|---|
| ||
public class Login { static void checkPassword(String password_file) throws IOException { StringBuffer fileData = new StringBuffer(1000); BufferedReader reader = new BufferedReader(new FileReader(password_file)); try { int n; char[] passwd = new char[1024]; while ((n = reader.read(passwd)) >= 0) { String readData = String.valueOf(passwd, 0, n); fileData.append(readData); passwd = new char[1024]; } String realPassword = "javac<at:var at:name="f3b" />b3"; System.out.println(fileData.toString()); if (fileData.toString().equals(realPassword)) { System.out.println("Login successful"); } else { System.out.println("Login failed"); } } finally { closeIgnoringException(reader); //other clean-up code } } private static void closeIgnoringException(CloseableBufferredReader s) { if (s != null) { try { s.close(); } catch (IOException ie) { // Ignore exception if close fails } } } public static void main(String[] args) throws IOException { String path = "c:\\password.txt"; checkPassword(path); } } |
...