    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        // 5.1.9.0 (2010/08/01) order by 変更
    065        public static final String QUERY = "select CLM,CODE,'','',CODELVL,CODEGRP,CODE_PARAM,ROLES,SYSTEM_ID,KBSAKU,'',''" // 5.6.8.2 (2013/09/20)
    066                                                                        + " from GEA04 where SYSTEM_ID in ( ?,'**') and FGJ='1'"
    067                                                                        + " order by SYSTEM_ID,KBSAKU,CLM,SEQNO,CODELVL,CODE" ;
    068
    069        /** ＤＢリソースの個別読み込み時のクエリー */
    070        // 5.1.9.0 (2010/08/01) order by 変更
    071        public static final String QUERY2 = "select CLM,CODE,'','',CODELVL,CODEGRP,CODE_PARAM,ROLES,SYSTEM_ID,KBSAKU,'',''" // 5.6.8.2 (2013/09/20)
    072                                                                        + " from GEA04 where SYSTEM_ID in ( ?,'**') and FGJ='1' and CLM=?"
    073                                                                        + " order by SYSTEM_ID,KBSAKU,CLM,SEQNO,CODELVL,CODE" ;
    074
    075        private final Map<String,CodeData> pool = Collections.synchronizedMap( new WeakHashMap<String,CodeData>() );    // キャッシュ用プール
    076        private final String  SYSTEM_ID ;               // システムID
    077
    078        /** コネクションにアプリケーション情報を追記するかどうか指定 */
    079        public static final boolean USE_DB_APPLICATION_INFO  = HybsSystem.sysBool( "USE_DB_APPLICATION_INFO" ) ;
    080
    081        // 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
    082        private final ApplicationInfo appInfo;
    083
    084        private final LabelDataLoader LABEL_LOADER; // 見直し要!!!
    085
    086        /**
    087         *  lang 毎に ファクトリオブジェクトを作成します。
    088         *
    089         * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
    090         *
    091         * @param systemId システムID
    092         * @param initLoad リソースデータの先読み可否(true:先読みする)
    093         * @param lLoader ラベルデータローダー
    094         */
    095        CodeDataLoader( final String systemId,final boolean initLoad,final LabelDataLoader lLoader) {
    096                SYSTEM_ID = systemId;
    097                LABEL_LOADER = lLoader;
    098
    099                // 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
    100                if( USE_DB_APPLICATION_INFO ) {
    101                        appInfo = new ApplicationInfo();
    102                        // ユーザーID,IPアドレス,ホスト名
    103                        appInfo.setClientInfo( SYSTEM_ID,HybsSystem.HOST_ADRS,HybsSystem.HOST_NAME );
    104                        // 画面ID,操作,プログラムID
    105                        appInfo.setModuleInfo( "CodeDataLoader",null,null );
    106                }
    107                else {
    108                        appInfo = null;
    109                }
    110
    111                // ApplicationInfo の設定が終わってから実行します。
    112                if( initLoad ) { loadDBResource(); }
    113        }
    114
    115        /**
    116         * ＤＢリソースより コードデータを取得、設定します。
    117         *
    118         * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
    119         * @og.rev 4.3.8.0 (2009/08/01) rawShortLabel追加
    120         * @og.rev 5.6.8.2 (2013/09/20) rawLongLabel対応
    121         */
    122        private void loadDBResource() {
    123                String[] args = new String[] { SYSTEM_ID };
    124
    125                String[][] vals = DBUtil.dbExecute( QUERY,args,appInfo,DBID );
    126
    127                Map<String,Map<String,String[]>> clmMap  = new HashMap<String,Map<String,String[]>>();
    128                int len = vals.length;
    129                String bkClm = null;                    // キーブレイク
    130                String bkSystem = null;
    131                String bkKbsaku = null;
    132                // 以下の処理は、SYSTEM_ID違いを塊で処理します。(混在させません。)
    133                Map<String,String[]> codeMap = null;
    134                for( int i=0; i<len; i++ ) {
    135                        String   clm  = vals[i][CodeData.CLM];
    136                        String   code = vals[i][CodeData.CODE];
    137                        String   systemId = vals[i][CodeData.SYSTEM_ID];
    138                        String   kbsaku = vals[i][CodeData.KBSAKU];
    139                        if( bkClm == null || !bkClm.equals( clm ) || !bkSystem.equals(systemId) || !bkKbsaku.equals(kbsaku) ) {
    140                                codeMap = new LinkedHashMap<String,String[]>();
    141                                clmMap.put( clm,codeMap );
    142                                bkClm = clm;
    143                                bkSystem = systemId;
    144                                bkKbsaku = kbsaku;
    145                        }
    146
    147                        String lkey = clm+"."+code; // やっつけ～
    148                        vals[i][CodeData.LNAME] = LABEL_LOADER.getLabelData(lkey).getLongLabel();
    149                        vals[i][CodeData.SNAME] = LABEL_LOADER.getLabelData(lkey).getShortLabel();
    150                        vals[i][CodeData.RSNAME] = LABEL_LOADER.getLabelData(lkey).getRawShortLabel(); // 4.3.8.0 (2009/08/01) spanが付かない名前短
    151                        vals[i][CodeData.RLNAME] = LABEL_LOADER.getLabelData(lkey).getRawLongLabel(); // 5.6.8.2 (2013/09/01) 加工していない名前長
    152
    153                        codeMap.put( code,vals[i] );
    154                }
    155
    156                String[] clmKeys = clmMap.keySet().toArray( new String[clmMap.size()] );
    157                int size = clmKeys.length;
    158                for( int i=0; i<size; i++ ) {
    159                        String clm = clmKeys[i];
    160                        codeMap = clmMap.get( clm );
    161
    162                        pool.put( clm,new CodeData( clm,codeMap ) );
    163                }
    164
    165                System.out.println( "  CodeDataLoader [" + size + "] loaded" );
    166        }
    167
    168        /**
    169         * CodeData オブジェクトを取得します。
    170         * 作成したCodeDataオブジェクトは，内部にプールしておき，同じリソース要求が
    171         * あったときは，プールの CodeDataを返します。
    172         *
    173         * @og.rev 4.3.8.0 (2009/08/01) rawShortLabel追加
    174         * @og.rev 5.6.8.2 (2013/09/20) rawLongLabel追加
    175         *
    176         * @param   key       コードのキー
    177         *
    178         * @return  CodeData  オブジェクト
    179         */
    180        public CodeData getCodeData( final String key ) {
    181                CodeData codeData = pool.get( key ) ;
    182
    183                if( codeData == null ) {
    184                        String[] args = new String[] { SYSTEM_ID,key };
    185                        String[][] vals = DBUtil.dbExecute( QUERY2,args,appInfo,DBID );
    186
    187                        int len = vals.length;
    188                        String bkSystem = null;                 // キーブレイク
    189                        String bkKbsaku = null;
    190                        // 以下の処理は、SYSTEM_ID違いを塊で処理します。(混在させません。)
    191                        Map<String,String[]> codeMap = null;
    192                        for( int i=0; i<len; i++ ) {
    193                                String   systemId = vals[i][CodeData.SYSTEM_ID];
    194                                String   code     = vals[i][CodeData.CODE];
    195                                String   kbsaku = vals[i][CodeData.KBSAKU];
    196                                if( bkSystem == null || !bkSystem.equals( systemId ) || !bkKbsaku.equals(kbsaku) ) {
    197                                        codeMap = new LinkedHashMap<String,String[]>();
    198                                        bkSystem = systemId;
    199                                        bkKbsaku = kbsaku;
    200                                }
    201
    202                                String lkey = key+"."+code; // やっつけ～
    203                                vals[i][CodeData.LNAME] = LABEL_LOADER.getLabelData(lkey).getLongLabel();
    204                                vals[i][CodeData.SNAME] = LABEL_LOADER.getLabelData(lkey).getShortLabel();
    205                                vals[i][CodeData.RSNAME] = LABEL_LOADER.getLabelData(lkey).getRawShortLabel(); // 4.3.8.0 (2009/08/01) spanが付かない名前短
    206                                vals[i][CodeData.RLNAME] = LABEL_LOADER.getLabelData(lkey).getRawLongLabel(); // 5.6.8.2 (2013/09/20) 加工していない名前長
    207
    208                                codeMap.put( code,vals[i] );
    209                        }
    210
    211                        if( codeMap != null ) {
    212                                codeData = new CodeData( key,codeMap );
    213                                pool.put( key,codeData );
    214                        }
    215                }
    216                return codeData ;
    217        }
    218
    219        /**
    220         * CodeData オブジェクトを取得します。
    221         * 作成したCodeDataオブジェクトは，内部にプールしておき，同じリソース要求が
    222         * あったときは，プールの CodeDataを返します。
    223         *
    224         * 引数にQUERYを渡すことで、DBから、動的にコードリソースを作成できます。
    225         * 引数の順番は、CodeData で定義している CLM,CODE,LNAME,SNAME の順番のままです。
    226         * QUERY には、key を引数にとる必要があります。つまり、WHERE CLM = ? の様な記述が必要です。
    227         *
    228         * @og.rev 5.4.2.2 (2011/12/14) 新規追加。
    229         *
    230         * @param   key   コードのキー
    231         * @param       query 検索SQL(引数に、? を一つ持つ)
    232         *
    233         * @return  CodeData  オブジェクト
    234         */
    235        public CodeData getCodeData( final String key,final String query ) {
    236                CodeData codeData = pool.get( key ) ;
    237
    238                if( codeData == null ) {
    239                        String[] args = new String[] { key };
    240                        String[][] vals = DBUtil.dbExecute( query,args,appInfo,DBID );
    241
    242                        int len = vals.length;
    243                        Map<String,String[]> codeMap = new LinkedHashMap<String,String[]>();
    244                        for( int i=0; i<len; i++ ) {
    245                                String[] cdVals = new String[CodeData.MAX_LENGTH];      // 空の配列を毎回作成
    246
    247                                String   code   = vals[i][CodeData.CODE];
    248
    249                                cdVals[CodeData.CLM]   = key ;
    250                                cdVals[CodeData.CODE]  = code;
    251                                cdVals[CodeData.LNAME] = vals[i][CodeData.LNAME];
    252                                cdVals[CodeData.SNAME] = vals[i][CodeData.SNAME];
    253
    254                                codeMap.put( code,cdVals );
    255                        }
    256
    257                        if( ! codeMap.isEmpty() ) {
    258                                codeData = new CodeData( key,codeMap );
    259                                pool.put( key,codeData );
    260                        }
    261                }
    262                return codeData ;
    263        }
    264
    265        /**
    266         * CodeData オブジェクトのキャッシュを個別にクリアします。
    267         * リソースデータの更新など、一部分の更新時に、すべてのキャッシュを
    268         * 破棄するのではなく、指定の分のみ破棄できる機能です。
    269         *
    270         * @og.rev 4.0.2.0 (2007/12/25) コードリソースクリア時に対応するラベルリソースもクリアする。
    271         *
    272         * @param   key       コードのキー
    273         */
    274        public void clear( final String key ) {
    275
    276                // 4.0.2.0 (2007/12/25)
    277                CodeData cdata = pool.remove( key );
    278                if( cdata != null ) {
    279                        String clm = cdata.getColumn();
    280                        for( int i=0; i<cdata.getSize(); i++ ) {
    281                                LABEL_LOADER.clear( clm + '.' + cdata.getCodeKey( i ) );
    282                        }
    283                }
    284        }
    285
    286        /**
    287         * CodeData オブジェクトのキャッシュをクリアして、再作成します。
    288         *
    289         */
    290        public void clear() {
    291                pool.clear();
    292        }
    293}