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