Automatic Resource Management - try-with-resources Statement
Recently I was reviewing some old Java code that performs SQL queries against a database and closes the resources in finally blocks. When I examined the code I realized that the handling of the resources was flawed if an exception occurred and not handled with care.
Automatic Resource Management(ARM) is new feature of Java 7 suggested as the part of "Project Coin" which enables the safe handling of resources. Before Java 7 it was developer's responsibility to handle the resources. Developer had to ensure that they manage their resources well.
Resource management, if not handled properly leads to memory leaks or resource leaks. Yet both experienced and inexperienced developer gets the resource management code wrong. For any resources that was successfully initialized needs to be closed. This needs the correct usage of try/catch/finally blocks. Sometimes even your finally block runs into exception while closing in cases, you will have to handle the exception of finally block too. To avoid these exception, we generally ignore the exception in the finally block.
Let’s analyze the following code snippet:
public static int getRecordCount(){
int recCount= -1;
Connection c = null;
Statement stmt = null;
ResultSet rs = null;
String select = "SELECT COUNT(*) FROM RECORD_M WHERE ID = 1";
String URL = "jdbc:oracle:thin:username/password@hostname:port:dbname";
try {
c = DriverManager.getConnection(URL);
stmt = c.createStatement();
rs = stmt.executeQuery(select);
if (rs.next())
recCount = rs.getInt(1);
} catch (Exception ex) {
//Log.logError(Log.SERVICE, this,
//"SQL Error occured while selecting record");
} finally {
try {
if (rs != null)
rs.close();
} catch (SQLException e) {
}
try {
if (stmt != null)
stmt.close();
} catch (SQLException e) {
}
if (c != null) {
try {
c.close();
} catch (SQLException e) {
}
}
}
return recCount;
}
Issues with traditional closing of Resources
1)
Scope : We had to create the object before try block and thereby increase the scope of resource objects.
2) Redundant code.
3)
Null Check : Null check for all the resources in the finally block.
4)
Overwrite Exception of try block : If an exception is thrown in the try block and a subsequent exception is thrown in the finally block the original exception is lost.
5)
Resource Leak : If an exception is thrown by the closing of the ResultSet (assuming we are throwing exception after we are catching SQLException), no attempt is made to close the Statement and connection objects, causing resource leak.
6)
Exception Masking : It is possible for the statement.close() to be the original exception and this is ignored. Ignoring an exception is likely to mislead a user trying to determine the cause of a failure. This is known as exception masking.
Resources such as Connections, ResultSet, Statement, etc. should be closed manually by the developer by writing bog-standard code i.e. try-finally block to close the respective resources.
Also, since you have to make the resources available for finally block to close. You have increase the scope of resources. If you don’t have to close the resources in the finally block. You could have made the scope of the resources local to keep the memory usage low.
Let’s go through the essentials of the
try-with-resources statement from the Java developer point of view and elucidate the mystery of the syntactic sugar behind the language extension.
Let’s assume that finally block doesn’t exist so if we have to write the above excerpt we would have written it as
public static int getRecordCount() throws SQLException{
int recCount= -1;
try {
String URL = "jdbc:oracle:thin:username/password@hostname:port:dbname";
Connection c = DriverManager.getConnection(URL);
String select = "SELECT COUNT(*) FROM RECORD_M WHERE ID = 1";
Statement stmt = c.createStatement();
ResultSet rs = stmt.executeQuery(select);
if (rs.next()) {
recCount = rs.getInt(1);
}
rs.close();
stmt.close();
c.close();
} catch (Exception ex) {
//Log.logError(Log.SERVICE, this SQL Error occured while selecting record");
}
return recCount;
}
At first sight this code doesn’t look harmful. It opens a connection, read the data and return. However we might run in SQLException, which can be due to wrong query or other reasons. This might lead our resources open. Hence this approach results into leakage of resources. Also, since we have instances not closed, they are still using memory and we much end up into heap space issue especially we have huge application and heavy calls to DB.
There is lot boilerplate code to ensure that our resources are closed properly in every instance. Such redundant code makes developer worry about the resources rather than required result & logic.
Applying "try-with-resources" statement
public static int getRecordCount() {
int recCount = -1;
String URL = "jdbc:oracle:thin:username/password@hostname:port:dbname";
String select = "SELECT COUNT(*) FROM RECORD_M WHERE ID = 1";
try (Connection c = DriverManager.getConnection(URL);
Statement stmt = c.createStatement();
ResultSet rs = stmt.executeQuery(select);) {
if (rs.next()) {
recCount = rs.getInt(1);
}
} catch (Exception ex) {
// Log.logError(Log.SERVICE, this SQL Error occured while selecting
// record");
}
// Finally block can be added to do other activies
return recCount;
}
A try-with-resources statement is parameterized with variables (known as resources) that are initialized before execution of the try block and closed automatically. Catch clauses and a finally clause are often unnecessary when resources are closed automatically.
Following is the Syntax of Try with Resource Block:
TryWithResourcesStatement:
try (Modifier Type VariableName = Expression;
[Modifier Type VariableName = Expression])
{
Block
}
[Catches]
[Finally]
Also, following statements holds true in case of try with blocks
1)
Multiple Resources for try statement : We can have one or more local variables with initialize expressions to act as resources for the try statement.
2)
Resources are final : Resources are implicitly declared as final. It will throw compile time error "Resource X of try-with-resources cannot be assigned".
3)
Resource must implement AutoCloseable interface : Resources must be a subtype of AutoCloseable, or a compile-time error occurs "Resource type "X" does not implement java.lang.Autocloseable."
4)
Scope of resource variables : Scope of variable declared is within the limit of entire try block associated with the try-with-resources statement and same name cannot be redeclared within the try block as local variable unless shadowed using Local Class.
5)
Exception in Resource Initialization : Resources are initialized in left-to-right order. If a resource fails to initialize (that is, its initialize expression throws an exception), then all resources initialized so far by the try-with-resources statement are closed. If all resources initialize successfully, the try block executes as normal and then all non-null resources of the try-with-resources statement are closed.
6)
Resource Closing: Resources are closed in the reverse order from that in which they were initialized. A resource is closed only if it initialized to a non-null value. An exception from the closing of one resource does not prevent the closing of other resources. Such an exception is suppressed if an exception was thrown previously by an initializer, the try block, or the closing of a resource.
This post is written by
Dipika Mulchandani. She is a freelance writer, loves to explore latest features in Java technology.
No comments:
Post a Comment