アプリケーションのリファクタリングを行っており、ISOスキャン(HP Fortifyに基づく静的コード分析ツール)で報告される問題の数を減らすようにしています。現在、私が対処しようとしているのは、アプリケーションが構築した「未リリースのリソース:データベース」の問題です。これの最も顕著な理由の1つは、次のような構成です。
Connection conn = null;
Boolean myConn = false;
try{
if(conn == null){
conn = DatabaseUtil.getConnection();
myConn = true;
}
result = DbClass.getObject(conn, otherParameters);
}catch(DatabaseException de){
throw de;
}catch(SQLException sqle){
throw new DatabaseException("Error Message");
}finally{
if(myConn && conn != null){
try{
conn.rollback();conn.close();
}catch(SQLException sqle){}
}
これは、アプリケーションのかなり標準的な構成であり、Tryブロックの最後にデータベース接続が確実に閉じられるようにします。しかし、使用するたびに、ツールはそれを問題として報告します。
このコンストラクトに誤りはありますか?問題である場合、それを修正するために私たちができることはありますか?
そして、ツールは正しいです!
分析の(可能性のある)制限とは別に、_(conn != null) == myConn
_を理解する場合としない場合があります。rollback
がSqlException
をスローすることを確認してください。存在する場合、close
が呼び出されることはありませんです。これは、正確にリソースリークの定義です。 current DB、ドライバ、およびプールライブラリがrollback
をスローした場合、接続はすでに解放されていますが、これは一般的なケースでは当てはまりません。したがって、潜在的な問題です。
コメントに記載されているように、Java 7を使用している場合、_try-with-resources
_を使用することにより、この構成の多くを簡略化できます。また、 tryブロックのclose()
メソッドでのロールバック:
_@Override
public void close() throws SQLException {
try {
rollback();
catch (...) {
//Do stuff
finally {
super.close();
}
}
_
ここでは、rollback
が何をする場合でも、close()
が実際に実行されることが100%保証されます。
レガシーソフトウェアを使用している場合は、上記のコードを直接try
に配置できます。
_Connection conn = null;
try {
conn = DatabaseUtil.getConnection();
result = DbClass.getObject(conn, otherParameters);
} catch(SQLException sqle) {
throw new DatabaseException("Error Message");
} finally {
if (conn != null) {
try{
conn.rollback();
} catch(SQLException sqle) {
//try-with-resources would add this to suppressed exceptions, you can either log or rethrow
} finally {
conn.close();
}
}
_
リソースのリリースはブール変数の状態を条件とし、別のtry
ブロックに入れられるため、静的アナライザーはrollback()
およびclose()
が実行が保証されています。
getConnection()
ファクトリメソッドによって返されたタイプのConnection
オブジェクトのドキュメントを確認し、メソッドrollback()
およびclose()
が例外をスローします。そうしない場合、これらのメソッドを呼び出すときに、追加のtry
catch
ブロックは必要ありません。
ただし、最初に、開いている接続を通知するために使用しているブール変数を削除します。必要ありません。接続の確立に成功した場合を除き、getConnection()
ファクトリメソッドから接続オブジェクトを取得しないでください。
Fortifyのセキュリティレポートは次のコード構造を示唆していることに注意してください:
public void execCxnSql(Connection conn) {
Statement stmt;
try {
stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(CXN_SQL);
...
}
finally {
if (stmt != null) {
safeClose(stmt);
}
}
}
public static void safeClose(Statement stmt) {
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
log(e);
}
}
}
...これは、ブール変数が問題であり、追加のtry
ブロックではないことを示唆しています。
コンパイラーはコードをまったく実行できなかったので、条件を取り除きます。
次に、Exception
ではなくSQLException
をキャッチして、接続が初期化されていない場合にNullPointerException
をキャッチします。 SybaseのEAServerペーパーで読んだ推奨事項と、おそらくJavaのガベージコレクターとは関係なく、EAServerの接続プールとは関係がないため、通常はオブジェクトを閉じた後でnullに設定します。おそらく"貨物養殖」ここに。
Connection conn = null;
Boolean myConn = false;
try{
if(conn == null){
conn = DatabaseUtil.getConnection();
// myConn = true; no need for this
}
result = DbClass.getObject(conn, otherParameters);
}catch(DatabaseException de){
throw de;
}catch(SQLException sqle){
throw new DatabaseException("Error Message");
}finally{
try{
conn.rollback();
conn.close();
conn=null; // good practice according to people at Sybase
}catch(Exception e){
//fails silently when conn is not initialized.
}
}