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        /** DBリソースの初期一括読み込みのクエリー */
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        /** DBリソースの個別読み込み時のクエリー */
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         * DBリソースより コードデータを取得、設定します。
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}