/*
 * Copyright (c) 2009 The openGion Project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package org.opengion.fukurou.system;								// 6.4.2.0 (2016/01/29) package変更 fukurou.util → fukurou.system

import java.io.IOException;
import java.io.Closeable;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.sql.SQLException;
import java.util.zip.ZipFile;		// 5.5.2.6 (2012/05/25)

import static org.opengion.fukurou.system.HybsConst.CR;				// 6.1.0.0 (2014/12/26) refactoring

/**
 * Closer.java は、共通的に使用される close処理を集約した、クラスです。
 *
 * 各種 close() 処理では、Exception が発生しても、どうすることも出来ない
 * ケースが多々あります。また、close() 処理中の Exception の為だけに、
 * try ～ catch 節を用意しなければならず、finally 節内からの さらなる
 * throw など、コーディング上、本流以外の箇所で、色々と問題が発生します。
 * ここでは、とりあえず、LogWriter.log するだけにしていますが、
 * 将来的には、エラーを別ファイルにセーブしたり、データベースに書き込んだり
 * 出来ると思います。
 *
 * また、close 処理とは異なりますが、commit や、rollback など、finally 節に
 * 書き込んで、必ず処理したいが、Exception 発生時に、どうしようもない処理も、
 * ここに集約していきます。
 *
 * @og.rev 6.4.2.0 (2016/01/29) エラー処理を統一的に行います。
 * @og.rev 6.4.2.0 (2016/01/29) package変更 fukurou.util → fukurou.system
 *
 * @version  4.0
 * @author	 Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public final class Closer {
	// 6.4.2.0 (2016/01/29) エラー処理を統一的に行います。
	private static final String IO_CLOSE_ERR	= "ストリーム close 処理でエラーが発生しました。" ;
	private static final String COMMIT_ERR		= "Connection を commit することが出来ません。" ;
	private static final String ROLLBACK_ERR	= "Connection を rollback することが出来ません。" ;
	private static final String CONN_CLOSE_ERR	= "Connection を close することが出来ません。" ;
	private static final String STMT_CLOSE_ERR	= "Statement を close することが出来ません。" ;
	private static final String RESULT_CLOSE_ERR= "ResultSet を close することが出来ません。" ;
	private static final String ZIP_CLOSE_ERR	= "ZipFile/JarFile を close することが出来ません。" ;
	private static final String UNNONE_ERR		= "予期せぬエラーが発生しました。" ;

	/**
	 * すべてが staticメソッドなので、コンストラクタを呼び出さなくしておきます。
	 *
	 */
	private Closer() {}

	/**
	 * io関連の close 処理時の IOException を無視して、close 処理を行います。
	 * ここでは、処理中のエラーは、System.err に出力するだけで無視します。
	 *
	 * これにより、try ～ catch ～ finally 処理で、close を finally 処理から
	 * 例外を送出させなくてすむようになります。
	 * 引数が、null の場合は、何も処理しません。(正常:trueを返します。)
	 *
	 * @og.rev 4.0.0.0 (2007/02/08) 新規追加
	 *
	 * @param obj Closeableインターフェースを実装したIO関連オブジェクト
	 *
	 * @return 正常:true/異常:false
	 */
	public static boolean ioClose( final Closeable obj ) {
		boolean isOK = true;

		try {
			if( obj != null ) { obj.close(); }
		}
		catch( final IOException ex ) {
			isOK = false;
			logWriter( IO_CLOSE_ERR , obj.toString() , ex );
		}
		catch( final RuntimeException ex ) {
			isOK = false;
			logWriter( UNNONE_ERR , obj.toString() , ex );
		}

		return isOK;
	}

	/**
	 * Connection オブジェクトを commit します。
	 * ここでは、処理中のエラーは、System.err に出力するだけで無視します。
	 *
	 * @og.rev 4.0.0.0 (2007/02/08) 新規追加
	 *
	 * @param conn コネクションオブジェクト
	 *
	 * @return 正常:true/異常:false
	 */
	public static boolean commit( final Connection conn ) {
		boolean isOK = true;

		try {
			if( conn != null ) { conn.commit(); }
		}
		catch( final SQLException ex ) {
			logWriter( COMMIT_ERR , ex.getSQLState() , ex );
		}
		catch( final RuntimeException ex ) {
			isOK = false;
			logWriter( UNNONE_ERR , ex.getMessage() , ex );
		}

		return isOK;
	}

	/**
	 * Connection オブジェクトをrollbackします。
	 * ここでは、処理中のエラーは、標準出力に出力するだけで無視します。
	 *
	 * @og.rev 4.0.0.0 (2007/02/08) 新規追加
	 * @og.rev 8.1.0.4 (2022/01/28) Connection#isClosed で先に判定する。
	 *
	 * @param conn コネクションオブジェクト
	 *
	 * @return 正常:true/異常:false
	 */
	public static boolean rollback( final Connection conn ) {
		boolean isOK = true;

		try {
//			if( conn != null ) { conn.rollback(); }
			if( conn != null && !conn.isClosed() ) { conn.rollback(); }	// 8.1.0.4 (2022/01/28)
		}
		catch( final SQLException ex ) {
			logWriter( ROLLBACK_ERR , ex.getSQLState() , ex );
		}
		catch( final RuntimeException ex ) {
			isOK = false;
			logWriter( UNNONE_ERR , ex.getMessage() , ex );
		}

		return isOK;
	}

	/**
	 * Connection オブジェクトをcloseします。
	 * ここでは、処理中のエラーは、標準出力に出力するだけで無視します。
	 *
	 * ここでは、現実の Connection の close() メソッドを呼び出しますので、
	 * キャッシュ等で使用しているコネクションには適用しないでください。
	 *
	 * @og.rev 4.0.0.0 (2007/02/08) 新規追加
	 * @og.rev 5.5.5.0 (2012/07/28) commit追加
	 * @og.rev 5.9.4.0 (201601//08) commit前にvalidのチェックを行う(10秒でタイムアウト)
	 *
	 * @param conn コネクションオブジェクト
	 *
	 * @return 正常:true/異常:false
	 */
	public static boolean connClose( final Connection conn ) {
		boolean isOK = true;

		try {
			if( conn != null && ! conn.isClosed() ) {
				if( conn.isValid(10) ){		// 5.9.4.0 (2016/01/08)
					conn.commit();			// 5.5.5.0 (2012/07/28)
				}
				conn.close();
			}
		}
		catch( final SQLException ex ) {
			logWriter( CONN_CLOSE_ERR , ex.getSQLState() , ex );
		}
		catch( final RuntimeException ex ) {
			isOK = false;
			logWriter( UNNONE_ERR , ex.getMessage() , ex );
		}

		return isOK;
	}

	/**
	 * Statement オブジェクトをクローズします。
	 * ここでは、処理中のエラーは、標準出力に出力するだけで無視します。
	 *
	 * @og.rev 4.0.0.0 (2007/02/08) 新規追加
	 * @og.rev 8.1.0.3 (2022/01/21) isClosed()ﾁｪｯｸ他
	 *
	 * @param stmt Statementオブジェクト
	 *
	 * @return 正常:true/異常:false
	 */
	public static boolean stmtClose( final Statement stmt ) {
		boolean isOK = true;

		try {
//			if( stmt != null ) { stmt.close(); }
			if( stmt != null && !stmt.isClosed() ) { stmt.close(); }	// 8.1.0.3 (2022/01/21)
		}
		catch( final SQLException ex ) {
			logWriter( STMT_CLOSE_ERR , ex.getSQLState() , ex );
		}
		catch( final RuntimeException ex ) {
			isOK = false;
			logWriter( UNNONE_ERR , ex.getMessage() , ex );
		}

		return isOK;
	}

	/**
	 * ResultSet オブジェクトをクローズします。
	 * ここでは、処理中のエラーは、標準出力に出力するだけで無視します。
	 *
	 * @og.rev 4.0.0.0 (2007/02/08) 新規追加
	 * @og.rev 8.1.0.3 (2022/01/21) isClosed()ﾁｪｯｸ他
	 *
	 * @param result ResultSetオブジェクト
	 *
	 * @return 正常:true/異常:false
	 */
	public static boolean resultClose( final ResultSet result ) {
		boolean isOK = true;

		try {
//			if( result != null ) { result.close(); }
			if( result != null && !result.isClosed() ) { result.close(); }	// 8.1.0.3 (2022/01/21)
		}
		catch( final SQLException ex ) {
			logWriter( RESULT_CLOSE_ERR , ex.getSQLState() , ex );
		}
		catch( final RuntimeException ex ) {
			isOK = false;
			logWriter( UNNONE_ERR , ex.getMessage() , ex );
		}

		return isOK;
	}

	/**
	 * ZipFile オブジェクトをクローズします。
	 * Jar ファイルも、このメソッドでクローズします。
	 * ここでは、処理中のエラーは、標準出力に出力するだけで無視します。
	 *
	 * @og.rev 5.5.2.6 (2012/05/25) findbugs対応に伴い、新規追加
	 *
	 * @param zipFile ZipFileオブジェクト
	 *
	 * @return 正常:true/異常:false
	 */
	public static boolean zipClose( final ZipFile zipFile ) {
		boolean isOK = true;

		try {
			if( zipFile != null ) { zipFile.close(); }
		}
		catch( final IOException ex ) {
			logWriter( ZIP_CLOSE_ERR , zipFile.getName() , ex );
		}
		catch( final RuntimeException ex ) {
			isOK = false;
			logWriter( UNNONE_ERR , ex.getMessage() , ex );
		}

		return isOK;
	}

	/**
	 * AutoCloseable オブジェクトをクローズします。
	 * これは、Java1.7 で導入された、try-with-resourcesブロックの終了時に自動的に呼び出される
	 * close() メソッドを、使用します。
	 * 基本的には、try-with-resourcesブロックを使用すべきですが、Exception を発生させたくないため
	 * ここで、close() する処理を残しておきます。
	 * ちなみに、IO系や、SQL系も、AutoCloseable を継承していますが、独自のエラーメッセージを出せないため、
	 * エラーの内容がわかっている場合は、個々に適したクロースメソッドを呼び出してください。
	 *
	 * @og.rev 6.4.2.0 (2016/01/29) エラー処理を統一的に行います。
	 *
	 * @param autoClose AutoCloseableオブジェクト
	 *
	 * @return 正常:true/異常:false
	 */
	public static boolean autoClose( final AutoCloseable autoClose ) {
		boolean isOK = true;

		try {
			if( autoClose != null ) { autoClose.close(); }
		}
		catch( final Throwable th ) {
			isOK = false;
			logWriter( UNNONE_ERR , th.getMessage() , th );
		}

		return isOK;
	}

	/**
	 * Exception 発生時の処理を、統一します。
	 * ここでは、LogWriter に、書き出します。
	 *
	 * @og.rev 6.4.2.0 (2016/01/29) エラー処理を統一的に行います。
	 *
	 * @param msg  エラー時のメッセージ
	 * @param cmnt コメント
	 * @param th   Throwableオブジェクト
	 */
	private static void logWriter( final String msg , final String cmnt , final Throwable th ) {
		final String errMsg = msg + th.getMessage() + ":" + cmnt + CR;
		LogWriter.log( errMsg );
		LogWriter.log( th );
	}
}
