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