    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 java.io.File;
    019import java.net.URL;
    020import java.util.ArrayList;
    021import java.util.LinkedHashMap;
    022import java.util.List;
    023import java.util.Locale;
    024import java.util.Map;
    025import java.util.Collections;
    026
    027import org.opengion.fukurou.util.StringUtil;
    028import org.opengion.fukurou.util.FileUtil;
    029import org.opengion.fukurou.util.HybsDateUtil;                          // 6.9.2.1 (2018/03/12)
    030import org.opengion.fukurou.system.OgRuntimeException ;         // 6.4.2.0 (2016/01/29)
    031import org.opengion.fukurou.system.LogWriter;
    032import org.opengion.fukurou.system.HybsConst;                           // 7.2.3.1 (2020/04/17)
    033import org.opengion.fukurou.xml.DomParser;
    034import org.w3c.dom.Document;
    035import org.w3c.dom.Element;
    036import org.w3c.dom.Node;
    037import org.w3c.dom.NodeList;
    038import org.w3c.dom.NamedNodeMap;                                                        // 6.9.2.0 (2018/03/05)
    039
    040import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;      // 6.1.0.0 (2014/12/26) refactoring
    041
    042/**
    043 * DB設定XMLの内容をJAXBを利用してロードする
    044 * Driverをロードする
    045 * 上記２つの機能を備えたクラスです
    046 *
    047 * 外部からはgetDbidメソッドを利用してDB設定(ExpandedDbid型)を取得します。
    048 * DB設定情報が無い場合にXMLを読みにいきます。
    049 * このDBIDを決めるキーは、内部取り込み字に、大文字変換されますので、大文字・
    050 * 小文字の区別はありません。
    051 *
    052 * @og.rev 4.0.0.0 (2007/10/25) 新規作成
    053 * @og.rev 5.1.7.0 (2010/06/01) org.opengion.fukurou.xml.jaxb.dbid 関係 廃止
    054 * @og.group 初期化
    055 *
    056 * @version  4.0
    057 * @author 高橋正和
    058 * @since   JDK6.0,
    059 */
    060public class DatabaseConfig {
    061
    062        // fukurou内で完結させるため、HybsDataからは読み込まずにここに書く
    063        private static final String DEFAULT_DRIVER       = "oracle.jdbc.OracleDriver";
    064
    065        // 6.4.3.3 (2016/03/04) 初期 DBConfig.xml ファイルの設定。
    066        /** 初期ファイルの設定 {@value} */
    067        public static final String DB_CONFIG_FILE = "../DBConfig.xml" ;
    068
    069        // XMLファイル関連
    070        private final String xmlFilename;                               // 5.7.2.2 (2014/01/24)
    071
    072        /** 6.4.3.1 (2016/02/12) Collections.synchronizedMap で同期処理を行います。  */
    073        private final Map<String, EDbid> dbidMap = Collections.synchronizedMap( new LinkedHashMap<>() );        // 5.6.7.0 (2013/07/27)
    074        /** 6.4.3.1 (2016/02/12) Collections.synchronizedList で同期処理を行います。  */
    075        private final List<String>    driverList = Collections.synchronizedList( new ArrayList<>() );
    076
    077        // 5.6.7.0 (2013/07/27) プルダウンメニュー用の情報の、キャッシュ用変数。
    078        private String codeKeyVal ;                                             // 初めて要求されたときに、セットします。
    079
    080        // 5.6.6.0 (2013/07/05) 表題(title)属性を追加
    081        private static final String[] DBID_INFO_KEYS
    082                                = { "dbidKey", "title", "url", "user", "password", "readonly"
    083                                        , "mincount", "maxcount", "pooltime", "applicationInfo","property" };
    084
    085        /* DBDRIVERのキーのを管理します。5.1.9.0 (2010/08/01) */
    086        private static final String DBDRIVER_CLASS_KEY = "class";
    087
    088        /**
    089         * 初期値を使ってXMLを読み込む
    090         * xmlFilenameの初期値は../DBConfig.xml
    091         *
    092         * @og.rev 4.3.1.1 (2008/08/23) 自分のコンストラクターを呼ぶように修正
    093         */
    094        public DatabaseConfig() {
    095                this( DB_CONFIG_FILE );
    096        }
    097
    098        /**
    099         * XMLファイルの名前を指定して読み込む
    100         *
    101         * @og.rev 5.1.9.0 (2010/08/01) クラスローダー外からでもDBConfig.xmlを取得できるようにする
    102         * @og.rev 5.6.7.0 (2013/07/27) オブジェクト作成時に初期化も行っておきます。
    103         * @og.rev 5.6.8.2 (2013/09/20) Tomcat8で、クラスローダーが変更されているのでその対応
    104         * @og.rev 5.7.2.2 (2014/01/24) WEB-INF/classes フォルダがないと、xmlURL がnull になる対応。
    105         * @og.rev 5.7.2.3 (2014/01/31) ファイルの存在チェックを追加します。
    106         * @og.rev 6.4.3.3 (2016/03/04) 初期 DBConfig.xml ファイルの設定。
    107         * @og.rev 6.6.0.0 (2016/12/01) コンテキストパスから、##バージョン番号を取り去った値を返すようにします。
    108         * @og.rev 6.8.5.1 (2018/01/15) ClassLoader変数名を、clsl から、loader に変更(他の変数名と整合性を持たす)。
    109         *
    110         * @param       infile  XMLファイルの名前
    111         */
    112        public DatabaseConfig( final String infile ) {
    113                final String xmlfile = infile == null || infile.isEmpty() ? DB_CONFIG_FILE : infile ;           // 引数が無い場合の初期設定を行います。
    114                String fileName = null;
    115
    116                // 6.3.9.1 (2015/11/27) In J2EE, getClassLoader() might not work as expected.  Use Thread.currentThread().getContextClassLoader() instead.(PMD)
    117                final ClassLoader loader        = Thread.currentThread().getContextClassLoader();
    118                URL xmlURL                                      = loader.getResource( xmlfile );
    119
    120                // 5.6.8.2 (2013/09/20) Tomcat8で、xmlURL が取得できなくなっている・・・ようだ。
    121                // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..;
    122                if( xmlURL == null ) {
    123                        xmlURL = loader.getResource( "/" );             // クラスパスのベースURL
    124                        // 5.7.2.2 (2014/01/24) Tomcat7で、WEB-INF/classes フォルダがないと、xmlURL がnull になる。
    125                        if( xmlURL != null ) {
    126                                final File temp = new File( xmlURL.getPath() , xmlfile );
    127                                if( temp.exists() ) { fileName = temp.getAbsolutePath(); }
    128                        }
    129                }
    130                else {
    131                        fileName = xmlURL.getFile().replaceAll( "%23%23","##" );                // 6.6.0.0 (2016/12/01)
    132                        // 5.7.2.3 (2014/01/31) ファイルの存在チェックを追加します。
    133                        if( ! new File( fileName ).exists() ) { fileName = null; }
    134                }
    135
    136                // 5.1.9.0 (2010/08/01)  クラスローダー外からでもDBConfig.xmlを取得できるようにする
    137                if( fileName == null && new File( xmlfile ).exists() ) {
    138                        fileName = xmlfile;
    139                }
    140
    141                if( fileName == null ) {
    142                        // 5.5.7.2 (2012/10/09) コメント追加
    143                        final String errMsg = "DBConfig.xmlが見つかりません。File=[" + xmlfile + "]\n"
    144                                                                + " WEB-INF/classes フォルダがないと、相対パスで見つけることができません。" ;
    145                        throw new OgRuntimeException( errMsg );
    146                }
    147
    148                xmlFilename = fileName;
    149
    150                init();                 // 5.6.7.0 (2013/07/27)
    151        }
    152
    153        /**
    154         * dbidKeyをキーにしてExpandedDbid型でマップの内容を返す。
    155         * 存在しない場合はNULLを返します。
    156         * キーが無い場合に初期化を行う。
    157         *
    158         * @og.rev 4.0.0.1 (2007/12/04) EDbid#clone() 廃止
    159         * @og.rev 5.6.7.0 (2013/07/27) synchronized メソッドにします。
    160         * @og.rev 6.0.0.1 (2014/04/25) Collections.synchronizedMap を使用します。
    161         *
    162         * @param key XMLで登録したdbidKey
    163         *
    164         * @return EDbid型オブジェクト
    165         */
    166        public EDbid getDbid( final String key ) {
    167                return dbidMap.get( key.toUpperCase( Locale.JAPAN ) ) ;
    168        }
    169
    170        /**
    171         * マップをクリアします。
    172         * XMLファイルを再読み込みする場合に使用します。
    173         *
    174         * @og.rev 5.1.9.0 (2010/08/01) ドライバーのリストもクリアする。
    175         * @og.rev 5.6.7.0 (2013/07/27) synchronized メソッドにします。
    176         * @og.rev 6.0.0.1 (2014/04/25) Collections.synchronizedMap を使用します。
    177         */
    178        public void reload() {
    179                dbidMap.clear();
    180                driverList.clear();
    181                init();
    182        }
    183
    184        /**
    185         * 初期化処理
    186         *
    187         * DB設定XMLファイル(DBConfig.xml)を読み込みます。
    188         * このファイルから、ドライバーリストの取得、DBIDのオブジェクトマップの作成を
    189         * 行います。
    190         * EDbidオブジェクト は、環境変数で、共通の初期値を定義しておくことが可能です。
    191         * 項目として、REALM_URL、REALM_NAME、REALM_PASSWORD が設定可能です。
    192         *
    193         * ドライバーリストの取得後、Class.forName で、ドライバの登録も行います。
    194         *
    195         * @og.rev 5.1.7.0 (2010/06/01) org.opengion.fukurou.xml.jaxb.dbid 関係 廃止
    196         * @og.rev 5.6.7.0 (2013/07/27) dbidMap,driverList を書き込むのではなく、作成します。
    197         * @og.rev 7.2.3.1 (2020/04/17) System.getenv → HybsConst.getenv 変更(サービス化対応)
    198         */
    199        private void init() {
    200                final Document doc = DomParser.read( new File(xmlFilename) ) ;
    201                final Element firstRoot = doc.getDocumentElement();
    202
    203                makeDriverList( firstRoot );                            // 5.6.7.0 (2013/07/27)
    204
    205                // 5.6.7.0 (2013/07/27) を、かけておきます。
    206                synchronized ( this ) {
    207                        for( final String dr : driverList ) {
    208                                try {
    209                                        Class.forName( dr );
    210//                              } catch( final ClassNotFoundException ex ) {            // 7.0.0.1 (2018/10/09) LinkageErrorが発生したので、いっそすべてをcatchします。
    211                                } catch( final Throwable th ) {
    212                                        final String errMsg = "ドライバクラスが見つかりません。[" + dr + "]" ;
    213                                        LogWriter.log( errMsg );
    214                                        LogWriter.log( th );
    215                                }
    216                        }
    217                }
    218
    219                final EDbid defDdbid = new EDbid();             // 初期値
    220//              defDdbid.setUrl(                System.getenv( "REALM_URL" ) );
    221//              defDdbid.setUser(               System.getenv( "REALM_NAME" ) );
    222//              defDdbid.setPassword(   System.getenv( "REALM_PASSWORD" ) );
    223                defDdbid.setUrl(                HybsConst.getenv( "REALM_URL"      ) ); // 7.2.3.1 (2020/04/17)
    224                defDdbid.setUser(               HybsConst.getenv( "REALM_NAME"     ) ); // 7.2.3.1 (2020/04/17)
    225                defDdbid.setPassword(   HybsConst.getenv( "REALM_PASSWORD" ) ); // 7.2.3.1 (2020/04/17)
    226
    227                makeDbidMap( firstRoot,defDdbid );                              // 5.6.7.0 (2013/07/27)
    228        }
    229
    230        /**
    231         * ドライバーリストを取得します。
    232         *
    233         * DB設定XMLファイル(DBConfig.xml)の、class タグを取り込みます。
    234         * このファイルから、ドライバーリストを取得します。
    235         *
    236         * 内部的に３段階の処理が実行されます。
    237         * 第１Step:DBConfig.xml から、ドライバーリストを取得
    238         * 第２Step:ドライバーリストが存在しない場合、環境変数の REALM_DRIVER からドライバーを取得
    239         * 第３Step:それでも存在しない場合、このクラスの DEFAULT_DRIVER 定数 からドライバーを取得
    240         *
    241         * @og.rev 5.1.7.0 (2010/06/01) org.opengion.fukurou.xml.jaxb.dbid 関係 廃止
    242         * @og.rev 5.1.9.0 (2010/08/01) ドライバ一覧のListをオブジェクト変数化
    243         * @og.rev 5.6.7.0 (2013/07/27) driverList を書き込むのではなく、作成します。
    244         * @og.rev 5.6.7.0 (2013/07/27) synchronized メソッドにします。
    245         * @og.rev 7.2.3.1 (2020/04/17) System.getenv → HybsConst.getenv 変更(サービス化対応)
    246         *
    247         * @param       element DB設定XMLファイルのElementオブジェクト
    248         */
    249        private void makeDriverList( final Element element ) {
    250
    251                final NodeList list = element.getElementsByTagName( "class" ) ;
    252                final int num = list.getLength();
    253                for( int i=0; i<num; i++ ) {
    254                        final Element cls = (Element)list.item(i);
    255                        driverList.add( cls.getTextContent() );
    256                }
    257
    258                if( driverList.isEmpty() ) {
    259//                      final String realmDriver = System.getenv( "REALM_DRIVER" );
    260                        final String realmDriver = HybsConst.getenv( "REALM_DRIVER" );  // 7.2.3.1 (2020/04/17)
    261                        if( realmDriver != null && realmDriver.length() > 0 ) {
    262                                driverList.add( realmDriver );
    263                        }
    264                }
    265
    266                if( driverList.isEmpty() ) { driverList.add( DEFAULT_DRIVER ); }
    267        }
    268
    269        /**
    270         * EDbidオブジェクトのマップを取得します。
    271         *
    272         * DB設定XMLファイル(DBConfig.xml)の、dbid タグを取り込みます。
    273         * このファイルから、EDbidオブジェクトの属性情報を取得し、オブジェクトを構築します。
    274         *
    275         * EDbidオブジェクト は、初期値をコピーして、作成していきます。
    276         * EDbidオブジェクトをマップから取り出すキーとなる、dbidKey は、大文字化して設定します。
    277         *
    278         * 7.0.5.0 (2019/09/09)
    279         *  begin,end,step,var,items 属性に対して、stepを年月指定できるように、type属性を追加しました。
    280         *  type="month" で、begin,end を、年月として処理します。
    281         *
    282         * @og.rev 5.1.7.0 (2010/06/01) org.opengion.fukurou.xml.jaxb.dbid 関係 廃止
    283         * @og.rev 5.1.9.0 (2010/08/01) Mapを返すように変更
    284         * @og.rev 5.5.2.0 (2012/05/01) property追加
    285         * @og.rev 5.6.6.0 (2013/07/05) 表題(title)属性の取得
    286         * @og.rev 5.6.7.0 (2013/07/27) 内部MapをDBConfig.xmlの読み込み順に変更。
    287         * @og.rev 5.6.7.0 (2013/07/27) dbidMap を書き込むのではなく、作成します。
    288         * @og.rev 5.6.7.0 (2013/07/27) synchronized メソッドにします。
    289         * @og.rev 5.6.7.1 (2013/08/09) DEFAULT と、RESOURCE の DBIDキーがなければ、内部的に作成します。
    290         * @og.rev 5.6.8.0 (2013/09/06) RESOURCE の DBIDキーを内部作成時に、title も設定します。
    291         * @og.rev 6.4.3.4 (2016/03/11) computeIfAbsent と、新しい clone(String) メソッドを使用する。
    292         * @og.rev 6.8.2.4 (2017/11/20) 環境変数( ${env.XXXX} ) を処理できるようにします。
    293         * @og.rev 6.9.2.0 (2018/03/05) dbidに、begin,end,step,var,items 属性を追加します(影響は、ForEachObject内)。
    294         * @og.rev 7.0.5.0 (2019/09/09) dbidに、type 属性を追加します(影響は、ForEachObject内)。
    295         *
    296         * @param  element DB設定XMLファイルのElementオブジェクト
    297         * @param  defDdbid 初期情報の設定された、EDbidオブジェクト
    298         */
    299        private void makeDbidMap( final Element element , final EDbid defDdbid ) {
    300                final NodeList list = element.getElementsByTagName( "dbid" ) ;
    301                final int num = list.getLength();
    302                for( int i=0; i<num; i++ ) {
    303                        final Element ele = (Element)list.item(i);
    304                        final ForEachObject forEach = new ForEachObject( ele );
    305
    306                        while( forEach.hasNext() ) {
    307                                final NodeList childs   = ele.getChildNodes();
    308                                final int      numChild = childs.getLength();
    309                                final EDbid    dbid     = defDdbid.clone();                             // 初期値をコピーして、作成
    310                                for( int j=0; j<numChild; j++ ) {
    311                                        final Node nd = childs.item(j);
    312                                        if( nd.getNodeType() == Node.ELEMENT_NODE ) {
    313                                                final Element el = (Element)nd;
    314                                                final String tag = el.getTagName();
    315//                                              // 6.8.2.4 (2017/11/20) 環境変数( ${env.XXXX} ) を処理できるようにします。
    316//                                              final String txt = envText( el.getTextContent() );
    317                                                // 6.9.2.0 (2018/03/05) 変数の置き換え( ${var} )と、環境変数( ${env.XXXX} ) を処理します。
    318                                                final String txt = forEach.getText( el.getTextContent() );
    319
    320                                                // dbidKey は、toUpperCase して、大文字のみとする。
    321                                                // 6.8.2.4 (2017/11/20) 環境変数( ${env.XXXX} ) を処理できるようにします。
    322                                                if( "dbidKey".equals( tag ) )   {
    323                                                        if( txt != null && txt.length() > 0 ) {
    324                                                                dbid.setDbidKey( txt.toUpperCase( Locale.JAPAN ) );
    325                                                        }
    326                                                }
    327                                                else if( "title".equals( tag ) )        { dbid.setTitle(        txt ); }                // 5.6.6.0 (2013/07/05) 表題(title)属性の取得
    328                                                else if( "url".equals( tag ) )          { dbid.setUrl(          txt ); }
    329                                                else if( "user".equals( tag ) )         { dbid.setUser(         txt ); }
    330                                                else if( "password".equals( tag ) ) { dbid.setPassword( txt ); }
    331                                                else if( "readonly".equals( tag ) ) { dbid.setReadonly( txt ); }
    332                                                else if( "mincount".equals( tag ) ) { dbid.setMincount( txt ); }
    333                                                else if( "maxcount".equals( tag ) ) { dbid.setMaxcount( txt ); }
    334                                                else if( "pooltime".equals( tag ) ) { dbid.setPooltime( txt ); }
    335                                                else if( "applicationInfo".equals( tag ) ) { dbid.setApplicationInfo( txt ); }
    336                                                else if( "property".equals( tag ) ) { dbid.addProp(             txt ); } // 5.5.2.0 (2012/05/01)
    337                                                else {
    338                                                        System.err.println( "警告：dbid に新しい属性が、追加されています。" );
    339                                                }
    340                                        }
    341                                }
    342                                dbidMap.put( dbid.getDbidKey(), dbid );         // 5.6.7.0 (2013/07/27) 復活
    343                        }
    344                }
    345
    346                // 5.6.7.1 (2013/08/09) DEFAULT と、RESOURCE の DBIDキーがなければ、内部的に作成します。
    347                // 6.4.3.4 (2016/03/11) computeIfAbsent と、新しい clone(String) メソッドを使用する。
    348                // Map#computeIfAbsent ： 戻り値は、既存の、または計算された値。追加有り、置換なし、削除なし
    349                dbidMap.computeIfAbsent( "DEFAULT", k -> defDdbid.clone( "DEFAULT" ) ); // DEFAULT が存在するか確認する。
    350
    351                // 6.4.3.4 (2016/03/11) computeIfAbsent と、新しい clone(String) メソッドを使用する。
    352                // Map#computeIfAbsent ： 戻り値は、既存の、または計算された値。追加有り、置換なし、削除なし
    353                dbidMap.computeIfAbsent( "RESOURCE", k -> defDdbid.clone( "RESOURCE" ) );       // RESOURCE が存在するか確認する。
    354        }
    355
    356//      /**
    357//       * 環境変数( ${env.XXX} )を含むテキストを処理します。
    358//       *
    359//       * 環境変数を含まない場合は、引数の orgTxt を返します。
    360//       *
    361//       * @og.rev 6.8.2.4 (2017/11/20) 環境変数( ${env.XXXX} ) を処理できるようにします。
    362//       *
    363//       * @param  orgTxt 環境変数( ${env.XXX} )を含む可能性のあるテキスト
    364//       * @return  環境変数を処理したテキスト
    365//       */
    366//      private String envText( final String orgTxt ) {
    367//              final int st = orgTxt.indexOf( "${env." );
    368//              if( st >= 0 ) {
    369//                      final int ed = orgTxt.indexOf( '}' , st );
    370//                      if( ed >= 0 ) {
    371//                              final String envKey = orgTxt.substring( st + "${env.".length() , ed );
    372//                              final String envTxt = orgTxt.substring( 0,st ) + System.getenv( envKey ) + orgTxt.substring( ed+1 );
    373//                              return envText( envTxt );                       // ループすべきだが、再帰処理で対応します。
    374//                      }
    375//                      else {
    376//                              final String errMsg = xmlFilename + " の環境変数( ${env.XXX} )の整合性が取れていません。" + orgTxt ;
    377//                              throw new OgRuntimeException( errMsg );
    378//                      }
    379//              }
    380//
    381//              return orgTxt;
    382//      }
    383
    384        /**
    385         * dbidの属性処理が、煩雑になってきたので、内部クラスにまとめます。
    386         *
    387         * begin,end,step,var,items 属性で、ループ処理を行うための設定を取得します。
    388         * items が未指定の場合は、begin,end,step による、数字(文字列)の配列を返します。
    389         * itemsが、定義されている場合は、ｶﾝﾏで分割後、文字列の配列を返します。
    390         * それぞれ、初期値があり、begin=0 , end=0 or items要素数,step=1,
    391         *
    392         * @og.rev 7.0.5.0 (2019/09/09) type属性を追加。
    393         *  begin,end,step,var,items 属性に対して、stepを年月指定できるように、type属性を追加しました。
    394         *  type="month" で、begin,end を、年月として処理します。
    395         *
    396         * @og.rev 6.9.2.1 (2018/03/12) dbidに、begin,end,step,var,items 属性を追加します。
    397         * @og.rev 7.0.5.0 (2019/09/09) dbidに、type 属性を追加します。
    398         */
    399        private static final class ForEachObject {
    400                private final String   attVar ;                         // var 属性の値を、${xxxx} に置き換えた文字列。未定義は、null
    401                private final String[] items ;                          // items 属性のCSV分割か、begin,end,step の数字文字列の配列
    402
    403                private int   idx      = -1;                            // hasNext() で、先にアップするので、一つ引いておきます。
    404
    405                /**
    406                 * Elementオブジェクトを指定して、インスタンスを生成するコンストラクタです。
    407                 *
    408                 * @og.rev 6.9.2.1 (2018/03/12) dbidに、begin,end,step,var,items 属性を追加します。
    409                 * @og.rev 7.0.5.0 (2019/09/09) dbidに、type 属性を追加します。
    410                 *
    411                 * @param  element DB設定XMLファイルのElementオブジェクト
    412                 */
    413                public ForEachObject( final Element ele ) {
    414                        // 6.9.2.0 (2018/03/05) dbidに、begin,end,step,var 属性を追加します。
    415                        if( ele.hasAttributes() ) {
    416                                final NamedNodeMap attNode = ele.getAttributes();
    417//                              final int attBgn = StringUtil.nval( getAttri( attNode,"begin" ),0 );    // 7.0.5.0 (2019/09/09) 削除
    418//                              final int attStp = StringUtil.nval( getAttri( attNode,"step"  ),1 );    // 7.0.5.0 (2019/09/09) (ifの中へ)移動
    419                                final String tmpVar = getAttri( attNode,"var" );
    420                                attVar = StringUtil.isNull( tmpVar ) ? null : "${" + tmpVar + "}" ;             // 置換元定数を作成します。
    421
    422//                              final String endStr = getAttri( attNode,"end" );                                                // 7.0.5.0 (2019/09/09) (ifの中へ)移動
    423                                final String itms   = getAttri( attNode,"items" );
    424                                if( StringUtil.isNull( itms ) ) {
    425                                        final String bgiStr = getAttri( attNode,"begin" );                                      // 7.0.5.0 (2019/09/09) 追加
    426                                        final String endStr = getAttri( attNode,"end" );                                        // 7.0.5.0 (2019/09/09) (ifの中へ)移動
    427                                        final int attStp = StringUtil.nval( getAttri( attNode,"step"  ),1 );
    428
    429                                        final String typStr = getAttri( attNode,"type" );                                       // 7.0.5.0 (2019/09/09) 新規追加
    430                                        if( "month".equalsIgnoreCase( typStr ) ) {                                                      // 7.0.5.0 (2019/09/09) 新規追加
    431                                                items = HybsDateUtil.stepYM( bgiStr,endStr,attStp );
    432                                        }
    433                                        else {
    434                                                final int attBgn = StringUtil.nval( bgiStr,0 );                                 // 7.0.5.0 (2019/09/09) 文字列化をint化
    435
    436                                                final int attEnd = StringUtil.nval( endStr,attBgn+1 );                  // とりあえず １回は回すため。
    437                                                final List<String> list = new ArrayList<>();
    438                                                for( int i=attBgn; i<attEnd; i+=attStp ) {
    439                                                        list.add( String.valueOf( i ) );
    440                                                }
    441
    442                                                items = list.toArray( new String[list.size()] ) ;
    443                                        }
    444                                }
    445                                else {
    446                                        items = itms.split( "," );
    447                                }
    448                        }
    449                        else {
    450                                attVar = null;
    451                                items  = new String[] { "0" };                  // 最低１回は回せるように。
    452                        }
    453                }
    454
    455                /**
    456                 * 反復処理でさらに要素がある場合にtrueを返します。
    457                 *
    458                 * この判定で、内部カウンタを、プラスしていますので、
    459                 * ループ判定時にのみ、呼び出してください。
    460                 *
    461                 * @og.rev 6.9.2.1 (2018/03/12) 新規追加
    462                 *
    463                 * @return 反復処理でさらに要素がある場合はtrue
    464                 */
    465                public boolean hasNext() { return ++idx < items.length; }
    466
    467                /**
    468                 * 引数の文字列に対して、var 属性と item要素の置換を行います。
    469                 *
    470                 * さらに、${env.XXXX} 環境変数の置換と、日付文字列{&#064;DATE.XXXX}の置換も
    471                 * 行います。
    472                 * この処理では、内部カウンタはアップしませんので、inTxt を替えて、何度でも
    473                 * 呼び出すことが可能です。
    474                 *
    475                 * @og.rev 6.9.2.1 (2018/03/12) 新規追加
    476                 * @og.rev 7.2.5.0 (2020/06/01) ${env.XXXX} 環境変数 は、{&#064;ENV.XXXX} も対応します。
    477                 *
    478                 * @param  inTxt 置換元の文字列
    479                 * @return items属性に対応して置換された、文字列
    480                 */
    481                public String getText( final String inTxt ) {
    482                        String rtnTxt = StringUtil.replace( inTxt , attVar , items[idx] );              // attVar が null の場合、inTxt が戻される。
    483                        rtnTxt = StringUtil.replaceText( rtnTxt , "${env."  , "}" , System::getenv );                           // 環境変数置換
    484                        rtnTxt = StringUtil.replaceText( rtnTxt , "{@ENV."  , "}" , System::getenv );                           // 7.2.5.0 (2020/06/01) 環境変数置換
    485                        rtnTxt = StringUtil.replaceText( rtnTxt , "{@DATE." , "}" , HybsDateUtil::getDateFormat );      // 日付文字列置換
    486
    487                        return rtnTxt;
    488                }
    489
    490                /**
    491                 * 属性を取得する簡易メソッドです。
    492                 *
    493                 * 引数に、ノードのコレクションと、属性のキーワードを指定することで、属性値を返します。
    494                 *
    495                 * @og.rev 6.9.2.0 (2018/03/05) 新規追加
    496                 * @og.rev 7.2.5.0 (2020/06/01) ${env.XXXX} 環境変数 は、{&#064;ENV.XXXX} も対応します。
    497                 *
    498                 * @param  nodeMap 名前を指定してアクセスできるノードのコレクション
    499                 * @param  key    属性のキーワード
    500                 * @return 属性値(キーに対応する属性が無い場合は、null)
    501                 */
    502                private String getAttri( final NamedNodeMap nodeMap,final String key ) {
    503                        final Node attNode = nodeMap.getNamedItem( key );
    504
    505//                      return attNode == null ? null : attNode.getNodeValue() ;
    506
    507                        String rtnTxt = attNode == null ? null : attNode.getNodeValue() ;
    508
    509                        rtnTxt = StringUtil.replaceText( rtnTxt , "${env."  , "}" , System::getenv );                           // 環境変数置換
    510                        rtnTxt = StringUtil.replaceText( rtnTxt , "{@ENV."  , "}" , System::getenv );                           // 7.2.5.0 (2020/06/01) 環境変数置換
    511                        rtnTxt = StringUtil.replaceText( rtnTxt , "{@DATE." , "}" , HybsDateUtil::getDateFormat );      // 日付文字列置換
    512
    513                        return rtnTxt;
    514                }
    515        }
    516
    517        /* ------------------------------------------------------------------------------------
    518         *
    519         * 以下は、DBConfig.xml編集用のメソッドです。
    520         * 編集用のメソッドでは、オブジェクト化されたDBID及びDBDRIVERの情報は使用せずに、
    521         * DBConfig.xmlからその情報を再度読み出して、SET/GETしています。
    522         * (オブジェクトとして依存しているのは、DBConfig.xmlのファイル名のみです)
    523         *
    524         * -------------------------------------------------------------------------------------
    525         */
    526        /**
    527         * DBIDとして管理している項目のキーの一覧を配列形式で返します。
    528         *
    529         * @og.rev 5.1.9.0 (2010/08/01) 新規作成
    530         *
    531         * @return 項目のキー一覧
    532         * @og.rtnNotNull
    533         */
    534        public static String[] getDbidInfoKeys() {
    535                return DBID_INFO_KEYS.clone();
    536        }
    537
    538        /**
    539         * 全てのDBIDの属性情報のリスト(配列)で返します。
    540         *
    541         * 値の順番については、{@link #getDbidInfoKeys()}で返されるキーの一覧と同じです。
    542         *
    543         * @og.rev 5.1.9.0 (2010/08/01) 新規作成
    544         * @og.rev 5.5.2.1 (2012/05/07) propertiesを出力
    545         * @og.rev 5.6.6.0 (2013/07/05) 表題(title)属性を追加
    546         * @og.rev 5.6.7.0 (2013/07/27) 内部MapをDBConfig.xmlの読み込み順に変更。
    547         * @og.rev 6.3.9.0 (2015/11/06) Use block level rather than method level synchronization.(PMD)
    548         *
    549         * @return 全てのDBIDの属性情報のリスト(配列)
    550         * @og.rtnNotNull
    551         * @see #getDbidInfoKeys()
    552         */
    553        public String[][] getDbidInfo() {
    554                // 6.3.9.0 (2015/11/06) 色々やりたいが、今はsynchronizedブロックにするだけにします。
    555                synchronized( dbidMap ) {
    556                        String[][] dbidInfo = new String[dbidMap.size()][DBID_INFO_KEYS.length];
    557                        int idx = 0;
    558                        for( final EDbid dbid : dbidMap.values() ) {
    559                                dbidInfo[idx][0] = dbid.getDbidKey();
    560                                dbidInfo[idx][1] = dbid.getTitle();                             // 5.6.6.0 (2013/07/05) 表題(title)属性を追加
    561                                dbidInfo[idx][2] = dbid.getUrl();
    562                                dbidInfo[idx][3] = dbid.getUser();
    563                                dbidInfo[idx][4] = dbid.getPassword();
    564                                dbidInfo[idx][5] = String.valueOf( dbid.isReadonly() );
    565                                dbidInfo[idx][6] = String.valueOf( dbid.getMincount() );
    566                                dbidInfo[idx][7] = String.valueOf( dbid.getMaxcount() );
    567                                dbidInfo[idx][8] = String.valueOf( dbid.getPooltime() );
    568                                dbidInfo[idx][9] = String.valueOf( dbid.isApplicationInfo() );
    569                                dbidInfo[idx][10]= String.valueOf( dbid.getProps().toString() ); // 5.5.2.1 (2012/05/07)
    570                                idx++;
    571                        }
    572
    573                        return dbidInfo;
    574                }
    575        }
    576
    577        /**
    578         * 全てのDBIDの属性情報のリスト(配列)をセットします。
    579         *
    580         * このメソッドを呼び出すと、DBConfig.xmlで定義されているDBID情報一覧を"一旦削除し"、
    581         * その上で、引数のDBID情報一覧をDBConfig.xmlに書き込みます。
    582         *
    583         * 値の順番については、{@link #getDbidInfoKeys()}で返されるキーの一覧と同じです。
    584         *
    585         * 書き込みの直前に、同じフォルダにタイムスタンプを付加したバックアップファイルを作成します。
    586         *
    587         * @og.rev 5.1.9.0 (2010/08/01) 新規作成
    588         * @og.rev 5.6.7.0 (2013/07/27) 内部MapをDBConfig.xmlの読み込み順に変更。
    589         *
    590         * @param dbidVals 全てのDBIDの属性情報の配列の配列
    591         * @see #getDbidInfoKeys()
    592         */
    593        public void setDbidInfo( final String[][] dbidVals ) {
    594                FileUtil.copy( xmlFilename, xmlFilename + "_" + System.currentTimeMillis() );
    595
    596                final Document doc = DomParser.read( new File(xmlFilename) ) ;
    597                final Element firstRoot = doc.getDocumentElement();
    598                deleteChildElements( firstRoot, "dbid" );
    599
    600                if( dbidVals != null && dbidVals.length > 0 ) {
    601                        // 5.6.7.0 (2013/07/27) 内部MapをDBConfig.xmlの読み込み順に変更(なので、廃止)。
    602                        for( int i=0; i<dbidVals.length; i++ ) {
    603                                final Element newEle = doc.createElement( "dbid" );
    604                                for( int j=0; j<dbidVals[i].length; j++ ) {
    605                                        final Element newChEle = doc.createElement( DBID_INFO_KEYS[j] );
    606                                        newChEle.setTextContent( dbidVals[i][j] );
    607                                        newEle.appendChild( newChEle );
    608                                }
    609                                firstRoot.appendChild( newEle );
    610                                firstRoot.appendChild( doc.createTextNode( "\n\n" ) );
    611                        }
    612                }
    613
    614                DomParser.write( new File(xmlFilename), doc );
    615
    616                reload();               // 5.6.7.0 (2013/07/27) DBIDの属性情報のリストを更新後、初期化します。
    617        }
    618
    619        /**
    620         * DBドライバーの属性キーを返します。
    621         *
    622         * @og.rev 5.1.9.0 (2010/08/01) 新規作成
    623         *
    624         * @return      DBドライバーの属性キー
    625         * @og.rtnNotNull
    626         */
    627        public static String getDriverKey() {
    628                return DBDRIVER_CLASS_KEY;
    629        }
    630
    631        /**
    632         * DBドライバーのリスト(配列)を返します。
    633         *
    634         * @og.rev 5.1.9.0 (2010/08/01) 新規作成
    635         * @og.rev 5.6.7.0 (2013/07/27) driverList を書き込むのではなく、作成します。
    636         *
    637         * @return      DBドライバーリスト(配列)
    638         * @og.rtnNotNull
    639         */
    640        public String[] getDriverList() {
    641                return driverList.toArray( new String[driverList.size()] );
    642        }
    643
    644        /**
    645         * DBドライバーのリスト(配列)をセットします。
    646         *
    647         * このメソッドを呼び出すと、DBConfig.xmlで定義されているclass一覧を"一旦削除し"、
    648         * その上で、引数のDBドライバー一覧をDBConfig.xmlに書き込みます。
    649         *
    650         * 書き込みの直前に、同じフォルダにタイムスタンプを付加したバックアップファイルを作成します。
    651         *
    652         * @og.rev 5.1.9.0 (2010/08/01) 新規作成
    653         * @og.rev 5.6.7.0 (2013/07/27) DBドライバーのリストを更新後、初期化します。
    654         *
    655         * @param drivers DBドライバーの配列(可変長引数)
    656         */
    657        public void setDriverList( final String... drivers ) {
    658                FileUtil.copy( xmlFilename, xmlFilename + "_" + System.currentTimeMillis() );
    659
    660                final Document doc = DomParser.read( new File(xmlFilename) );
    661                final Element firstRoot = doc.getDocumentElement();
    662
    663                final Element parent = (Element)firstRoot.getElementsByTagName( "dbDriver" ).item( 0 );
    664                deleteChildElements( parent, "class" );
    665
    666                // 6.1.1.0 (2015/01/17) 可変長引数でもnullは来る。
    667                if( drivers != null && drivers.length > 0 ) {
    668                        for( int i=0; i<drivers.length; i++ ) {
    669                                final Element newEle = doc.createElement( "class" );
    670                                newEle.setTextContent( drivers[i] );
    671                                parent.appendChild( newEle );
    672                        }
    673                }
    674
    675                DomParser.write( new File(xmlFilename), doc );
    676
    677                reload();               // 5.6.7.0 (2013/07/27) DBドライバーのリストを更新後、初期化します。
    678        }
    679
    680        /**
    681         * DBID情報のキーとタイトルから、プルダウンメニューを作成するための情報を取得します。
    682         *
    683         * このメソッドを呼び出すと、DBConfig.xmlで定義されている dbidKey と、 title 属性から、
    684         * 「key1:val1 key2:val2 ・・・」という文字列を作成します。
    685         * これを利用すれば、プルダウンメニューが簡単に作成できます。
    686         *
    687         * @og.rev 5.6.7.0 (2013/07/27) プルダウンメニュー用の情報を作成します。
    688         * @og.rev 5.6.7.1 (2013/08/09) 表題(title)属性のスペース対策
    689         * @og.rev 6.2.6.0 (2015/06/19) 表題(title)属性のスペース対策(KEY:LBL をダブルクオートで囲う)
    690         * @og.rev 6.3.9.0 (2015/11/06) Use block level rather than method level synchronization.(PMD)
    691         * @og.rev 7.1.0.0 (2020/01/20) DB接続できないDBIDはプルダウンメニューに含めない。
    692         * @og.rev 7.2.3.1 (2020/04/17) 超特殊処理：Derby の場合は、ｴﾗｰﾒｯｾｰｼﾞを出さない。
    693         *
    694         * @return プルダウンメニューを作成するための情報
    695         */
    696        public String getCodeKeyVal() {
    697                if( codeKeyVal == null ) {
    698                        final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
    699                        // 6.3.9.0 (2015/11/06) 色々やりたいが、今はsynchronizedブロックにするだけにします。
    700                        synchronized( dbidMap ) {
    701                                for( final EDbid dbid : dbidMap.values() ) {
    702                                        final String key = dbid.getDbidKey();
    703                                        final String dbName = dbid.getDbProductName();          // 7.2.3.1 (2020/04/17)
    704                                        try {                                                                                           // 7.1.0.0 (2020/01/20)
    705//                                              if( dbid.getDbProductName() == null ) {
    706                                                if( dbName == null ) {
    707                                                        ConnectionFactory.getDBName( key );                     // 7.1.0.0 (2020/01/20) DB接続テスト
    708                                                }
    709
    710                                                final String lbl = StringUtil.nval( dbid.getTitle() , key );
    711                                                // 6.0.2.5 (2014/10/31) char を append する。
    712                                                if( lbl.indexOf( ' ' ) >= 0 ) {                                 // 5.6.7.1 (2013/08/09) スペース対策
    713                                                        buf.append( '"' ).append( key ).append( ':' ).append( lbl ).append( '"' );
    714                                                }
    715                                                else {
    716                                                        buf.append( key ).append( ':' ).append( lbl );
    717                                                }
    718                                                buf.append( ' ' );
    719                                        }
    720        //                              catch( final OgRuntimeException ex ) {                          // 7.1.0.0 (2020/01/20) エラー時
    721                                        catch( final RuntimeException ex ) {                            // 7.1.0.0 (2020/01/20) エラー時
    722                                                // 7.2.3.1 (2020/04/17) 超特殊処理：Derby の場合は、ｴﾗｰﾒｯｾｰｼﾞを出さない。
    723                                                // ProductやdbNameはnullなので、URLから判定します。
    724                                                final String dbUrl = dbid.getUrl();
    725                                                // 7.2.9.5 (2020/11/28)
    726                                                if( dbUrl == null || !dbUrl.contains( "derby" ) ) {
    727                                                        System.err.println( "警告：" + dbUrl + "\t\n" +  ex.getMessage() );
    728                                                }
    729
    730//                                              if( dbUrl != null && dbUrl.contains( "derby" ) ) {
    731//              //                                      final String errMsg = "DBID=[" + key + "] 接続できません" ;            // ｴﾗｰﾒｯｾｰｼﾞの簡素化
    732//              //                                      System.err.println( errMsg );
    733//                                              }
    734//                                              else {
    735//                                                      System.err.println( "警告：" + dbUrl + "\t\n" +  ex.getMessage() );
    736//                                              }
    737                                        }
    738                                }
    739                        }
    740
    741                        buf.setLength( buf.length()-1 );                // 最後のスペースを削除
    742                        codeKeyVal = buf.toString();
    743                }
    744
    745                return codeKeyVal;
    746        }
    747
    748        /**
    749         * 親要素を基点として、引数で指定されたタグ名を持つ子要素を削除します。
    750         *
    751         * @og.rev 5.6.7.0 (2013/07/27) staticメソッド を イスタンスメソッドに変更
    752         *
    753         * @param parent 親要素
    754         * @param childTagName 削除する子要素のタグ名
    755         */
    756        private void deleteChildElements( final Element parent, final String childTagName ) {
    757                Node child = parent.getFirstChild();
    758                boolean isDel = false;
    759                while( child != null ) {
    760                        // エレメント間の改行Cも削除するため、次の異なる要素が来るまでは削除し続けます。
    761                        if( child.getNodeType() == Node.ELEMENT_NODE ) {
    762                                // 6.4.4.1 (2016/03/18)
    763                                isDel = ((Element)child).getTagName().equalsIgnoreCase( childTagName );
    764                        }
    765
    766                        final Node next = child.getNextSibling();
    767                        if( isDel ) {
    768                                parent.removeChild( child );
    769                        }
    770                        child = next;
    771                }
    772        }
    773}