    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.hayabusa.resource;
    017
    018import org.opengion.hayabusa.common.HybsSystem;
    019import org.opengion.fukurou.util.ApplicationInfo;
    020import org.opengion.fukurou.db.DBUtil;
    021
    022import java.util.Map;
    023import java.util.HashMap;
    024import java.util.LinkedHashMap ;
    025import java.util.WeakHashMap ;
    026import java.util.Collections ;
    027
    028/**
    029 * コードオブジェクトを作成するデータロードクラスです。
    030 * systemId と lang に対応したコードオブジェクトを作成します。
    031 *
    032 * コードオブジェクトは、項目(CLM)に対して、複数のコード(CODE)を持っています。
    033 * この複数のコードを表示順に持つことで、プルダウンメニュー等の表示順を指定します。
    034 *
    035 * コードオブジェクトを作成する場合は、同一項目・コードで、作成区分(KBSAKU)違いの場合は、
    036 * 最も大きな作成区分を持つコードを使用します。
    037 * 作成区分(KBSAKU)は、他のリソースと異なり、同一項目・コード単位に設定すべきです。
    038 * これは、通常は項目単位に作成区分を持つべきところを、コード単位でしか
    039 * 持てないデータベースの設計になっている為です。アプリケーション側で設定条件を
    040 * きちんと管理すれば、作成区分を使用できますが、一般にはお奨めできません。
    041 * 作成区分(KBSAKU)='0' のデータは、マスタリソースとして、エンジンとともに
    042 * 配布されるリソースになります。
    043 *
    044 * 読み込みフラグ(FGLOAD)は、使用しません。
    045 * コードリソースに関しては、システム起動時に、すべてのコードリソースをエンジン内部
    046 * に取り込みます。ただし、リソースのキャッシュに、WeakHashMap クラスを使用しているため、
    047 * メモリオーバー時には、クリアされるため、単独での読み取りも行います。
    048 * SYSTEM_ID='**' は、共通リソースです。
    049 * これは、システム間で共通に使用されるリソース情報を登録しておきます。
    050 *
    051 * @og.rev 4.0.0.0 (2004/12/31) 新規作成
    052 * @og.group リソース管理
    053 *
    054 * @version  4.0
    055 * @author   Kazuhiko Hasegawa
    056 * @since    JDK5.0,
    057 */
    058final class CodeDataLoader {
    059        // リソースの接続先を、取得します。
    060        private final String DBID = HybsSystem.sys( "RESOURCE_DBID" );
    061
    062        /** ＤＢリソースの初期一括読み込みのクエリー */
    063        // キーブレイクで、SYSTEM_ID 違いは、まとめて処理する為、最初に ORDER BY しておく必要があります。
    064        //public static final String QUERY = "select CLM,CODE,'','',CODELVL,CODEGRP,CODE_PARAM,ROLES,SYSTEM_ID,KBSAKU" // 4.0.0.0(2007/10/17)
    065//      public static final String QUERY = "select CLM,CODE,'','',CODELVL,CODEGRP,CODE_PARAM,ROLES,SYSTEM_ID,KBSAKU,''" // 4.3.8.0 (2009/08/01)
    066//                                                                      + " from GEA04 where SYSTEM_ID in ( ?,'**') and FGJ='1'"
    067//                                                                      + " order by SYSTEM_ID,KBSAKU,CLM,SEQNO,CODE" ;
    068        // 5.1.9.0 (2010/08/01) order by 変更
    069//      public static final String QUERY = "select CLM,CODE,'','',CODELVL,CODEGRP,CODE_PARAM,ROLES,SYSTEM_ID,KBSAKU,''" // 5.1.9.0 (2010/08/01)
    070        public static final String QUERY = "select CLM,CODE,'','',CODELVL,CODEGRP,CODE_PARAM,ROLES,SYSTEM_ID,KBSAKU,'',''" // 5.6.8.2 (2013/09/20)
    071                                                                        + " from GEA04 where SYSTEM_ID in ( ?,'**') and FGJ='1'"
    072                                                                        + " order by SYSTEM_ID,KBSAKU,CLM,SEQNO,CODELVL,CODE" ;
    073
    074        /** ＤＢリソースの個別読み込み時のクエリー */
    075        //public static final String QUERY2 = "select CLM,CODE,'','',CODELVL,CODEGRP,CODE_PARAM,ROLES,SYSTEM_ID,KBSAKU" // 4.0.0.0(2007/10/17)
    076//      public static final String QUERY2 = "select CLM,CODE,'','',CODELVL,CODEGRP,CODE_PARAM,ROLES,SYSTEM_ID,KBSAKU,''" // 4.3.8.0 (2009/08/01)
    077//                                                                      + " from GEA04 where SYSTEM_ID in ( ?,'**') and FGJ='1' and CLM=?"
    078//                                                                      + " order by SYSTEM_ID,KBSAKU,CLM,SEQNO,CODE" ;
    079        // 5.1.9.0 (2010/08/01) order by 変更
    080//      public static final String QUERY2 = "select CLM,CODE,'','',CODELVL,CODEGRP,CODE_PARAM,ROLES,SYSTEM_ID,KBSAKU,''" // 5.1.9.0 (2010/08/01)
    081        public static final String QUERY2 = "select CLM,CODE,'','',CODELVL,CODEGRP,CODE_PARAM,ROLES,SYSTEM_ID,KBSAKU,'',''" // 5.6.8.2 (2013/09/20)
    082                                                                        + " from GEA04 where SYSTEM_ID in ( ?,'**') and FGJ='1' and CLM=?"
    083                                                                        + " order by SYSTEM_ID,KBSAKU,CLM,SEQNO,CODELVL,CODE" ;
    084
    085        private final Map<String,CodeData> pool = Collections.synchronizedMap( new WeakHashMap<String,CodeData>() );        // キャッシュ用プール
    086        private final String  SYSTEM_ID ;               // システムID
    087//      private final String  LANG ;                    // 言語
    088
    089        /** コネクションにアプリケーション情報を追記するかどうか指定 */
    090        public static final boolean USE_DB_APPLICATION_INFO  = HybsSystem.sysBool( "USE_DB_APPLICATION_INFO" ) ;
    091
    092        // 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
    093        private final ApplicationInfo appInfo;
    094
    095        private final LabelDataLoader LABEL_LOADER; // 見直し要!!!
    096
    097        /**
    098         *  lang 毎に ファクトリオブジェクトを作成します。
    099         *
    100         * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
    101         *
    102         * @param systemId システムID
    103         * @param initLoad リソースデータの先読み可否(true:先読みする)
    104         * @param lLoader ラベルデータローダー
    105         */
    106//      CodeDataLoader( final String systemId,final String lang,final boolean initLoad ) {
    107        CodeDataLoader( final String systemId,final boolean initLoad,final LabelDataLoader lLoader) {
    108                SYSTEM_ID = systemId;
    109//              LANG      = lang;
    110                LABEL_LOADER = lLoader;
    111
    112                // 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
    113                if( USE_DB_APPLICATION_INFO ) {
    114                        appInfo = new ApplicationInfo();
    115                        // ユーザーID,IPアドレス,ホスト名
    116                        appInfo.setClientInfo( SYSTEM_ID,HybsSystem.HOST_ADRS,HybsSystem.HOST_NAME );
    117                        // 画面ID,操作,プログラムID
    118                        appInfo.setModuleInfo( "CodeDataLoader",null,null );
    119                }
    120                else {
    121                        appInfo = null;
    122                }
    123
    124                // ApplicationInfo の設定が終わってから実行します。
    125                if( initLoad ) { loadDBResource(); }
    126        }
    127
    128        /**
    129         * ＤＢリソースより コードデータを取得、設定します。
    130         *
    131         * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
    132         * @og.rev 4.3.8.0 (2009/08/01) rawShortLabel追加
    133         * @og.rev 5.6.8.2 (2013/09/20) rawLongLabel対応
    134         */
    135        private void loadDBResource() {
    136//              String[] args = new String[] { LANG,SYSTEM_ID };
    137                String[] args = new String[] { SYSTEM_ID };
    138
    139                String[][] vals = DBUtil.dbExecute( QUERY,args,appInfo,DBID );
    140
    141                Map<String,Map<String,String[]>> clmMap  = new HashMap<String,Map<String,String[]>>();
    142                int len = vals.length;
    143                String bkClm = null;                    // キーブレイク
    144                String bkSystem = null;
    145                String bkKbsaku = null;
    146                // 以下の処理は、SYSTEM_ID違いを塊で処理します。(混在させません。)
    147                Map<String,String[]> codeMap = null;
    148                for( int i=0; i<len; i++ ) {
    149                        String   clm  = vals[i][CodeData.CLM];
    150                        String   code = vals[i][CodeData.CODE];
    151                        String   systemId = vals[i][CodeData.SYSTEM_ID];
    152                        String   kbsaku = vals[i][CodeData.KBSAKU];
    153//                      if( bkClm == null || !bkClm.equals( clm ) ) {
    154                        if( bkClm == null || !bkClm.equals( clm ) || !bkSystem.equals(systemId) || !bkKbsaku.equals(kbsaku) ) {
    155                                codeMap = new LinkedHashMap<String,String[]>();
    156                                clmMap.put( clm,codeMap );
    157                                bkClm = clm;
    158                                bkSystem = systemId;
    159                                bkKbsaku = kbsaku;
    160                        }
    161
    162                        String lkey = clm+"."+code; // やっつけ〜
    163                        vals[i][CodeData.LNAME] = LABEL_LOADER.getLabelData(lkey).getLongLabel();
    164                        vals[i][CodeData.SNAME] = LABEL_LOADER.getLabelData(lkey).getShortLabel();
    165                        vals[i][CodeData.RSNAME] = LABEL_LOADER.getLabelData(lkey).getRawShortLabel(); // 4.3.8.0 (2009/08/01) spanが付かない名前短
    166                        vals[i][CodeData.RLNAME] = LABEL_LOADER.getLabelData(lkey).getRawLongLabel(); // 5.6.8.2 (2013/09/01) 加工していない名前長
    167
    168                        codeMap.put( code,vals[i] );
    169                }
    170
    171                String[] clmKeys = clmMap.keySet().toArray( new String[clmMap.size()] );
    172                int size = clmKeys.length;
    173                for( int i=0; i<size; i++ ) {
    174                        String clm = clmKeys[i];
    175                        codeMap = clmMap.get( clm );
    176
    177                        pool.put( clm,new CodeData( clm,codeMap ) );
    178                }
    179
    180                System.out.println( "  CodeDataLoader [" + size + "] loaded" );
    181        }
    182
    183        /**
    184         * CodeData オブジェクトを取得します。
    185         * 作成したCodeDataオブジェクトは，内部にプールしておき，同じリソース要求が
    186         * あったときは，プールの CodeDataを返します。
    187         *
    188         * @og.rev 4.3.8.0 (2009/08/01) rawShortLabel追加
    189         * @og.rev 5.6.8.2 (2013/09/20) rawLongLabel追加
    190         *
    191         * @param   key       コードのキー
    192         *
    193         * @return  CodeData  オブジェクト
    194         */
    195        public CodeData getCodeData( final String key ) {
    196                CodeData codeData = pool.get( key ) ;
    197
    198                if( codeData == null ) {
    199//                      String[] args = new String[] { LANG,SYSTEM_ID,key };
    200                        String[] args = new String[] { SYSTEM_ID,key };
    201                        String[][] vals = DBUtil.dbExecute( QUERY2,args,appInfo,DBID );
    202
    203                        int len = vals.length;
    204                        String bkSystem = null;                 // キーブレイク
    205                        String bkKbsaku = null;
    206                        // 以下の処理は、SYSTEM_ID違いを塊で処理します。(混在させません。)
    207                        Map<String,String[]> codeMap = null;
    208                        for( int i=0; i<len; i++ ) {
    209                                String   systemId = vals[i][CodeData.SYSTEM_ID];
    210                                String   code     = vals[i][CodeData.CODE];
    211                                String   kbsaku = vals[i][CodeData.KBSAKU];
    212//                              if( bkSystem == null || !bkSystem.equals( systemId ) ) {
    213                                if( bkSystem == null || !bkSystem.equals( systemId ) || !bkKbsaku.equals(kbsaku) ) {
    214                                        codeMap = new LinkedHashMap<String,String[]>();
    215                                        bkSystem = systemId;
    216                                        bkKbsaku = kbsaku;
    217                                }
    218
    219                                String lkey = key+"."+code; // やっつけ〜
    220                                vals[i][CodeData.LNAME] = LABEL_LOADER.getLabelData(lkey).getLongLabel();
    221                                vals[i][CodeData.SNAME] = LABEL_LOADER.getLabelData(lkey).getShortLabel();
    222                                vals[i][CodeData.RSNAME] = LABEL_LOADER.getLabelData(lkey).getRawShortLabel(); // 4.3.8.0 (2009/08/01) spanが付かない名前短
    223                                vals[i][CodeData.RLNAME] = LABEL_LOADER.getLabelData(lkey).getRawLongLabel(); // 5.6.8.2 (2013/09/20) 加工していない名前長
    224
    225                                codeMap.put( code,vals[i] );
    226                        }
    227
    228                        if( codeMap != null ) {
    229                                codeData = new CodeData( key,codeMap );
    230                                pool.put( key,codeData );
    231                        }
    232                }
    233                return codeData ;
    234        }
    235
    236        /**
    237         * CodeData オブジェクトを取得します。
    238         * 作成したCodeDataオブジェクトは，内部にプールしておき，同じリソース要求が
    239         * あったときは，プールの CodeDataを返します。
    240         *
    241         * 引数にQUERYを渡すことで、DBから、動的にコードリソースを作成できます。
    242         * 引数の順番は、CodeData で定義している CLM,CODE,LNAME,SNAME の順番のままです。
    243         * QUERY には、key を引数にとる必要があります。つまり、WHERE CLM = ? の様な記述が必要です。
    244         *
    245         * @og.rev 5.4.2.2 (2011/12/14) 新規追加。
    246         *
    247         * @param   key   コードのキー
    248         * @param       query 検索SQL(引数に、? を一つ持つ)
    249         *
    250         * @return  CodeData  オブジェクト
    251         */
    252        public CodeData getCodeData( final String key,final String query ) {
    253                CodeData codeData = pool.get( key ) ;
    254
    255                if( codeData == null ) {
    256                        String[] args = new String[] { key };
    257                        String[][] vals = DBUtil.dbExecute( query,args,appInfo,DBID );
    258
    259                        int len = vals.length;
    260                        Map<String,String[]> codeMap = new LinkedHashMap<String,String[]>();
    261                        for( int i=0; i<len; i++ ) {
    262                                String[] cdVals = new String[CodeData.MAX_LENGTH];      // 空の配列を毎回作成
    263
    264                                String   code   = vals[i][CodeData.CODE];
    265
    266                                cdVals[CodeData.CLM]   = key ;
    267                                cdVals[CodeData.CODE]  = code;
    268                                cdVals[CodeData.LNAME] = vals[i][CodeData.LNAME];
    269                                cdVals[CodeData.SNAME] = vals[i][CodeData.SNAME];
    270
    271                                codeMap.put( code,cdVals );
    272                        }
    273
    274                        if( ! codeMap.isEmpty() ) {
    275                                codeData = new CodeData( key,codeMap );
    276                                pool.put( key,codeData );
    277                        }
    278                }
    279                return codeData ;
    280        }
    281
    282        /**
    283         * CodeData オブジェクトのキャッシュを個別にクリアします。
    284         * リソースデータの更新など、一部分の更新時に、すべてのキャッシュを
    285         * 破棄するのではなく、指定の分のみ破棄できる機能です。
    286         *
    287         * @og.rev 4.0.2.0 (2007/12/25) コードリソースクリア時に対応するラベルリソースもクリアする。
    288         *
    289         * @param   key       コードのキー
    290         */
    291        public void clear( final String key ) {
    292
    293                // 4.0.2.0 (2007/12/25)
    294                CodeData cdata = pool.remove( key );
    295                if( cdata != null ) {
    296                        String clm = cdata.getColumn();
    297                        for( int i=0; i<cdata.getSize(); i++ ) {
    298                                LABEL_LOADER.clear( clm + '.' + cdata.getCodeKey( i ) );
    299                        }
    300                }
    301        }
    302
    303        /**
    304         * CodeData オブジェクトのキャッシュをクリアして、再作成します。
    305         *
    306         */
    307        public void clear() {
    308                pool.clear();
    309        }
    310}