/*
 * 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.hayabusa.taglib;

import org.opengion.fukurou.db.Transaction;
import org.opengion.fukurou.db.TransactionImpl;
import org.opengion.fukurou.util.ToString;						// 6.1.1.0 (2015/01/17)

// import java.io.ObjectOutputStream;
// import java.io.ObjectInputStream;
// import java.io.IOException;

/**
 * コネクションを共有して、トランザクションを実現します。
 *
 * 通常のタグでは、コネクションプールより、その時々のコネクションを取り出して利用するため、
 * タグごとに異なるコネクションで処理されます。
 * また、commit や rollback などもそれぞれのタグで行われるため、連続処理時にエラーが
 * 発生しても、中途半端な状態になります。
 * ここでは、各 DBID 単位にコネクションを共有し、このタグの間は、同じオブジェクトを
 * commit や、rollback せずに使いまわすようにします。
 * これにより、複数タグ間のトランザクションや、異なる DBID 間のトランザクションを
 * 実現します。
 *
 * このタグは、doEndTag() メソッドが正常に呼び出されることで、トランザクションが成立します。
 * つまり、途中で、JSP出力が、SKIP_PAGE された場合は、commit もされません。
 * これは、データベースエラー以外のエラーでも、トランザクション処理されることを意味します。
 *
 * @og.formSample
 * ●形式：&lt;og:transaction &gt; ... &lt;/og:transaction &gt;
 * ●body：あり(EVAL_BODY_INCLUDE:BODYをインクルードし、{&#064;XXXX} は解析しません)
 *
 * ●Tag定義：
 *   &lt;og:transaction
 *       debug              【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false)
 *   &gt;   ... Body ...
 *   &lt;/og:transaction&gt;
 *
 * ●使用例
 *     &lt;og:transaction &gt;
 *         &lt;og:query command="NEW" dbid="SERVER1" &gt;
 *             insert into XX01 (aa,bb,cc) values ('AA','BB','CC') /&gt;
 *         &lt;/og:query &gt;
 *         &lt;og:query command="NEW" dbid="SERVER2" &gt;
 *             update YY02 set aa='AA',bb='BB',cc='CC' where uniq='00001' /&gt;
 *         &lt;/og:query &gt;
 *     &lt;/og:transaction &gt;
 *
 * @og.rev 5.1.9.0 (2010/08/01) 新規作成
 * @og.group ＤＢ登録
 *
 * @version  5.0
 * @author	 Kazuhiko Hasegawa
 * @since    JDK6.0,
 */
public class TransactionTag extends CommonTagSupport {
	/** このプログラムのVERSION文字列を設定します。	{@value} */
	private static final String VERSION = "6.4.3.3 (2016/03/04)" ;
	private static final long serialVersionUID = 643320160304L ;

	// TransactionTag では、Transaction インターフェースではなく、実装クラスで管理します。
//	private TransactionImpl tran;
	private transient TransactionImpl tran;				// 6.3.9.0 (2015/11/06) transient 追加

	/**
	 * デフォルトコンストラクター
	 *
	 * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
	 */
	public TransactionTag() {
		super();		// これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
	}

	/**
	 * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。
	 *
	 * @return	後続処理の指示( EVAL_BODY_INCLUDE )
	 */
	@Override
	public int doStartTag() {
		tran = new TransactionImpl( getApplicationInfo() );

		return EVAL_BODY_INCLUDE ;	// Body インクルード( extends TagSupport 時)
	}

	/**
	 * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
	 *
	 * TransactionTag の doEndTag() では、途中で、SKIP_PAGE されると、呼ばれません。
	 * これは、データベース以外のタグで、エラー等が発生したことになります。
	 * 最後の、endCommit() が呼ばれない限り、トランザクションは commit されずに、
	 * rollback されます。
	 *
	 * @og.rev 6.3.6.1 (2015/08/28) AutoCloseable の close() メソッドに対応。正常時は、commit()。
	 * @og.rev 6.4.3.3 (2016/03/04) 一般的なタグで、SKIP_PAGE された場合、rollback するようにします。
	 *
	 * @return	後続処理の指示
	 */
	@Override
	public int doEndTag() {
		debugPrint();		// 4.0.0 (2005/02/28)

//		// finish() は、TransactionImpl のメソッドです。
//		if( tran != null ) { tran.finish(); }
//		if( tran != null ) { tran.commit(); }
		if( tran != null ) { tran.endCommit(); }		// 6.4.3.3 (2016/02/26

		return EVAL_PAGE ;		// ページの残りを評価する。
	}

	/**
	 * タグの処理中(セッターメソッドを除く)の例外を全て受け取ります。
	 *
	 * タグの中のボディ部の評価中、または Tag.doStartTag(), Tag.doEndTag(),
	 * IterationTag.doAfterBody(), BodyTag.doInitBody() のいずれもの
	 * メソッドの中で、Throwableが投げられたときに呼び出されます。
	 *
	 * このメソッドはセッターメソッドの中でThrowableが起きた場合は呼び出されません。
	 *
	 * @og.rev 6.3.6.1 (2015/08/28) AutoCloseable の close() メソッドに対応。異常時は、rollback()。
	 *
	 * @param	th	このタグを通過してきたThrowableな例外
	 */
	@Override
	public void doCatch( final Throwable th ) throws Throwable {
		if( tran != null ) { tran.rollback(); }
		super.doCatch( th );
	}

	/**
	 * タグリブオブジェクトをリリースします。
	 * キャッシュされて再利用されるので、フィールドの初期設定を行います。
	 *
	 * @og.rev 6.3.6.1 (2015/08/28) AutoCloseable の close() メソッドに対応。終了時は、finish()。
	 */
	@Override
	protected void release2() {
		super.release2();

//		// realClose() は、TransactionImpl のメソッドです。
//		if( tran != null ) { tran.realClose(); }
		// finish() は、TransactionImpl のメソッドです。
		if( tran != null ) { tran.finish(); }
		tran = null;
	}

	/**
	 * Transactionオブジェクトを返します。
	 *
	 * @og.rev 6.3.6.1 (2015/08/28) Transactionオブジェクトの取得方法変更。
	 *
	 * @return	Transactionオブジェクト
	 */
//	protected Transaction getTransaction() {
	protected Transaction getTranObj() {
		return tran ;
	}

//	/**
//	 * シリアライズ用のカスタムシリアライズ書き込みメソッド。
//	 *
//	 * @og.rev 4.0.0.0 (2006/09/31) 新規追加
//	 * @serialData 一部のオブジェクトは、シリアライズされません。
//	 *
//	 * @param	strm	ObjectOutputStreamオブジェクト
//	 * @throws IOException	入出力エラーが発生した場合
//	 */
//	private void writeObject( final ObjectOutputStream strm ) throws IOException {
//		strm.defaultWriteObject();
//	}

//	/**
//	 * シリアライズ用のカスタムシリアライズ読み込みメソッド
//	 *
//	 * ここでは、transient 宣言された内部変数の内、初期化が必要なフィールドのみ設定します。
//	 *
//	 * @og.rev 4.0.0.0 (2006/09/31) 新規追加
//	 * @serialData 一部のオブジェクトは、シリアライズされません。
//	 *
//	 * @param	strm	ObjectInputStreamオブジェクト
//	 * @see #release2()
//	 * @throws IOException	シリアライズに関する入出力エラーが発生した場合
//	 * @throws ClassNotFoundException	クラスを見つけることができなかった場合
//	 */
//	private void readObject( final ObjectInputStream strm ) throws IOException , ClassNotFoundException {
//		strm.defaultReadObject();
//	}

	/**
	 * このオブジェクトの文字列表現を返します。
	 * 基本的にデバッグ目的に使用します。
	 *
	 * @return このクラスの文字列表現
	 * @og.rtnNotNull
	 */
	@Override
	public String toString() {
		return ToString.title( this.getClass().getName() )
				.println( "VERSION"		,VERSION	)
				.println( "Other..."	,getAttributes().getAttribute() )
				.fixForm().toString() ;
	}
}
