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.AbstractObjectPool;
019import org.opengion.fukurou.system.Closer;
020import org.opengion.fukurou.system.OgRuntimeException ;                         // 6.4.2.0 (2016/01/29)
021import static org.opengion.fukurou.system.HybsConst.CR;                         // 6.1.0.0 (2014/12/26) refactoring
022import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;      // 6.4.3.3 (2016/03/04)
023
024        // import java.util.concurrent.ExecutorService;                                 // 7.1.0.0 (2020/01/20)
025        // import java.util.concurrent.Executors;                                               // 7.1.0.0 (2020/01/20)
026        // import java.util.concurrent.Future;                                                  // 7.1.0.0 (2020/01/20)
027        // import java.util.concurrent.Callable;                                                // 7.1.0.0 (2020/01/20)
028        // import java.util.concurrent.TimeUnit;                                                // 7.1.0.0 (2020/01/20)
029        // import java.util.concurrent.ExecutionException;                              // 7.1.0.0 (2020/01/20)
030        // import java.util.concurrent.TimeoutException;                                // 7.1.0.0 (2020/01/20)
031        // import java.util.concurrent.CancellationException;                   // 7.1.0.0 (2020/01/20)
032import java.util.concurrent.ConcurrentMap;                                                      // 6.4.3.3 (2016/03/04)
033import java.util.concurrent.ConcurrentHashMap;
034import java.util.Locale;
035import java.util.Properties;
036import java.sql.Connection;
037import java.sql.SQLException;
038import java.sql.DriverManager;
039import java.sql.DatabaseMetaData;
040
041/**
042 * データベースのコネクションオブジェクトを取得する為に使用する,ファクトリクラスです。
043 *
044 * Connection.connection() メソッドで,Connectionオブジェクトを取得します。
045 * Connection#close() メソッドで,内部的に ConnectionFactory にオブジェクトを戻す
046 * 事によって,Connectionオブジェクトのプーリングを行なっています。
047 *
048 * コネクションオブジェクトは,プールから貸し出します。
049 * つまり,貸し出し中には,プールには,オブジェクトは残っていません。
050 * その状態で,コネクションオブジェクトをclose()しない場合は,オブジェクトが破棄されて,
051 * 貸し出し中カウントと実際のオブジェクト数が食い違い,リソースが不足します。
052 * 必ず,作成したオブジェクトは,close()メソッドを呼び出して,プールに返して下さい。
053 *
054 * システムリソースの USE_DB_APPLICATION_INFO=true の場合、コネクションにアプリケーション
055 * 情報を追記するため、ApplicationInfoオブジェクトを使用します。
056 * このオブジェクトは、jsp/common/session-init.jsp にてユーザー情報とアプリケーション
057 * 情報を画面アクセスごとに設定します。
058 *
059 * @og.group DB/Shell制御
060 * @og.rev 4.0.0.0 (2007/10/16) パッケージ移動(hayabusa/db ⇒ fukurou/db)
061 *
062 * @version  4.0
063 * @author   Kazuhiko Hasegawa
064 * @since    JDK5.0,
065 */
066public final class ConnectionFactory {
067        /** 6.4.3.4 (2016/03/11) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。 */
068        private static final ConcurrentMap<String,ConnectionPool> CONN_MAP = new ConcurrentHashMap<>();
069
070        // 4.0.0.0 (2007/10/10) キャッシュされた、初期ConnectionPool を使用
071        // 4.0.0.0 (2007/10/29) 初期値をここでセットする
072        private static String                   defDBID = "DEFAULT";                                    // 6.3.9.1 (2015/11/27) Variables should start with a lowercase character(PMD)
073        private static ConnectionPool   defPOOL ;                                                               // 6.3.9.1 (2015/11/27) Variables should start with a lowercase character(PMD)
074        private static DatabaseConfig   dbConf ;
075
076        /**
077         *  デフォルトコンストラクターをprivateにして、
078         *  オブジェクトの生成をさせないようにする。
079         *
080         */
081        private ConnectionFactory() {
082                // オブジェクトの生成をさせないようにする。
083        }
084
085        /**
086         * 初期化メソッドです。
087         * <pre>
088         * 1)第二引数にXMLファイルをクラスローダ基底からの相対パスで指定した場合は
089         *   そのXMLを利用してDBConfigオブジェクトを作成します。例:ConnectionFactory.init( CONTEXT_NAME, "../DBConfig.xml")
090         *   nullの場合はWEB-INF/DBConfig.xmlを利用します。例:ConnectionFactory.init( CONTEXT_NAME, null)
091         * 2)キャッシュ初期ConnectionPoolのキーを設定してキャッシュプールを作ります。
092         *   この値がnullの場合は"DEFAULT"が設定されます。
093         * </pre>
094         *
095         * <strong>このクラスを利用する場合は必ず最初にこのメソッドを実行する必要があります。</strong>
096         * キャッシュとDBConfigオブジェクトの同期化はされていないので初期化以外での利用は避けて下さい。
097         *
098         * @og.rev 4.0.0.0 (2007/11/05) 新規作成
099         * @og.rev 6.4.3.3 (2016/03/04) DatabaseConfig のコンストラクター修正で、引数の nullチェックは不要。
100         * @og.rev 7.1.0.0 (2020/01/20) #ConnectionPool(DatabaseConfig,String) に統一
101         *
102         * @param defPoolKey  初期DBID名(nullの場合は、"DEFAULT")
103         * @param xmlFileName DBConfig.xmlファイルのファイル名(nullの場合は、WEB-INF/DBConfig.xml)
104         */
105        public static void init( final String defPoolKey, final String xmlFileName ) {
106                // DBConfigオブジェクトの作成
107                // 6.4.3.3 (2016/03/04) DatabaseConfig のコンストラクター修正で、引数の nullチェックは不要。
108                dbConf = new DatabaseConfig( xmlFileName );
109
110                if( defPoolKey == null || defPoolKey.isEmpty() || dbConf.getDbid( defPoolKey ) == null ) {
111                        defDBID = "DEFAULT";
112                }
113                else {
114                        defDBID = defPoolKey;
115                }
116
117                defPOOL = new ConnectionPool( dbConf,defDBID );         // 7.1.0.0 (2020/01/20)
118//
119//              final EDbid edbid = dbConf.getDbid( defDBID );
120//              if( edbid == null ) {
121//                      final String errMsg = "初期化時に、指定のDBIDキーが存在しません。"
122//                              + "[Key ="
123//                              + defDBID
124//                              + "]";
125//                      throw new OgRuntimeException( errMsg );
126//              }
127//
128//      //      if( DEF_POOL != null ) { DEF_POOL.clear(); }    // 6.0.2.5 (2014/10/31) nullでなければ初期化する。
129//              defPOOL = new ConnectionPool( edbid );
130        }
131
132        /**
133         * コネクションオブジェクトを取得します。
134         * 遅い初期化を行なう事により,実際に必要となるまでコネクションオブジェクトは
135         * 作成しません。
136         * 最大プール数に達して,なおかつ,すべてのConnectionが貸し出し中の場合,
137         *
138         * @og.rev 2.1.1.3 (2002/11/22) コネクションID が null の場合に DEFAULT から所得するように変更。
139         * @og.rev 3.1.0.0 (2003/03/20) Hashtable を使用している箇所で、非同期でも構わない箇所を、HashMap に置換え。
140         * @og.rev 3.5.6.2 (2004/07/05) 文字列の連結にStringBuilderを使用します。
141         * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
142         * @og.rev 3.8.8.2 (2007/01/26) USE_DB_APPLICATION_INFO ⇒ pool.useApplicationInfo() 変更
143         * @og.rev 4.0.0.0 (2007/10/10) キャッシュされた、初期ConnectionPool を使用
144         * @og.rev 4.1.0.1 (2008/01/21) 登録時に、大文字に変換する。
145         * @og.rev 6.4.3.3 (2016/03/04) Map#computeIfAbsent で対応する。
146         *
147         * @param   dbid 接続先ID
148         * @param   appInfo アプリ情報オブジェクト
149         *
150         * @return  コネクションオブジェクト
151         */
152        public static Connection connection( final String dbid , final ApplicationInfo appInfo ) {
153                final ConnectionPool pool ;
154                if( dbid == null || dbid.isEmpty() || defDBID.equalsIgnoreCase( dbid ) ) {
155                        pool = defPOOL ;
156                }
157                else {
158                        final String udbid = dbid.toUpperCase( Locale.JAPAN );  // 大文字化
159
160                        // 6.4.3.3 (2016/03/04) Map#computeIfAbsent で対応する。
161                        // Map#computeIfAbsent : 戻り値は、既存の、または計算された値。追加有り、置換なし、削除なし
162                        // ※ 注意:ConnectionPool のコンストラクタに、従来と異なり、DatabaseConfig オブジェクトを渡しています。
163                        pool = CONN_MAP.computeIfAbsent( udbid , k -> new ConnectionPool( dbConf,udbid ) );
164                }
165
166                final Connection conn = pool.newInstance();
167
168                // 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを使用
169                // 3.8.8.2 (2007/01/26) ORACLE 以外は、使用しません。
170                // 4.0.0.0 (2007/11/29) 入れ子if の統合
171                if( appInfo != null && pool.useApplicationInfo() ) {
172                        appInfo.callAppInfo( conn );
173                }
174                return conn;
175        }
176
177        /**
178         * コネクションオブジェクトをプールに戻します。
179         * Connectionオブジェクトは,close()メソッドで,自分自身を ConnectionFactory の
180         * プールに戻します。
181         * それ以外の コネクションオブジェクトをプールに戻す場合は,このメソッドを使用します。
182         *
183         * @og.rev 2.1.1.3 (2002/11/22) コネクションID が null の場合に DEFAULT から所得するように変更。
184         * @og.rev 4.0.0.0 (2007/10/10) キャッシュされた、初期ConnectionPool を使用
185         * @og.rev 4.1.0.1 (2008/01/21) 登録時に、大文字に変換する。
186         * @og.rev 5.9.32.0 (2018/05/18) プールに戻す前に明示的にcommitをかける
187         *
188         * @param   conn コネクションオブジェクト
189         * @param   dbid 接続先ID
190         */
191        public static void close( final Connection conn,final String dbid ) {
192                if( conn != null ) {
193                        Closer.commit( conn );  // 5.9.32.0 (2018/05/18) プールに戻す前に明示的にcommitをかける
194                        if( dbid == null || dbid.isEmpty() || defDBID.equalsIgnoreCase( dbid ) ) {
195                                defPOOL.release( conn ) ;
196                        }
197                        else {
198                                final String udbid = dbid.toUpperCase( Locale.JAPAN );  // 大文字化
199        //                      synchronized( CONN_MAP ) {
200                                        final ConnectionPool pool = CONN_MAP.get( udbid );
201                                        if( pool != null ) {
202                                                pool.release( conn );
203                                        }
204        //                      }
205                        }
206                }
207        }
208
209        /**
210         * コネクションオブジェクトを物理的に削除(クローズ)戻します。
211         * これは、コネクション等がエラーを起こした場合に、プールに戻すのではなく、
212         * 接続を閉じる場合に、使用されます。
213         *
214         * @og.rev 2.1.1.3 (2002/11/22) コネクションID が null の場合に DEFAULT から所得するように変更。
215         * @og.rev 4.0.0.0 (2007/10/10) キャッシュされた、初期ConnectionPool を使用
216         * @og.rev 4.1.0.1 (2008/01/21) 登録時に、大文字に変換する。
217         *
218         * @param   conn コネクションオブジェクト
219         * @param   dbid 接続先ID
220         */
221        public static void remove( final Connection conn,final String dbid ) {
222                if( conn != null ) {
223                        if( dbid == null || dbid.isEmpty() || defDBID.equalsIgnoreCase( dbid ) ) {
224                                defPOOL.remove( conn ) ;
225                        }
226                        else {
227                                final String udbid = dbid.toUpperCase( Locale.JAPAN );  // 大文字化
228                //              synchronized( CONN_MAP ) {
229                                        final ConnectionPool pool = CONN_MAP.get( udbid );
230                                        if( pool != null ) {
231                                                pool.remove( conn );
232                                        }
233                //              }
234                        }
235                }
236        }
237
238        /**
239         * コネクションオブジェクトを実際にすべてクローズします。
240         * コネクションプールの再編成や,管理者による強制クローズに使用します。
241         *
242         * クローズに失敗(コネクションが貸し出し中)の場合は,内部的に
243         * DB_CLOSE_RETRY_TIME だけ待機して, DB_CLOSE_RETRY_COUNT 回数だけ,試行します。
244         * それでもクローズできない場合は, RuntimeException を throw します。
245         *
246         * @og.rev 4.0.0.0 (2005/01/31) ロジック見直し。 pool.clear() で、基本的にはすべて削除されます。
247         * @og.rev 4.0.0.0 (2007/10/10) キャッシュされた、初期ConnectionPool を使用
248         * @og.rev 6.4.3.3 (2016/03/04) Map#forEach で対応する。
249         */
250        public static void realClose() {
251                synchronized( defPOOL ) {
252                        if( ! defPOOL.isEmpty() ) {
253                                defPOOL.clear();
254                        }
255                }
256
257                // 6.4.3.3 (2016/03/04) Map#forEach で対応する。
258                CONN_MAP.forEach( (id,pl) -> pl.clear() );
259                CONN_MAP.clear();
260        }
261
262        /**
263         * ConnectionFactory の現在の状況(詳細メッセージ)を返します。
264         * これは,コネクションプールの数(最大値,作成済み数など)を確認する為のものです。
265         *
266         * @og.rev 4.0.0.0 (2007/10/10) キャッシュされた、初期ConnectionPool を使用
267         *
268         * @return  現在の状態表示
269         * @og.rtnNotNull
270         */
271        public static String information() {
272                return information( true );
273        }
274
275        /**
276         * ConnectionFactory の現在の状況を返します。
277         * これは,コネクションプールの数(最大値,作成済み数など)を確認する為のものです。
278         * 引数により詳細メッセージかどうかを指定できます。
279         *
280         * @og.rev 4.0.0.0 (2007/10/10) キャッシュされた、初期ConnectionPool を使用
281         * @og.rev 5.3.4.0 (2011/04/01) 詳細メッセージ用引数を追加
282         * @og.rev 5.6.7.3 (2013/08/23) 若干の修正
283         * @og.rev 6.4.3.3 (2016/03/04) Map#forEach で対応する。
284         * @og.rev 7.0.1.0 (2018/10/15) XHTML → HTML5 対応(空要素の、"/>" 止めを、">" に変更します)。
285         * @og.rev 7.1.0.0 (2020/01/20) DB未接続のDBIDは表示から除外します。
286         *
287         * @param       isDetail        詳細メッセージかどうか [true:詳細メッセージ/false:簡易メッセージ]
288         *
289         * @return  現在の状態表示
290         * @og.rtnNotNull
291         */
292        public static String information(final boolean isDetail ) {
293                // 4.0.0.0 (2007/10/25) hybsとの依存関係を弱めるため。
294                final StringBuilder strBuf = new StringBuilder( BUFFER_MIDDLE );
295
296                strBuf.append( "<b>【Connection Information】</b>" ).append( CR );        // 5.6.7.3 (2013/08/23) 若干の修正
297
298                synchronized( defPOOL ) {
299                        if( ! defPOOL.isEmpty() ) {
300                                // 5.3.4.0 (2011/04/01) 詳細メッセージ用引数を追加
301                                if( isDetail ) {
302//                                      strBuf.append( defPOOL.toString() ).append( "<br /><hr />" );
303                                        strBuf.append( defPOOL.toString() ).append( "<br><hr>" );               // 7.0.1.0 (2018/10/15)
304                                }
305                                else {
306                                        strBuf.append( defPOOL.dbidInfo() );
307                                }
308                        }
309                }
310
311                // 6.4.3.3 (2016/03/04) Map#forEach で対応する。
312                CONN_MAP.forEach( (id,pl) -> {
313                        if( pl.getDBName() != null ) {                  // 7.1.0.0 (2020/01/20) DB未接続のDBIDは表示から除外
314                                if( isDetail ) {
315//                                      strBuf.append( pl.toString() ).append( "<br /><hr />" );
316                                        strBuf.append( pl.toString() ).append( "<br><hr>" );                    // 7.0.1.0 (2018/10/15)
317                                }
318                                else {
319                                        strBuf.append( pl.dbidInfo() );
320                                }
321                        }
322                } );
323
324                return strBuf.append( CR ).toString();
325        }
326
327        /**
328         * この接続が、PreparedStatement#getParameterMetaData() を使用するかどうかを判定します。
329         *
330         * PreparedStatement に対して、String化された 数字などを setObject( int,String ) するとき、
331         * ORACLE と SQLServer は、そのまま設定すれば、自動的に変換されます。
332         * postgreSQL では、ParameterMetaData#getParameterType(int) で、カラムタイプを取得し、
333         * setObject( int,String,int ) する必要があります。
334         * その判定に、このメソッドを使用します。
335         * この結果は、あくまで、各種データベース毎の実地調査の結果を元に、判定結果を
336         * 返すようにしています。
337         * ORACLE の場合は、使用しない(false)が返るように設定しています。
338         * SQLServer では、ORACLEと同様に、false を返します。
339         *
340         * このメソッドは、元々、ApplicationInfo#useParameterMetaData(Connection) に有ったものを
341         * EDbid から取得するように修正したものです。
342         *
343         * @og.rev 5.3.8.0 (2011/08/01) 新規追加
344         * @og.rev 6.4.3.3 (2016/03/04) EDbid のnullチェックを追加
345         *
346         * @param dbid 接続先ID
347         *
348         * @return      [true:使用する/false:その他]
349         */
350        public static boolean useParameterMetaData( final String dbid ) {
351
352                // 6.1.0.0 (2014/12/26) refactoring の一環
353                final String udbid = dbid == null || dbid.isEmpty() ? defDBID : dbid.toUpperCase( Locale.JAPAN ) ;              // 6.4.2.1 (2016/02/05) PMD refactoring. Useless parentheses.
354
355                final EDbid edbid = dbConf.getDbid( udbid );
356
357                return edbid != null && edbid.useParamMetaData();
358        }
359
360        /**
361         * 接続先のDB名に対応した、enum (DBName) を返します(toUpperCase)。
362         *
363         * @og.rev 5.1.4.0 (2010/03/01) getDBFullName の代わりに新規作成
364         * @og.rev 5.7.7.2 (2014/06/20) 最初の取得時のエラー回避
365         *
366         * @param dbid 接続先ID
367         *
368         * @return  接続先のDB名
369         * @og.rtnNotNull
370         */
371        public static String getDBName( final String dbid ) {
372                final String dbName;
373
374                if( dbid == null || dbid.isEmpty() || defDBID.equalsIgnoreCase( dbid ) ) {
375                        dbName = defPOOL.getDBName();
376                }
377                else {
378                        final String udbid = dbid.toUpperCase( Locale.JAPAN );  // 大文字化
379                        ConnectionPool pool = null;
380        //              synchronized( CONN_MAP ) {
381                                pool = CONN_MAP.get( udbid );
382                                if( pool == null ) {
383                                        connection( dbid, null );               // ダミーで、コネクトする。
384                                        pool = CONN_MAP.get( udbid );   // connectionで、CONN_MAP に設定しているため、もう一度、取得する。
385                                }
386        //              }
387                        // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..;
388                        if( pool == null ) {
389                                final String errMsg = "指定のDBIDキーに対応するデータベース名を取得出来ません。"
390                                                                        + "[Key =" + dbid + "]";
391                                throw new OgRuntimeException( errMsg );
392                        }
393
394                        dbName = pool.getDBName();
395                }
396
397                return dbName.toUpperCase( Locale.JAPAN );
398        }
399
400        /**
401         * ConnectionPool は、AbstractObjectPool を継承した オブジェクトプールです。
402         *
403         * コネクションオブジェクトをプールすることにより、ConnectionFactory で
404         * 管理する Map オブジェクトの実態として、各ID毎の コネクションをキープします。
405         *
406         * @og.rev 7.1.0.0 (2020/01/20) Connection 作成時のタイムアウトを用意します。
407         *
408         * @og.group DB/Shell制御
409         *
410         * @version  4.0
411         * @author   Kazuhiko Hasegawa
412         * @since    JDK5.0,
413         */
414        // class ConnectionPool extends AbstractObjectPool<Connection> {
415        private static final class ConnectionPool extends AbstractObjectPool<Connection> {
416                private final transient EDbid edbid;
417                private final static    int   TIMEOUT = 10;                             // 7.1.0.0 (2020/01/20)
418
419                /**
420                 * DatabaseConfig と、dbid を指定して作成する コンストラクター
421                 * オブジェクト作成時のMap設定で、一連の処理を行うために、エラーチェックをもつ
422                 * コンストラクターを用意します。
423                 * DBID が null の場合は,"DEFAULT" が使用されます。
424                 *
425                 * @og.rev 6.4.3.3 (2016/03/04) 処理の簡素化のための新規コンストラクター追加
426                 *
427                 * @param   dbConf      DatabaseConfigオブジェクト
428                 * @param   dbid        接続先ID(大文字に変換済み)
429                 */
430                private ConnectionPool( final DatabaseConfig dbConf , final String dbid ) {
431                        super();
432
433                        final EDbid edbid = dbConf.getDbid( dbid );
434                        if( edbid == null ) {
435                                final String errMsg = "指定のDBIDキーが存在しません。"
436                                        + "[Key ="
437                                        + dbid
438                                        + "]";
439                                throw new OgRuntimeException( errMsg );
440                        }
441
442                        this.edbid      =       edbid;
443                        init( edbid.getMincount(),edbid.getMaxcount(),true,edbid.getPooltime() );
444                }
445
446//              /**
447//               * DBID を指定して作成する コンストラクター
448//               * DBID をキーに、 HybsSystem.sys メソッドのデータベース変数を取得します。
449//               * 取得するのは、 DBID + _DB_URL/_DB_USER/_DB_PASSWD/_DB_MINCOUNT/_DB_MAXCOUNT
450//               * です。
451//               * DBID が null の場合は,"DEFAULT" が使用されます。
452//               *
453//               * @og.rev 3.5.4.3 (2004/01/05) キャッシュの寿命を指定
454//               * @og.rev 3.5.4.7 (2004/02/06) DBID のゼロストリングチェック追加
455//               * @og.rev 4.0.0.0 (2007/10/10) キャッシュされた、初期ConnectionPool を使用
456//               * @og.rev 4.0.0.0 (2007/10/25) DB設定情報のXML化に伴う変更
457//               * @og.rev 6.4.1.1 (2016/01/16) PMD refactoring. It is a good practice to call super() in a constructor
458//               * @og.rev 7.1.0.0 (2020/01/20) #ConnectionPool(DatabaseConfig,String) に統一
459//               *
460//               * @param   edbid 接続先情報オブジェクト
461//               */
462//              public ConnectionPool( final EDbid edbid ) {
463//                      super();
464//
465//                      // 4.0.0.0 XML化に伴いロード先を変更
466//                      this.edbid      =       edbid;
467//                      init( edbid.getMincount(),edbid.getMaxcount(),true,edbid.getPooltime() );
468//              }
469
470                /**
471                 * オブジェクトプールから削除するときに呼ばれます。
472                 * このメソッドで各オブジェクトごとの終了処理を行います。
473                 * 例えば、データベースコネクションであれば、close() 処理などです。
474                 *
475                 * @og.rev 3.5.4.8 (2004/02/23) SQLException は無視します。
476                 * @og.rev 3.5.6.0 (2004/06/18) synchronized を解除します。
477                 *
478                 * @param  obj 終了処理を行うオブジェクト
479                 */
480                @Override
481                protected void objectFinal( final Connection obj ) {
482                        Closer.connClose( obj );
483                }
484
485                /**
486                 * コネクションオブジェクトを作成します。
487                 * DriverManager.getConnection により作成されたConnection を Connection で
488                 * ラッピングします。
489                 * Connectionオブジェクトは,close()メソッドで,自分自身を ConnectionFactory の
490                 * プールに戻します。
491                 *
492                 * @og.rev 3.3.3.3 (2003/08/06) コネクションに対して、setTransactionIsolation を、設定しておく。
493                 * @og.rev 3.5.2.0 (2003/10/20) 接続情報に、データベース名、ドライバ名情報を追加する。
494                 * @og.rev 3.5.6.0 (2004/06/18) synchronized を解除します。
495                 * @og.rev 3.8.8.2 (2007/01/26) useAppInfo を設定します。
496                 * @og.rev 4.0.0.0 (2007/10/30) 保持情報オブジェクト化に伴う変更
497                 * @og.rev 5.1.2.0 (2010/01/01) MySQL対応 明示的に、TRANSACTION_READ_COMMITTED を指定する。
498                 * @og.rev 5.5.2.0 (2012/05/01) properties対応
499                 * @og.rev 6.3.9.0 (2015/11/06) 内部Propertiesオブジェクトではなく、複製して返します。
500                 * @og.rev 7.1.0.0 (2020/01/20) Connection 作成時のタイムアウトを用意します。
501                 * @og.rev 5.11.0.0 (2021/05/17) エラーメッセージ出力追加
502                 *
503                 * @return  コネクションオブジェクト
504                 */
505                @Override
506                protected Connection createInstance() {
507                        Connection conn = null;
508                        try {
509                //              DriverManager.setLogWriter( HybsSystem.out );                   // ドライバーのログ
510
511                                // 5.5.2.0 (2012/05/01) propertyの追加処理と、接続のproperties化
512                                // 6.3.9.0 (2015/11/06) 内部Propertiesオブジェクトではなく、複製して返します。
513        //                      final Properties prop = new Properties (edbid.getProps());
514                                final Properties prop = edbid.getProps();
515                                prop.put ( "user"    , edbid.getUser() );
516                                prop.put ( "password", edbid.getPassword() );
517
518                                // 7.1.0.0 (2020/01/20) Connection 作成時のタイムアウトを用意します。
519        //                      if( TIMEOUT > 0 ) {
520        //                              DriverManager.setLoginTimeout( TIMEOUT );
521        //                              // このExecutorService はConnection を取得する一発物なので、シャットダウンしておく。
522        //                              final ExecutorService executor = Executors.newSingleThreadExecutor();           // 7.1.0.0 (2020/01/20)
523        //                              final Future<Connection> future = executor.submit(
524        //                                      new Callable<Connection>() {
525        //                                              @Override
526        //                                              public Connection call() throws SQLException {
527        //                                                      return DriverManager.getConnection( edbid.getUrl(), prop );
528        //                                              }
529        //                                      }
530        //                              );
531        //                              try {
532        //                                      conn = future.get( TIMEOUT, TimeUnit.SECONDS );
533        //                              }
534        //                              finally {
535        //                                      executor.shutdown();                    // 正常なシャットダウン
536        //                              }
537        //                      }
538        //                      else {
539                                        DriverManager.setLoginTimeout( TIMEOUT );                                                                       // 7.1.0.0 (2020/01/20)
540                                        conn = DriverManager.getConnection( edbid.getUrl(), prop );
541        //                      }
542
543//                              conn = DriverManager.getConnection( edbid.getUrl(), prop );
544                //              conn.setReadOnly( true );
545                                conn.setReadOnly( edbid.isReadonly() );
546
547                                conn.setAutoCommit( false );
548                                conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);  // 初期値
549                //              ((OracleConnection)conn).setDefaultExecuteBatch(1);  // 初期値
550                //              ((OracleConnection)conn).setDefaultRowPrefetch(20);  // 初期値
551
552                                // 3.5.2.0 (2003/10/20) 接続情報に、データベース名、ドライバ名情報を追加する。
553                                // 4.0.0.0 (2007/10/26) 登録先をオブジェクト化
554                                if( edbid.getDbProductName() == null ) {
555                                        final DatabaseMetaData meta = conn.getMetaData();
556                                        edbid.setMetaDataInfo( meta );
557                                }
558                                return conn ;
559                        }
560                        catch( final SQLException ex ) {
561                                final String errMsg = "コネクトすることが出来ません。" + CR
562                                                                + "DBID=[" + edbid.getDbidKey() + "]"   + CR
563                                                                + "URL=[" + edbid.getUrl() + "]"                + CR // 5.11.0.0 (2021/05/17)
564                                                                + ex.getMessage() + " , status=" + ex.getSQLState();
565                                Closer.connClose( conn );
566                                throw new OgRuntimeException( errMsg,ex );              // 3.5.5.4 (2004/04/15) 引数の並び順変更
567                        }
568        //              catch( final InterruptedException | TimeoutException | CancellationException | ExecutionException ex ) {
569        //                      final String errMsg = "コネクト時にタイムアウトが発生しました。"    + CR
570        //                                                      + "DBID=[" + edbid.getDbidKey() + "]" ;
571        //                      Closer.connClose( conn );
572        //                      throw new OgRuntimeException( errMsg,ex );
573        //              }
574                }
575
576                /**
577                 * アクセスログ取得の為のDBMS_APPLICATION_INFOの使用可否を取得します(初期値:true)。
578                 *
579                 * データベースへのアクセスログ取得の為、エンジンで接続するコネクションについて
580                 * DBMS_APPLICATION_INFO.SET_CLIENT_INFO と SET_MODULE を呼び出しています。
581                 * この処理は、ORACLEとの接続のみ有効ですので、接続先データベースが ORACLE 以外は
582                 * false を返します。
583                 * ORACLE の場合は、システムリソースの USE_DB_APPLICATION_INFO 属性の設定値を
584                 * 返します。
585                 * この設定値の初期値は、true です。
586                 *
587                 * @og.rev 3.8.8.2 (2007/01/26) 新規追加
588                 *
589                 * @return  true:使用する/false:使用しない
590                 */
591                public boolean useApplicationInfo() {
592                        return edbid.isApplicationInfo();
593                }
594
595                /**
596                 * 接続先のDB名を返します。
597                 *
598                 * @og.rev 4.3.7.0 (2009/06/01) 新規作成
599                 *
600                 * @return  接続先のDB名
601                 */
602                public String getDBName() {
603                        return edbid.getDbProductName();
604                }
605
606                /**
607                 * 接続先のDBバージョンを返します。
608                 *
609                 * @og.rev 4.3.7.0 (2009/06/01) 新規作成
610                 *
611                 * @return 接続先のDBバージョン
612                 */
613                public String getDBVersion() {
614                        return edbid.getDbProductVersion();
615                }
616
617                /**
618                 * 接続先の簡易な内部情報を返します。
619                 *
620                 * @og.rev 5.3.4.0 (2011/04/01) toString() の簡易版
621                 *
622                 * @return 接続先の簡易な内部情報
623                 */
624                public String dbidInfo() {
625                        return edbid.info();
626                }
627
628                /**
629                 * 内部状況を簡易的に表現した文字列を返します。
630                 * DBID/URL/USER/プールサイズ を返します。
631                 *
632                 * @og.rev 3.5.2.0 (2003/10/20) 接続情報に、データベース名、ドライバ名情報を追加する。
633                 * @og.rev 3.5.6.6 (2004/08/23) 同期化方法を統一する為、synchronized をつけます。(別途 要検討)
634                 * @og.rev 4.0.0.0 (2007/10/29) EDbidのtoStringを呼ぶように変更
635                 *
636                 * @return   このオブジェクトプールの文字列表現
637                 * @og.rtnNotNull
638                 */
639                @Override
640                public String toString() {
641                        return edbid.toString() + super.toString() ;
642                }
643
644//              /**
645//               * すべての要素を オブジェクトプールから削除します。
646//               * 貸し出し中のオブジェクトは、クリアしません。よって、返り値は、
647//               * すべてのオブジェクトをクリアできた場合は、true 、貸し出し中の
648//               * オブジェクトが存在した場合(クリアできなかった場合)は、false です。
649//               *
650//               * @og.rev 6.3.9.0 (2015/11/06) Use block level rather than method level synchronization.(PMD)
651//               * @og.rev 7.1.0.0 (2020/01/20) Connection 作成時のタイムアウトを用意します。
652//               *
653//               * @return すべてクリア(true)/貸し出し中のオブジェクトが残っている(false)
654//               */
655//              @Override
656//              public boolean clear() {
657//                      executor.shutdownNow();
658//                      return super.clear();
659//              }
660        }
661}