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 java.util.Map; 019import java.util.HashMap; 020import java.util.LinkedHashMap ; 021import java.util.WeakHashMap ; 022import java.util.Collections ; 023 024import org.opengion.hayabusa.common.HybsSystem; 025import org.opengion.fukurou.db.ApplicationInfo; 026import org.opengion.fukurou.db.DBUtil; 027import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE; // 8.0.0.0 (2021/10/01) 028 029/** 030 * コードオブジェクトを作成するデータロードクラスです。 031 * systemId と lang に対応したコードオブジェクトを作成します。 032 * 033 * コードオブジェクトは、項目(CLM)に対して、複数のコード(CODE)を持っています。 034 * この複数のコードを表示順に持つことで、プルダウンメニュー等の表示順を指定します。 035 * 036 * コードオブジェクトを作成する場合は、同一項目・コードで、作成区分(KBSAKU)違いの場合は、 037 * 最も大きな作成区分を持つコードを使用します。 038 * 作成区分(KBSAKU)は、他のリソースと異なり、同一項目・コード単位に設定すべきです。 039 * これは、通常は項目単位に作成区分を持つべきところを、コード単位でしか 040 * 持てないデータベースの設計になっている為です。アプリケーション側で設定条件を 041 * きちんと管理すれば、作成区分を使用できますが、一般にはお奨めできません。 042 * 作成区分(KBSAKU)='0' のデータは、マスタリソースとして、エンジンとともに 043 * 配布されるリソースになります。 044 * 045 * 読み込みフラグ(FGLOAD)は、使用しません。 046 * コードリソースに関しては、システム起動時に、すべてのコードリソースをエンジン内部 047 * に取り込みます。ただし、リソースのキャッシュに、WeakHashMap クラスを使用しているため、 048 * メモリオーバー時には、クリアされるため、単独での読み取りも行います。 049 * SYSTEM_ID='**' は、共通リソースです。 050 * これは、システム間で共通に使用されるリソース情報を登録しておきます。 051 * 052 * @og.rev 4.0.0.0 (2004/12/31) 新規作成 053 * @og.group リソース管理 054 * 055 * @version 4.0 056 * @author Kazuhiko Hasegawa 057 * @since JDK5.0, 058 */ 059final class CodeDataLoader { 060 // リソースの接続先を、取得します。 061 private final String DBID = HybsSystem.sys( "RESOURCE_DBID" ); 062 063 /** DBリソースの初期一括読み込みのクエリー */ 064 // 7.3.1.3 (2021/03/09) 065// private static final String SEL_CLM = "select CLM,CODE,'' as LNAME,'' as SNAME,CODELVL,CODEGRP" 066// + ",CODE_PARAM,ROLES,SYSTEM_ID,KBSAKU" 067// + ",'' as RSNAME,'' as RLNAME,'' as DESCRIPT" 068// + ",SEQNO" ; 069 070 // 7.3.1.3 (2021/03/09) 071 // 7.4.5.0 (2021/08/31) Firebird 対応 072// private static final String QUERY = "select a.* from (" 073// + SEL_CLM + ",0 as SNO" 074// + " from GEA04 where SYSTEM_ID='**' and FGJ='1'" // エンジン共通 075// + " union all " 076// + SEL_CLM + ",1 as SNO" 077// + " from GEA04 where SYSTEM_ID=? and FGJ='1'" // RESOURCE_BASE_SYSTEM_ID 078// + " union all " 079// + SEL_CLM + ",2 as SNO" 080// + " from GEA04 where SYSTEM_ID=? and FGJ='1'" // 最上位ののSYSTEM_ID 081// + " ) a " // 8.0.0.0 (2021/08/31) 082// + " order by a.SNO,a.KBSAKU,a.CLM,a.SEQNO,a.CODELVL,a.CODE" ; 083 084 /** 8.0.0.0 (2021/10/01) RESOURCE_BASE_SYSTEM_ID は、SYSTEM_IDの配列で複数指定できる。 */ 085 private static final String QUERY = "select CLM,CODE,'' as LNAME,'' as SNAME,CODELVL,CODEGRP" 086 + ",CODE_PARAM,ROLES,SYSTEM_ID,KBSAKU" 087 + ",'' as RSNAME,'' as RLNAME,'' as DESCRIPT,SEQNO" 088 + " from GEA04 where SYSTEM_ID=? and FGJ='1'" // バインド変数 SYSTEM_ID=? 089 + " order by KBSAKU,CLM,SEQNO,CODELVL,CODE" ; 090 091 /** DBリソースの個別読み込み時のクエリー */ 092 // 注意:CLMを unionする前に条件として入れたのでパラメータの順番が変わる。 093 // 7.3.1.3 (2021/03/09) 094 // 7.4.5.0 (2021/08/31) Firebird 対応 095// private static final String QUERY2 = "select a.* from (" 096// + SEL_CLM + ",0 as SNO" 097// + " from GEA04 where SYSTEM_ID='**' and CLM=? and FGJ='1'" // エンジン共通 098// + " union all " 099// + SEL_CLM + ",1 as SNO" 100// + " from GEA04 where SYSTEM_ID=? and CLM=? and FGJ='1'" // RESOURCE_BASE_SYSTEM_ID 101// + " union all " 102// + SEL_CLM + ",2 as SNO" 103// + " from GEA04 where SYSTEM_ID=? and CLM=? and FGJ='1'" // 最上位ののSYSTEM_ID 104// + " ) a " // 8.0.0.0 (2021/08/31) 105// + " order by a.SNO,a.KBSAKU,a.CLM,a.SEQNO,a.CODELVL,a.CODE" ; 106 107 /** 8.0.0.0 (2021/10/01) RESOURCE_BASE_SYSTEM_ID は、SYSTEM_IDの配列で複数指定できる。 */ 108 private static final String QUERY2 = "select CLM,CODE,'' as LNAME,'' as SNAME,CODELVL,CODEGRP" 109 + ",CODE_PARAM,ROLES,SYSTEM_ID,KBSAKU" 110 + ",'' as RSNAME,'' as RLNAME,'' as DESCRIPT,SEQNO" 111 + " from GEA04 where SYSTEM_ID=? and CLM=? and FGJ='1'" // バインド変数 SYSTEM_ID=? and CLM=? 112 + " order by KBSAKU DESC,CLM,SEQNO,CODELVL,CODE" ; // 逆順で検索し、先頭採用 113 114 /** 6.4.3.1 (2016/02/12) Collections.synchronizedMap で同期処理を行います。 */ 115 private final Map<String,CodeData> codeDtMap = Collections.synchronizedMap( new WeakHashMap<>() ); // キャッシュ用プール 116 // 8.0.0.0 (2021/10/01) RESOURCE_BASE_SYSTEM_ID は、SYSTEM_IDの配列で複数指定できる。 117// private final String SYSTEM_ID ; // システムID 118// private final String BASE_SYS_ID ; // 7.2.9.2 (2020/10/30) ベースシステムID 119 private final String[] SYS_ARRAY; // 8.0.0.0 (2021/10/01) 120 121 /** コネクションにアプリケーション情報を追記するかどうか指定 */ 122 public static final boolean USE_DB_APPLICATION_INFO = HybsSystem.sysBool( "USE_DB_APPLICATION_INFO" ) ; 123 124 /** 3.8.7.0 (2006/12/15) アクセスログ取得の為、ApplicationInfoオブジェクトを設定 */ 125 private final ApplicationInfo appInfo; 126 127 private final LabelDataLoader LABEL_LOADER; // 見直し要!!! 128 129 /** 130 * lang 毎に ファクトリオブジェクトを作成します。 131 * 132 * @og.rev 7.2.9.2 (2020/10/30) ベースとなるSYSTEM_ID(RESOURCE_BASE_SYSTEM_ID)の取得 133 * @og.rev 8.0.0.0 (2021/10/01) RESOURCE_BASE_SYSTEM_ID は、SYSTEM_IDの配列で複数指定できる。 134 * 135// * @param systemId システムID 136// * @param baseSys ベースとなるSYSTEM_ID 137 * @param sysAry 階層リソースの元となるSYSTEM_IDの配列(前方優先) 138 * @param initLoad リソースデータの先読み可否(true:先読みする) 139 * @param lLoader ラベルデータローダー 140 */ 141// CodeDataLoader( final String systemId,final String baseSys,final boolean initLoad,final LabelDataLoader lLoader) { 142 /* default */ CodeDataLoader( final String[] sysAry,final boolean initLoad,final LabelDataLoader lLoader) { 143// SYSTEM_ID = systemId; 144// BASE_SYS_ID = baseSys ; // 7.2.9.2 (2020/10/30) 145 SYS_ARRAY = sysAry ; // 8.0.0.0 (2021/10/01) 146 LABEL_LOADER= lLoader; 147 148 // 3.8.7.0 (2006/12/15) アクセスログ取得の為、ApplicationInfoオブジェクトを設定 149 if( USE_DB_APPLICATION_INFO ) { 150 appInfo = new ApplicationInfo(); 151 // ユーザーID,IPアドレス,ホスト名 152// appInfo.setClientInfo( SYSTEM_ID,HybsSystem.HOST_ADRS,HybsSystem.HOST_NAME ); 153 appInfo.setClientInfo( SYS_ARRAY[0],HybsSystem.HOST_ADRS,HybsSystem.HOST_NAME ); 154 // 画面ID,操作,プログラムID 155 appInfo.setModuleInfo( "CodeDataLoader",null,null ); 156 } 157 else { 158 appInfo = null; 159 } 160 161 // ApplicationInfo の設定が終わってから実行します。 162 if( initLoad ) { loadDBResource(); } 163 } 164 165 /** 166 * DBリソースより コードデータを取得、設定します。 167 * 168 * ※ 以下のロジックは、後方優先であり、SYSTEM_IDの配列は前方優先なので逆順で回します。 169 * 170 * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為、ApplicationInfoオブジェクトを設定 171 * @og.rev 4.3.8.0 (2009/08/01) rawShortLabel追加 172 * @og.rev 5.6.8.2 (2013/09/20) rawLongLabel対応 173 * @og.rev 6.2.0.0 (2015/02/27) description 概要説明 追加 174 * @og.rev 7.2.6.0 (2020/06/30) "**"以外にベースとなるSYSTEM_ID(RESOURCE_BASE_SYSTEM_ID)設定の対応 175 * @og.rev 8.0.0.0 (2021/10/01) 階層リソースの元となるSYSTEM_IDの配列(前方優先)を使用する。 176 */ 177 private void loadDBResource() { 178 final int size = SYS_ARRAY.length; 179 180 final int[] cnt = new int[size]; // 各SYSTEM_ID の個数 181 int selCnt = 0; 182 final Map<String,Map<String,String[]>> clmMap = new HashMap<>(); 183 for( int j=size-1; j>=0; j-- ) { // SYSTEM_IDの配列は、前方優先なので、逆順で回す必要がある。 184 final String sysId = SYS_ARRAY[j]; 185// final String[] args = new String[] { BASE_SYS_ID,SYSTEM_ID }; // 7.2.6.0 (2020/06/30) 186 // 8.5.4.2 (2024/01/12) PMD 7.0.0 UseShortArrayInitializer 187// final String[] args = new String[] { sysId }; // 8.0.0.0 (2021/10/01) 188 final String[] args = { sysId }; // 8.0.0.0 (2021/10/01) 189 190 final String[][] vals = DBUtil.dbExecute( QUERY,args,appInfo,DBID ); // 8.5.4.2 (2024/01/12) PMD 7.0.0 LocalVariableCouldBeFinal 191 192// final Map<String,Map<String,String[]>> clmMap = new HashMap<>(); 193 final int len = vals.length; 194 selCnt += len; 195 cnt[j] = len ; 196 197 String bkClm = null; // キーブレイク 198 // String bkSystem = null; 199 String bkKbsaku = null; 200 // 以下の処理は、SYSTEM_ID違いを塊で処理します。(混在させません。)…SYS_ARRAY上で別れたのでbreak条件から外す。 201 Map<String,String[]> codeMap = null; 202 for( int i=0; i<len; i++ ) { 203 final String clm = vals[i][CodeData.CLM]; 204 final String code = vals[i][CodeData.CODE]; 205 // final String systemId = vals[i][CodeData.SYSTEM_ID]; 206 final String kbsaku = vals[i][CodeData.KBSAKU]; 207 // if( bkClm == null || !bkClm.equals( clm ) || !bkSystem.equals( systemId ) || !bkKbsaku.equals( kbsaku ) ) { 208 if( bkClm == null || !bkClm.equals( clm ) || !bkKbsaku.equals( kbsaku ) ) { 209 codeMap = new LinkedHashMap<>(); 210 clmMap.put( clm,codeMap ); 211 bkClm = clm; 212 // bkSystem = systemId; 213 bkKbsaku = kbsaku; 214 } 215 216 final String lkey = clm+"."+code; // やっつけ~ 217 // 6.2.0.0 (2015/02/27) 変数使用 218 final LabelData lblData = LABEL_LOADER.getLabelData(lkey); 219 vals[i][CodeData.LNAME] = lblData.getLongLabel(); 220 vals[i][CodeData.SNAME] = lblData.getShortLabel(); 221 vals[i][CodeData.RSNAME] = lblData.getRawShortLabel(); // 4.3.8.0 (2009/08/01) spanが付かない名前短 222 vals[i][CodeData.RLNAME] = lblData.getRawLongLabel(); // 5.6.8.2 (2013/09/01) 加工していない名前長 223 vals[i][CodeData.DESCRIPT] = lblData.getDescription(); // 6.2.0.0 (2015/02/27) 概要説明 224 225 codeMap.put( code,vals[i] ); 226 } 227 } 228 229 // 8.0.0.0 (2021/10/01) 230 for( final Map.Entry<String,Map<String,String[]>> entry : clmMap.entrySet() ) { 231 final String clm = entry.getKey(); 232 final Map<String,String[]> codeMap = entry.getValue(); 233 234 codeDtMap.put( clm,new CodeData( clm,codeMap ) ); 235 } 236 237// final String[] clmKeys = clmMap.keySet().toArray( new String[clmMap.size()] ); 238// final int size = clmKeys.length; 239// for( int i=0; i<size; i++ ) { 240// final String clm = clmKeys[i]; 241// codeMap = clmMap.get( clm ); 242// 243// codeDtMap.put( clm,new CodeData( clm,codeMap ) ); 244// } 245 246// System.out.println( " CodeDataLoader [" + codeDtMap.size() + "] loaded" ); 247 248 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ); 249 buf.append( " " ).append( SYS_ARRAY[0] ).append( " CodeDataLoader [" ).append( selCnt ) 250 .append( "] Map=[" ).append( codeDtMap.size() ).append( "] " ); 251 for( int j=0; j<size; j++ ) { 252 buf.append( SYS_ARRAY[j] ).append( "=[" ).append( cnt[j] ).append( "] " ); 253 } 254 buf.append( "loaded." ); 255 System.out.println( buf ); 256 } 257 258 /** 259 * CodeData オブジェクトを取得します。 260 * 作成したCodeDataオブジェクトは、内部にプールしておき、同じリソース要求が 261 * あったときは、プールの CodeDataを返します。 262 * 263 * @og.rev 4.3.8.0 (2009/08/01) rawShortLabel追加 264 * @og.rev 5.6.8.2 (2013/09/20) rawLongLabel追加 265 * @og.rev 6.2.0.0 (2015/02/27) description 概要説明 追加 266 * @og.rev 7.2.6.0 (2020/06/30) "**"以外にベースとなるSYSTEM_ID(RESOURCE_BASE_SYSTEM_ID)設定の対応 267 * @og.rev 7.3.1.3 (2021/03/09) QUERY文字列を変更。それに伴って、引数の並び順を変更。 268 * @og.rev 8.0.0.0 (2021/10/01) 階層リソースの元となるSYSTEM_IDの配列(前方優先)を使用する。 269 * 270 * @param key コードのキー 271 * 272 * @return CodeDataオブジェクト 273 */ 274 public CodeData getCodeData( final String key ) { 275 CodeData codeData = codeDtMap.get( key ) ; 276 277 if( codeData == null ) { 278 final int size = SYS_ARRAY.length; 279 Map<String,String[]> codeMap = null; 280 for( int j=0; j<size; j++ ) { // SYSTEM_IDの配列(前方優先)で、最初に見つかったキーを採用する。 281 final String sysId = SYS_ARRAY[j]; 282// final String[] args = new String[] { key,BASE_SYS_ID,key,SYSTEM_ID,key }; // 7.3.1.3 (2021/03/09) 283 // 8.5.4.2 (2024/01/12) PMD 7.0.0 UseShortArrayInitializer 284// final String[] args = new String[] { sysId,key }; // 8.0.0.0 (2021/10/01) 285 final String[] args = { sysId,key }; // 8.0.0.0 (2021/10/01) 286 final String[][] vals = DBUtil.dbExecute( QUERY2,args,appInfo,DBID ); // 8.5.4.2 (2024/01/12) PMD 7.0.0 LocalVariableCouldBeFinal 287 288 final int len = vals.length; 289 // String bkSystem = null; // キーブレイク 290 String bkKbsaku = null; 291 // 以下の処理は、SYSTEM_ID違いを塊で処理します。(混在させません。) 292 // Map<String,String[]> codeMap = null; 293 for( int i=0; i<len; i++ ) { 294 // final String systemId = vals[i][CodeData.SYSTEM_ID]; 295 final String code = vals[i][CodeData.CODE]; 296 final String kbsaku = vals[i][CodeData.KBSAKU]; 297 // if( bkSystem == null || !bkSystem.equals( systemId ) || !bkKbsaku.equals( kbsaku ) ) { 298 if( bkKbsaku == null || !bkKbsaku.equals( kbsaku ) ) { 299 codeMap = new LinkedHashMap<>(); 300 // bkSystem = systemId; 301 bkKbsaku = kbsaku; 302 } 303 304 final String lkey = key+"."+code; // やっつけ~ 305 // 6.2.0.0 (2015/02/27) 変数使用 306 final LabelData lblData = LABEL_LOADER.getLabelData(lkey); 307 vals[i][CodeData.LNAME] = lblData.getLongLabel(); 308 vals[i][CodeData.SNAME] = lblData.getShortLabel(); 309 vals[i][CodeData.RSNAME] = lblData.getRawShortLabel(); // 4.3.8.0 (2009/08/01) spanが付かない名前短 310 vals[i][CodeData.RLNAME] = lblData.getRawLongLabel(); // 5.6.8.2 (2013/09/01) 加工していない名前長 311 vals[i][CodeData.DESCRIPT] = lblData.getDescription(); // 6.2.0.0 (2015/02/27) 概要説明 312 313 codeMap.put( code,vals[i] ); 314 } 315 316 if( codeMap != null ) { 317 codeData = new CodeData( key,codeMap ); 318 codeDtMap.put( key,codeData ); 319 break; // 存在すれば、即抜ける(前方優先) 320 } 321 } 322 } 323 return codeData ; 324 } 325 326 /** 327 * CodeData オブジェクトを取得します。 328 * 作成したCodeDataオブジェクトは、内部にプールしておき、同じリソース要求が 329 * あったときは、プールの CodeDataを返します。 330 * 331 * 引数にQUERYを渡すことで、DBから、動的にコードリソースを作成できます。 332 * 引数の順番は、CodeData で定義している CLM,CODE,LNAME,SNAME の順番のままです。 333 * QUERY には、key を引数にとる必要があります。つまり、WHERE CLM = ? の様な記述が必要です。 334 * 335 * @og.rev 5.4.2.2 (2011/12/14) 新規追加。 336 * 337 * @param key コードのキー 338 * @param query 検索SQL(引数に、? を一つ持つ) 339 * 340 * @return CodeDataオブジェクト 341 */ 342 public CodeData getCodeData( final String key,final String query ) { 343 CodeData codeData = codeDtMap.get( key ) ; 344 345 if( codeData == null ) { 346 // 8.5.4.2 (2024/01/12) PMD 7.0.0 UseShortArrayInitializer 347// final String[] args = new String[] { key }; 348 final String[] args = { key }; 349 final String[][] vals = DBUtil.dbExecute( query,args,appInfo,DBID ); 350 351 final int len = vals.length; 352 final Map<String,String[]> codeMap = new LinkedHashMap<>(); 353 for( int i=0; i<len; i++ ) { 354 // 8.5.4.2 (2024/01/12) PMD 7.0.0 LocalVariableCouldBeFinal 355 final String[] cdVals = new String[CodeData.DATA_SIZE]; // 7.2.6.0 (2020/06/30) キーワード変更 空の配列を毎回作成 356 357 final String code = vals[i][CodeData.CODE]; 358 359 cdVals[CodeData.CLM] = key ; 360 cdVals[CodeData.CODE] = code; 361 cdVals[CodeData.LNAME] = vals[i][CodeData.LNAME]; 362 cdVals[CodeData.SNAME] = vals[i][CodeData.SNAME]; 363 364 codeMap.put( code,cdVals ); 365 } 366 367 if( ! codeMap.isEmpty() ) { 368 codeData = new CodeData( key,codeMap ); 369 codeDtMap.put( key,codeData ); 370 } 371 } 372 return codeData ; 373 } 374 375 /** 376 * CodeData オブジェクトのキャッシュを個別にクリアします。 377 * リソースデータの更新など、一部分の更新時に、すべてのキャッシュを 378 * 破棄するのではなく、指定の分のみ破棄できる機能です。 379 * 380 * @og.rev 4.0.2.0 (2007/12/25) コードリソースクリア時に対応するラベルリソースもクリアする。 381 * 382 * @param key コードのキー 383 */ 384 public void clear( final String key ) { 385 386 // 4.0.2.0 (2007/12/25) 387 final CodeData cdata = codeDtMap.remove( key ); 388 if( cdata != null ) { 389 final String clm = cdata.getColumn(); 390 for( int i=0; i<cdata.getSize(); i++ ) { 391 LABEL_LOADER.clear( clm + '.' + cdata.getCodeKey( i ) ); 392 } 393 } 394 } 395 396 /** 397 * CodeData オブジェクトのキャッシュをクリアして、再作成します。 398 * 399 */ 400 public void clear() { 401 codeDtMap.clear(); 402 } 403}