001/*
002 * Copyright (c) 2009 The openGion Project.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
013 * either express or implied. See the License for the specific language
014 * governing permissions and limitations under the License.
015 */
016package org.opengion.fukurou.db;
017
018import org.opengion.fukurou.util.Closer;
019import org.opengion.fukurou.util.ApplicationInfo;
020
021import java.sql.Connection;
022
023import java.util.Locale;
024import java.util.Map;
025import java.util.HashMap;
026
027/**
028 * コネクションを共有して、トランザクションを実現します。
029 *
030 * 基本的には、TransactionTag で利用されますが、一部、このオブジェクトを
031 * 渡して、直接、利用するケースもあります。
032 *
033 * トランザクションがすべて完了した後で、realClose() メソッドを呼び出します。
034 * 一度でも、rollback が指定されていれば、ロールバックを行い、コネクションを
035 * 破棄します。それ以外で、commit が指定されていれば、コミットを行い、
036 * コネクションを、プールに戻します。どちらも指定されていなければ、
037 * コネクションプールに戻すだけになります。
038 *
039 * 考え方として、下記のような流れになります。
040 * <pre>
041 *   TransactionImpl tran = new TransactionImpl( appInfo ) ;
042 *   try {
043 *      ・・・・・
044 *      tran.commit();
045 *      tran.finish();
046 *   }
047 *   catch( Exception ex ) {
048 *      tran.rollback();
049 *   }
050 *   finally {
051 *      tran.realClose()
052 *   }
053 * </pre>
054 *
055 * @og.rev 5.1.9.0 (2010/08/01) 新規作成
056 *
057 * @version  5.0
058 * @author       Kazuhiko Hasegawa
059 * @since    JDK6.0,
060 */
061public class TransactionImpl implements Transaction {
062        //* このプログラムのVERSION文字列を設定します。   {@value} */
063        private static final String VERSION = "5.3.8.0 (2011/08/01)" ;
064
065        private static final long serialVersionUID = 538020110801L ;
066
067        private static final String  DBID = "DEFAULT";
068        private final ApplicationInfo appInfo ;
069
070        private Connection defconn = null;              // 最も利用率の高いDEFAULTだけ、別に変数を用意。
071
072        private final Map<String,Connection> dbidMap = new HashMap<String,Connection>();
073        private boolean isCommit   = false;
074        private boolean isRollback = false;
075        private boolean isError    = false;
076        private boolean isFinish   = false;
077
078        /**
079         * ApplicationInfo を指定して作成する、コンストラクター
080         *
081         * このクラスは、基本的には、TransactionTag クラスから作成されます。
082         *
083         * @param       appInfo 内部統制用のアクセス情報
084         */
085        public TransactionImpl( final ApplicationInfo appInfo ) {
086                this.appInfo = appInfo ;
087        }
088
089        /**
090         * 指定のDBID に対応した、Connection オブジェクトを返します。
091         * 内部Mapに存在していれば、そのコネクションを、存在しなければ、
092         * 新しく作成します。
093         *
094         * @param       dbid  接続先ID
095         *
096         * @return      指定のDBID に対応した、Connectionオブジェクト
097         */
098        public Connection getConnection( final String dbid ) {
099                if( dbid == null || dbid.length() == 0 || DBID.equalsIgnoreCase( dbid ) ) {
100                        if( defconn == null ) {
101                                defconn = ConnectionFactory.connection( DBID,appInfo );
102                        }
103                        return defconn;
104                }
105
106                String udbid = dbid.toUpperCase( Locale.JAPAN );        // 大文字化
107
108                Connection conn = dbidMap.get( udbid );
109                if( conn == null ) {
110                        conn = ConnectionFactory.connection( udbid,appInfo );
111                        dbidMap.put( udbid,conn );
112                }
113
114                return conn;
115        }
116
117        /**
118         * コミット処理が行われた場合に、内部フラグ(isCommit)を true にセットします。
119         * 1回でもコミットが行われており、ロールバックが行われていなければ、
120         * コミットされます。
121         *
122         * 検索処理のみで、エラーが発生していない場合は、コミットも行われないケースがあります。
123         *
124         * @return 正常:true/異常:false
125         */
126        public boolean commit() {
127                isCommit = true;
128                return true;
129        }
130
131        /**
132         * ロールバック処理が行われた場合に、内部フラグ(isRollback)を true にセットします。
133         * 1回でもロールバックが行われていれば、最終的にはロールバックされます。
134         *
135         * ロールバック指定の場合は、isError フラグを true(エラー有)にセットします。
136         *
137         * @return 正常:true/異常:false
138         */
139        public boolean rollback() {
140                isRollback = true;
141                isError    = true;
142                return true;
143        }
144
145        /**
146         * トランザクションの、終了時処理を行います。
147         *
148         * 実質的には、なにもしません。
149         *
150         * @see #close( boolean )
151         *
152         * @return 正常:true/異常:false
153         */
154        public boolean close() {
155                return close( false );
156        }
157
158        /**
159         * トランザクションの、終了時処理を行います。
160         *
161         * 引数は、正常かどうかを判定するフラグです。異常の場合は、true をセットします。
162         * ここでは、実際には何もしませんが、内部的にエラーフラグをセットします。
163         * (エラーの場合のみセット。リセットはされません)
164         * 一度でも、エラーが発生したコネクションは、破棄します。それ以外は、プールに戻します。
165         *
166         * @param       errFlag         [true:エラー状態/false:通常]
167         *
168         * @return 正常:true/異常:false
169         */
170        public boolean close( final boolean errFlag ) {
171                if( errFlag ) { isError = true; }
172                return true;
173        }
174
175        /**
176         * トランザクションとして、正常終了時に処理を行います。
177         *
178         * 実質的には、内部のfinishフラグをセットするだけです。
179         * ただし、このフラグがセットされていない場合は、処理が途中で止まった
180         * 可能性があるため、トランザクションとしては、正常終了させることができません。
181         *
182         * @see #realClose()
183         */
184        public void finish() {
185                isFinish = true;
186        }
187
188        /**
189         * トランザクションとして、終了時処理を行います。
190         *
191         * トランザクションがすべて完了した後で、呼び出します。
192         * 一度でも、Rollback が指定されていれば、ロールバックを行い、コネクションを
193         * 破棄します。それ以外で、Commit が指定されていれば、コミットを行い、
194         * コネクションを、プールに戻します。どちらも指定されていなければ、
195         * コネクションプールに戻すだけになります。
196         *
197         * @og.rev 5.3.8.0 (2011/08/01) 内部変数を初期化し、このオブジェクトが再利用できるようにする。
198         */
199        public void realClose() {
200                if( defconn != null ) {
201                        connClose( defconn,DBID );
202                }
203
204                for( Map.Entry<String,Connection> entry : dbidMap.entrySet() ) {
205                        String     dbid = entry.getKey();
206                        Connection conn = entry.getValue();
207
208                        connClose( conn,dbid );
209                }
210
211                // 内部変数を初期化します。
212                defconn    = null;
213
214                // 5.3.8.0 (2011/08/01) 内部変数を初期化し、このオブジェクトが再利用できるようにする。
215//              dbidMap    = null;
216                dbidMap.clear();
217
218                isCommit   = false;
219                isRollback = false;
220                isError    = false;
221                isFinish   = false;
222        }
223
224        /**
225         * Connection オブジェクトをクローズします。
226         *
227         * 実際にはクローズせず、commit または、rollback を行った後、
228         * エラー状況に応じて、ConnectionFactory に返却するか、削除します。
229         * なお、commit または、rollback 時にエラーが発生した場合でも、無視して
230         * そのまま、処理を継続します。
231         *
232         * @param  conn クローズ処理を行う、Connection オブジェクト
233         * @param  dbid 接続先ID
234         */
235        private void connClose( final Connection conn, final String dbid ) {
236                // まず、コミットかロールバックされた場合は、どちらかの処理が必要
237                if( isCommit || isRollback ) {
238                        // commit できる条件:コミットされ、フィニッシュされ、エラーでなく、ロールバックでない
239                        if( isCommit && isFinish && (!isError) && (!isRollback) ) {
240                                Closer.commit( conn );
241                        }
242                        // それ以外は、ロールバックする。
243                        else {
244                                Closer.rollback( conn );
245                        }
246                }
247
248                // 残りは、コミットもロールバックもされていないため、単にキャッシュへの返し方のみ判定する。
249                // isFinish されていないが、エラーにもなっていない場合は、接続要因以外の問題なので、返却でよい。
250                if( isError ) { ConnectionFactory.remove( conn,dbid ); }        // 削除
251                else {                  ConnectionFactory.close( conn,dbid );  }        // 返却
252
253        }
254}