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; 021import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE; // 8.0.0.0 (2021/10/01) 022 023import java.util.Map; 024import java.util.HashMap; 025import java.util.LinkedHashMap ; 026import java.util.WeakHashMap ; 027import java.util.Collections ; 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 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 final String[] args = new String[] { sysId }; // 8.0.0.0 (2021/10/01) 187 188 String[][] vals = DBUtil.dbExecute( QUERY,args,appInfo,DBID ); 189 190// final Map<String,Map<String,String[]>> clmMap = new HashMap<>(); 191 final int len = vals.length; 192 selCnt += len; 193 cnt[j] = len ; 194 195 String bkClm = null; // キーブレイク 196 // String bkSystem = null; 197 String bkKbsaku = null; 198 // 以下の処理は、SYSTEM_ID違いを塊で処理します。(混在させません。)…SYS_ARRAY上で別れたのでbreak条件から外す。 199 Map<String,String[]> codeMap = null; 200 for( int i=0; i<len; i++ ) { 201 final String clm = vals[i][CodeData.CLM]; 202 final String code = vals[i][CodeData.CODE]; 203 // final String systemId = vals[i][CodeData.SYSTEM_ID]; 204 final String kbsaku = vals[i][CodeData.KBSAKU]; 205 // if( bkClm == null || !bkClm.equals( clm ) || !bkSystem.equals( systemId ) || !bkKbsaku.equals( kbsaku ) ) { 206 if( bkClm == null || !bkClm.equals( clm ) || !bkKbsaku.equals( kbsaku ) ) { 207 codeMap = new LinkedHashMap<>(); 208 clmMap.put( clm,codeMap ); 209 bkClm = clm; 210 // bkSystem = systemId; 211 bkKbsaku = kbsaku; 212 } 213 214 final String lkey = clm+"."+code; // やっつけ~ 215 // 6.2.0.0 (2015/02/27) 変数使用 216 final LabelData lblData = LABEL_LOADER.getLabelData(lkey); 217 vals[i][CodeData.LNAME] = lblData.getLongLabel(); 218 vals[i][CodeData.SNAME] = lblData.getShortLabel(); 219 vals[i][CodeData.RSNAME] = lblData.getRawShortLabel(); // 4.3.8.0 (2009/08/01) spanが付かない名前短 220 vals[i][CodeData.RLNAME] = lblData.getRawLongLabel(); // 5.6.8.2 (2013/09/01) 加工していない名前長 221 vals[i][CodeData.DESCRIPT] = lblData.getDescription(); // 6.2.0.0 (2015/02/27) 概要説明 222 223 codeMap.put( code,vals[i] ); 224 } 225 } 226 227 // 8.0.0.0 (2021/10/01) 228 for( final Map.Entry<String,Map<String,String[]>> entry : clmMap.entrySet() ) { 229 final String clm = entry.getKey(); 230 final Map<String,String[]> codeMap = entry.getValue(); 231 232 codeDtMap.put( clm,new CodeData( clm,codeMap ) ); 233 } 234 235// final String[] clmKeys = clmMap.keySet().toArray( new String[clmMap.size()] ); 236// final int size = clmKeys.length; 237// for( int i=0; i<size; i++ ) { 238// final String clm = clmKeys[i]; 239// codeMap = clmMap.get( clm ); 240// 241// codeDtMap.put( clm,new CodeData( clm,codeMap ) ); 242// } 243 244// System.out.println( " CodeDataLoader [" + codeDtMap.size() + "] loaded" ); 245 246 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ); 247 buf.append( " " ).append( SYS_ARRAY[0] ).append( " CodeDataLoader [" ).append( selCnt ) 248 .append( "] Map=[" ).append( codeDtMap.size() ).append( "] " ); 249 for( int j=0; j<size; j++ ) { 250 buf.append( SYS_ARRAY[j] ).append( "=[" ).append( cnt[j] ).append( "] " ); 251 } 252 buf.append( "loaded." ); 253 System.out.println( buf ); 254 } 255 256 /** 257 * CodeData オブジェクトを取得します。 258 * 作成したCodeDataオブジェクトは,内部にプールしておき,同じリソース要求が 259 * あったときは,プールの CodeDataを返します。 260 * 261 * @og.rev 4.3.8.0 (2009/08/01) rawShortLabel追加 262 * @og.rev 5.6.8.2 (2013/09/20) rawLongLabel追加 263 * @og.rev 6.2.0.0 (2015/02/27) description 概要説明 追加 264 * @og.rev 7.2.6.0 (2020/06/30) "**"以外にベースとなるSYSTEM_ID(RESOURCE_BASE_SYSTEM_ID)設定の対応 265 * @og.rev 7.3.1.3 (2021/03/09) QUERY文字列を変更。それに伴って、引数の並び順を変更。 266 * @og.rev 8.0.0.0 (2021/10/01) 階層リソースの元となるSYSTEM_IDの配列(前方優先)を使用する。 267 * 268 * @param key コードのキー 269 * 270 * @return CodeDataオブジェクト 271 */ 272 public CodeData getCodeData( final String key ) { 273 CodeData codeData = codeDtMap.get( key ) ; 274 275 if( codeData == null ) { 276 final int size = SYS_ARRAY.length; 277 Map<String,String[]> codeMap = null; 278 for( int j=0; j<size; j++ ) { // SYSTEM_IDの配列(前方優先)で、最初に見つかったキーを採用する。 279 final String sysId = SYS_ARRAY[j]; 280// final String[] args = new String[] { key,BASE_SYS_ID,key,SYSTEM_ID,key }; // 7.3.1.3 (2021/03/09) 281 final String[] args = new String[] { sysId,key }; // 8.0.0.0 (2021/10/01) 282 String[][] vals = DBUtil.dbExecute( QUERY2,args,appInfo,DBID ); 283 284 final int len = vals.length; 285 // String bkSystem = null; // キーブレイク 286 String bkKbsaku = null; 287 // 以下の処理は、SYSTEM_ID違いを塊で処理します。(混在させません。) 288 // Map<String,String[]> codeMap = null; 289 for( int i=0; i<len; i++ ) { 290 // final String systemId = vals[i][CodeData.SYSTEM_ID]; 291 final String code = vals[i][CodeData.CODE]; 292 final String kbsaku = vals[i][CodeData.KBSAKU]; 293 // if( bkSystem == null || !bkSystem.equals( systemId ) || !bkKbsaku.equals( kbsaku ) ) { 294 if( bkKbsaku == null || !bkKbsaku.equals( kbsaku ) ) { 295 codeMap = new LinkedHashMap<>(); 296 // bkSystem = systemId; 297 bkKbsaku = kbsaku; 298 } 299 300 final String lkey = key+"."+code; // やっつけ~ 301 // 6.2.0.0 (2015/02/27) 変数使用 302 final LabelData lblData = LABEL_LOADER.getLabelData(lkey); 303 vals[i][CodeData.LNAME] = lblData.getLongLabel(); 304 vals[i][CodeData.SNAME] = lblData.getShortLabel(); 305 vals[i][CodeData.RSNAME] = lblData.getRawShortLabel(); // 4.3.8.0 (2009/08/01) spanが付かない名前短 306 vals[i][CodeData.RLNAME] = lblData.getRawLongLabel(); // 5.6.8.2 (2013/09/01) 加工していない名前長 307 vals[i][CodeData.DESCRIPT] = lblData.getDescription(); // 6.2.0.0 (2015/02/27) 概要説明 308 309 codeMap.put( code,vals[i] ); 310 } 311 312 if( codeMap != null ) { 313 codeData = new CodeData( key,codeMap ); 314 codeDtMap.put( key,codeData ); 315 break; // 存在すれば、即抜ける(前方優先) 316 } 317 } 318 } 319 return codeData ; 320 } 321 322 /** 323 * CodeData オブジェクトを取得します。 324 * 作成したCodeDataオブジェクトは,内部にプールしておき,同じリソース要求が 325 * あったときは,プールの CodeDataを返します。 326 * 327 * 引数にQUERYを渡すことで、DBから、動的にコードリソースを作成できます。 328 * 引数の順番は、CodeData で定義している CLM,CODE,LNAME,SNAME の順番のままです。 329 * QUERY には、key を引数にとる必要があります。つまり、WHERE CLM = ? の様な記述が必要です。 330 * 331 * @og.rev 5.4.2.2 (2011/12/14) 新規追加。 332 * 333 * @param key コードのキー 334 * @param query 検索SQL(引数に、? を一つ持つ) 335 * 336 * @return CodeDataオブジェクト 337 */ 338 public CodeData getCodeData( final String key,final String query ) { 339 CodeData codeData = codeDtMap.get( key ) ; 340 341 if( codeData == null ) { 342 final String[] args = new String[] { key }; 343 final String[][] vals = DBUtil.dbExecute( query,args,appInfo,DBID ); 344 345 final int len = vals.length; 346 final Map<String,String[]> codeMap = new LinkedHashMap<>(); 347 for( int i=0; i<len; i++ ) { 348 String[] cdVals = new String[CodeData.DATA_SIZE]; // 7.2.6.0 (2020/06/30) キーワード変更 空の配列を毎回作成 349 350 final String code = vals[i][CodeData.CODE]; 351 352 cdVals[CodeData.CLM] = key ; 353 cdVals[CodeData.CODE] = code; 354 cdVals[CodeData.LNAME] = vals[i][CodeData.LNAME]; 355 cdVals[CodeData.SNAME] = vals[i][CodeData.SNAME]; 356 357 codeMap.put( code,cdVals ); 358 } 359 360 if( ! codeMap.isEmpty() ) { 361 codeData = new CodeData( key,codeMap ); 362 codeDtMap.put( key,codeData ); 363 } 364 } 365 return codeData ; 366 } 367 368 /** 369 * CodeData オブジェクトのキャッシュを個別にクリアします。 370 * リソースデータの更新など、一部分の更新時に、すべてのキャッシュを 371 * 破棄するのではなく、指定の分のみ破棄できる機能です。 372 * 373 * @og.rev 4.0.2.0 (2007/12/25) コードリソースクリア時に対応するラベルリソースもクリアする。 374 * 375 * @param key コードのキー 376 */ 377 public void clear( final String key ) { 378 379 // 4.0.2.0 (2007/12/25) 380 final CodeData cdata = codeDtMap.remove( key ); 381 if( cdata != null ) { 382 final String clm = cdata.getColumn(); 383 for( int i=0; i<cdata.getSize(); i++ ) { 384 LABEL_LOADER.clear( clm + '.' + cdata.getCodeKey( i ) ); 385 } 386 } 387 } 388 389 /** 390 * CodeData オブジェクトのキャッシュをクリアして、再作成します。 391 * 392 */ 393 public void clear() { 394 codeDtMap.clear(); 395 } 396}