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