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.HashMap; 019import java.util.Map; 020import java.util.WeakHashMap; 021import java.util.Collections ; 022 023import org.opengion.hayabusa.common.HybsSystem; 024import org.opengion.hayabusa.common.HybsSystemException; 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 * systemId と lang に対応したラベルデータを作成するデータロードクラスです。 031 * 032 * ラベルデータは、項目(CLM)に対して、各種ラベル情報を持っています。 033 * ラベルデータは、名前(ORG)と名前(短)と名前(長)を持っています。従来のラベルは、表示名称と 034 * して、一種類しか持っていませんでした。 035 * 名前(ORG)は、従来の表示名称にあたります。これは、一般的なラベルとして 036 * 使用されます。名前(短)は、テーブル一覧のヘッダーの様に、特殊なケースで、 037 * 簡略化された名称を使用するときに利用されます。この切り替えは、自動で判断されます。 038 * 名前(短)に、なにも設定されていない場合は、名前(長)が自動的に使用されますので 039 * 初期データ移行時には、そのまま、通常時もテーブルヘッダー時も同じ文字列が 040 * 使用されます。 041 * 名前(短)と名前(長)は、コメント情報が存在する場合は、Tips表示を行います。 042 * 043 * ラベルデータを作成する場合は、同一ラベルで、作成区分(KBSAKU)違いの場合は、 044 * 最も大きな作成区分を持つコードを使用します。 045 * 作成区分(KBSAKU)='0' のデータは、マスタリソースとして、エンジンとともに 046 * 配布されるリソースになります。 047 * 048 * 読込フラグ(FGLOAD)='1'のラベルリソースは、このLabelDataLoaderオブジェクトが 049 * 構築された時に、すべてキャッシュとして内部メモリに読み取ります。 050 * 読込フラグが、'1' 以外のデータは、初期起動時には、メモリにキャッシュされず 051 * 実際に使用されるまで、オブジェクトが作成されません。 052 * これは、使用されるかどうか判らないラベルデータを、予め作成しないことで、メモリの 053 * 節約を図っています。 054 * ただし、リソースのキャッシュに、WeakHashMap クラスを使用しているため、 055 * メモリオーバー時には、クリアされるため、単独での読み取りも行います。 056 * 057 * SYSTEM_ID='**' は、共通リソースです。 058 * これは、システム間で共通に使用されるリソース情報を登録しておきます。 059 * 060 * @og.rev 4.0.0.0 (2004/12/31) 新規作成 061 * @og.group リソース管理 062 * 063 * @version 4.0 064 * @author Kazuhiko Hasegawa 065 * @since JDK5.0, 066 */ 067final class LabelDataLoader { 068 // リソースの接続先を、取得します。 069 private final String DBID = HybsSystem.sys( "RESOURCE_DBID" ); 070 071 // DBリソースの初期一括読み込みのクエリー 072 073// // 7.3.1.3 (2021/03/09) 074// private static final String SEL_CLM = "select CLM,SNAME,LNAME,DESCRIPTION,FGLOAD,UNIQ,SYSTEM_ID" 075// + ",KBSAKU" ; 076 077 // 注意:LANGを unionする前に条件として入れたのでパラメータの順番が変わる。 078 // 7.3.1.3 (2021/03/09) 079 // 7.4.5.0 (2021/08/31) Firebird 対応 080// private static final String QUERY = "select a.* from (" 081// + SEL_CLM + ",0 as SNO" 082// + " from GEA08 where SYSTEM_ID='**' and LANG=? and FGJ='1'" // エンジン共通 083// + " union all " 084// + SEL_CLM + ",1 as SNO" 085// + " from GEA08 where SYSTEM_ID=? and LANG=? and FGJ='1'" // RESOURCE_BASE_SYSTEM_ID 086// + " union all " 087// + SEL_CLM + ",2 as SNO" 088// + " from GEA08 where SYSTEM_ID=? and LANG=? and FGJ='1'" // 最上位ののSYSTEM_ID 089// + " ) a " // 8.0.0.0 (2021/08/31) 090// + " order by a.SNO,a.KBSAKU,a.CLM" ; 091 092 /** 8.0.0.0 (2021/10/01) RESOURCE_BASE_SYSTEM_ID は、SYSTEM_IDの配列で複数指定できる。 */ 093 private static final String QUERY = "select CLM,SNAME,LNAME,DESCRIPTION,FGLOAD,UNIQ,SYSTEM_ID,KBSAKU" 094 + " from GEA08 where SYSTEM_ID=? and LANG=? and FGJ='1'" // バインド変数 SYSTEM_ID=? and LANG=? 095 + " order by KBSAKU,CLM" ; 096 097 // DBリソースの個別読み込み時のクエリー 098 // 注意:LANGとCLMを unionする前に条件として入れたのでパラメータの順番が変わる。 099 // 7.3.1.3 (2021/03/09) 100 // 7.4.5.0 (2021/08/31) Firebird 対応 101// private static final String QUERY2 = "select a.* from (" 102// + SEL_CLM + ",0 as SNO" 103// + " from GEA08 where SYSTEM_ID='**' and LANG=? and CLM=? and FGJ='1'" // エンジン共通 104// + " union all " 105// + SEL_CLM + ",1 as SNO" 106// + " from GEA08 where SYSTEM_ID=? and LANG=? and CLM=? and FGJ='1'" // RESOURCE_BASE_SYSTEM_ID 107// + " union all " 108// + SEL_CLM + ",2 as SNO" 109// + " from GEA08 where SYSTEM_ID=? and LANG=? and CLM=? and FGJ='1'" // 最上位ののSYSTEM_ID 110// + " ) a " // 8.0.0.0 (2021/08/31) 111// + " order by a.SNO,a.KBSAKU" ; 112 113 /** 8.0.0.0 (2021/10/01) RESOURCE_BASE_SYSTEM_ID は、SYSTEM_IDの配列で複数指定できる。 */ 114 private static final String QUERY2 = "select CLM,SNAME,LNAME,DESCRIPTION,FGLOAD,UNIQ,SYSTEM_ID,KBSAKU" 115 + " from GEA08 where SYSTEM_ID=? and LANG=? and CLM=? and FGJ='1'" // バインド変数 SYSTEM_ID=? and LANG=? and CLM=? 116 + " order by KBSAKU DESC" ; // 逆順で検索し、先頭採用 117 118 /** 6.3.1.1 (2015/07/10) 読込フラグ(FGLOAD) のマーカー設定追加。 */ 119 private static final boolean IS_FGLOAD_AUTOSET = HybsSystem.sysBool( "USE_FGLOAD_AUTOSET" ); // 6.4.1.1 (2016/01/16) useFgloadAutoset → IS_FGLOAD_AUTOSET refactoring 120 121 /** 6.3.1.1 (2015/07/10) FGLOAD更新(UNIQ だけで指定可能だが、万一を想定して、SYSTEM_IDとCLMを条件に追記) */ 122 // 7.2.6.0 (2020/06/30) "**"以外にベースとなるSYSTEM_ID(RESOURCE_BASE_SYSTEM_ID)設定の対応したため、where条件から、SYSTEM_ID は削除します。 123 private static final String UPDATE2 = "update GEA08 set FGLOAD='2' where UNIQ=? and CLM=?"; 124 125 /** 6.4.3.1 (2016/02/12) Collections.synchronizedMap で同期処理を行います。 */ 126 private final Map<String,LabelData> labelMap = Collections.synchronizedMap( new WeakHashMap<>() ); // キャッシュ用プール 127 // 8.0.0.0 (2021/10/01) RESOURCE_BASE_SYSTEM_ID は、SYSTEM_IDの配列で複数指定できる。 128// private final String SYSTEM_ID ; // システムID 129// private final String BASE_SYS_ID ; // 7.2.9.2 (2020/10/30) ベースシステムID 130 private final String[] SYS_ARRAY; // 8.0.0.0 (2021/10/01) 131 private final String LANG ; // 言語 132 133 /** コネクションにアプリケーション情報を追記するかどうか指定 */ 134 public static final boolean USE_DB_APPLICATION_INFO = HybsSystem.sysBool( "USE_DB_APPLICATION_INFO" ) ; 135 136 /** 3.8.7.0 (2006/12/15) アクセスログ取得の為、ApplicationInfoオブジェクトを設定 */ 137 private final ApplicationInfo appInfo; 138 139 /** 140 * SystemId と lang 毎に ファクトリオブジェクトを作成します。 141 * 142 * @og.rev 7.2.9.2 (2020/10/30) ベースとなるSYSTEM_ID(RESOURCE_BASE_SYSTEM_ID)の取得 143 * @og.rev 8.0.0.0 (2021/10/01) RESOURCE_BASE_SYSTEM_ID は、SYSTEM_IDの配列で複数指定できる。 144 * 145// * @param systemId システムID 146// * @param baseSys ベースとなるSYSTEM_ID 147 * @param sysAry 階層リソースの元となるSYSTEM_IDの配列(前方優先) 148 * @param lng 言語 149 * @param initLoad リソースデータの先読み可否(true:先読みする) 150 */ 151// LabelDataLoader( final String systemId,final String baseSys,final String lang,final boolean initLoad ) { 152 /* default */ LabelDataLoader( final String[] sysAry,final String lng,final boolean initLoad ) { 153// SYSTEM_ID = systemId; 154// BASE_SYS_ID = baseSys ; // 7.2.9.2 (2020/10/30) 155 SYS_ARRAY = sysAry ; // 8.0.0.0 (2021/10/01) 156 LANG = lng; 157 158 // 3.8.7.0 (2006/12/15) アクセスログ取得の為、ApplicationInfoオブジェクトを設定 159 if( USE_DB_APPLICATION_INFO ) { 160 appInfo = new ApplicationInfo(); 161 // ユーザーID,IPアドレス,ホスト名 162// appInfo.setClientInfo( SYSTEM_ID,HybsSystem.HOST_ADRS,HybsSystem.HOST_NAME ); 163 appInfo.setClientInfo( SYS_ARRAY[0],HybsSystem.HOST_ADRS,HybsSystem.HOST_NAME ); 164 // 画面ID,操作,プログラムID 165 appInfo.setModuleInfo( "LabelDataLoader",null,null ); 166 } 167 else { 168 appInfo = null; 169 } 170 171 // ApplicationInfo の設定が終わってから実行します。 172 if( initLoad ) { loadDBResource(); } 173 } 174 175 /** 176 * DBリソースより ラベルデータを取得、設定します。 177 * 取得データは、CLM,SNAME,LNAME,DESCRIPTION です。 178 * 179 * ※ 以下のロジックは、後方優先であり、SYSTEM_IDの配列は前方優先なので逆順で回します。 180 * 181 * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為、ApplicationInfoオブジェクトを設定 182 * @og.rev 4.3.5.7 (2009/03/22) FGLOADの影響でシステム個別リソースが読まれない問題対応 183 * @og.rev 7.0.7.0 (2019/12/13) 読み取り件数の評価を、破棄分も考慮する。 184 * @og.rev 7.2.6.0 (2020/06/30) "**"以外にベースとなるSYSTEM_ID(RESOURCE_BASE_SYSTEM_ID)設定の対応 185 * @og.rev 7.2.8.0 (2020/09/04) "**"以外にベースとなるSYSTEM_ID(RESOURCE_BASE_SYSTEM_ID)設定の対応。SQL文手直し 186 * @og.rev 7.3.1.3 (2021/03/09) QUERY文字列を変更。それに伴って、引数の並び順を変更。 187 * @og.rev 8.0.0.0 (2021/10/01) 階層リソースの元となるSYSTEM_IDの配列(前方優先)を使用する。 188 */ 189 private void loadDBResource() { 190 final int size = SYS_ARRAY.length; 191 192 final int[] cnt = new int[size]; // 各SYSTEM_ID の個数 193 int selCnt = 0; 194 195 for( int j=size-1; j>=0; j-- ) { // SYSTEM_IDの配列は、前方優先なので、逆順で回す必要がある。 196 final String sysId = SYS_ARRAY[j]; 197 198// final String[] args = new String[] { LANG,BASE_SYS_ID,LANG,SYSTEM_ID,LANG }; // 7.3.1.3 (2021/03/09) 199 // 8.5.4.2 (2024/01/12) PMD 7.0.0 UseShortArrayInitializer 200// final String[] args = new String[] { sysId,LANG }; // 8.0.0.0 (2021/10/01) 201 final String[] args = { sysId,LANG }; // 8.0.0.0 (2021/10/01) 202 203 final String[][] vals = DBUtil.dbExecute( QUERY,args,appInfo,DBID ); 204// final int[] cnt = new int[3]; // **,BASE_SYS_ID,SYSTEM_ID の個数 205 206 final int len = vals.length; 207 selCnt += len; 208 for( int i=0; i<len; i++ ) { 209 final String clm = vals[i][0]; 210// final int idx = Integer.parseInt( vals[i][LabelData.SNO] ); 211 212 if( "1".equals( vals[i][LabelData.FG_LOAD] ) ){ // 4.3.5.7 (2009/03/22) 213 labelMap.put( clm,new LabelData( vals[i] ) ); 214// cnt[idx]++ ; 215 cnt[j]++ ; 216 } 217 // より上の作成区分で、FGLOAD='1'(一括読込)以外の場合は、破棄する。 218 else if( labelMap.get( clm ) != null ){ 219 labelMap.remove( clm ); 220 } 221 } 222 } 223 224 // 7.0.7.0 (2019/12/13) 読み取り件数の評価を、破棄分も考慮する。 225// System.out.println( " LabelDataLoader [" + len + "] select [" + labelMap.size() + "] " 226// + " ** [" + cnt[0] + "] " + BASE_SYS_ID + " [" + cnt[1] + "] " + SYSTEM_ID + " [" + cnt[2] + "] loaded" ); 227 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ); 228 buf.append( " " ).append( SYS_ARRAY[0] ).append( " LabelDataLoader [" ).append( selCnt ) 229 .append( "] Map=[" ).append( labelMap.size() ).append( "] " ); 230 for( int j=0; j<size; j++ ) { 231 buf.append( SYS_ARRAY[j] ).append( "=[" ).append( cnt[j] ).append( "] " ); 232 } 233 buf.append( "loaded." ); 234 System.out.println( buf ); 235 } 236 237 /** 238 * LabelData オブジェクトを取得します。 239 * 作成したLabelDataオブジェクトは、内部にプールしておき、同じリソース要求が 240 * あったときは、プールの LabelDataを返します。 241 * リソースDBに存在しない場合は、NULLラベルを作成します。このオブジェクトも 242 * キャッシュします。 243 * 読込フラグ(FGLOAD)が '1' のデータは、起動時に先読みします。 244 * それ以外のデータは、ここでキー要求が発生した時点で読み込みます。 245 * 読込フラグ(FGLOAD) のマーカー設定モード(USE_FGLOAD_AUTOSET)を使用する(true)場合は、 246 * 追加読み込み(先読みされていないカラム)に対して、読込フラグ(FGLOAD)を 2:使用実績 に 247 * 設定します。(次回起動時の、初期読み込みは行いません。) 248 * 249 * ※ 以下のロジックは、先に見つかった値を返すので、前方優先で検索します。 250 * 251 * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為、ApplicationInfoオブジェクトを設定 252 * @og.rev 6.3.1.1 (2015/07/10) 読込フラグ(FGLOAD) のマーカー設定追加。 253 * @og.rev 7.2.6.0 (2020/06/30) "**"以外にベースとなるSYSTEM_ID(RESOURCE_BASE_SYSTEM_ID)設定の対応 254 * @og.rev 7.2.8.0 (2020/09/04) "**"以外にベースとなるSYSTEM_ID(RESOURCE_BASE_SYSTEM_ID)設定の対応。SQL文手直し 255 * @og.rev 7.3.1.3 (2021/03/09) QUERY文字列を変更。それに伴って、引数の並び順を変更。 256 * 257 * @param key ラベルのキー 258 * 259 * @return LabelDataオブジェクト 260 * @og.rtnNotNull 261 */ 262 public LabelData getLabelData( final String key ) { 263 LabelData label = labelMap.get( key ) ; 264 265 if( label == null ) { 266 final int size = SYS_ARRAY.length; 267 for( int j=0; j<size; j++ ) { // SYSTEM_IDの配列(前方優先)で、最初に見つかったキーを採用する。 268 final String sysId = SYS_ARRAY[j]; 269 270// final String[] args = new String[] { LANG,key,BASE_SYS_ID,LANG,key,SYSTEM_ID,LANG,key }; // 7.3.1.3 (2021/03/09) 271 // 8.5.4.2 (2024/01/12) PMD 7.0.0 UseShortArrayInitializer 272// final String[] args = new String[] { sysId,LANG,key }; // 8.0.0.0 (2021/10/01) 273 final String[] args = { sysId,LANG,key }; // 8.0.0.0 (2021/10/01) 274 final String[][] vals = DBUtil.dbExecute( QUERY2,args,appInfo,DBID ); // SYSTEM_ID='**' も含む 275 276 if( vals.length > 0 ) { 277// final int row=vals.length-1; // 最後の検索結果 278 final int row=0 ; // 最初の検索結果が有効 279 label = new LabelData( vals[row] ); // 最初のデータ 280 281 // 6.3.1.1 (2015/07/10) 読込フラグ(FGLOAD) のマーカー設定追加。 282 if( IS_FGLOAD_AUTOSET ) { 283 // 1:一括読込 と、2:使用実績 以外のリソースは、2:使用実績 をセットする。(SYSTEM_ID='**'は含まない) 284 final String fgld = vals[row][LabelData.FG_LOAD]; 285 final String sysld = vals[row][LabelData.SYSTEM_ID]; 286 if( !"1".equals( fgld ) && !"2".equals( fgld ) && !"**".equals( sysld ) ) { 287 // 7.2.6.0 (2020/06/30) RESOURCE_BASE_SYSTEM_ID 追加したため、where条件から、SYSTEM_ID は削除します。 288 // 8.5.4.2 (2024/01/12) PMD 7.0.0 UseShortArrayInitializer 289// final String[] args2 = new String[] { vals[row][LabelData.UNIQ],key }; 290 final String[] args2 = { vals[row][LabelData.UNIQ],key }; 291 DBUtil.dbExecute( UPDATE2,args2,appInfo,DBID ); // FGLOAD を、2:使用実績 にセット 292 } 293 } 294 break; // 8.0.0.0 (2021/10/01) 逆順検索しているので、最初の検索結果が有効 295 } 296// else { 297// label = new LabelData( key ); // null ラベル 298// } 299// labelMap.put( key,label ); 300 } 301 // この段階でも見つからない場合は、key ラベルを生成する。 302 if( label == null ) { label = new LabelData( key ); } 303 labelMap.put( key,label ); 304 } 305 306 return label ; 307 } 308 309 /** 310 * 指定されたクエリを発行し、ラベルマップを作成します。 311 * 312 * ここで作成されたラベル情報は、内部的にキャッシュされません。 313 * 各画面で一時的にラベル情報を追加したい場合に使用します。 314 * 315 * 発行するクエリでは、第1カラムにラベルキーを、第2カラムにラベル名称を設定します。 316 * 第3カラムが存在する場合は、名称(短)として使用されます。(必須ではありません) 317 * クエリが指定されていない又は、検索したカラム数が1以下の場合はエラーを返します。 318 * 319 * @og.rev 4.3.4.0 (2008/12/01) 新規作成 320 * @og.rev 6.4.0.5 (2016/01/09) useLabelMap="true" 時のSQL文の実行は、dbid を使用して行う。 321 * 322 * @param query ラベルマップを作成するクエリ 323 * @param dbid 接続先ID 324 * 325 * @return ラベルマップ 326 */ 327 public Map<String, LabelData> getLabelMap( final String query , final String dbid ) { 328 if( query == null || query.isEmpty() ) { 329 final String errMsg = "ラベルを取得するクエリが指定されていません。"; 330 throw new HybsSystemException( errMsg ); 331 } 332 333// final String[][] rtn = DBUtil.dbExecute( query, new String[0], appInfo, dbid ); // 6.4.0.5 (2016/01/09) 334 final String[][] rtn = DBUtil.dbExecute( query, null, appInfo, dbid ); // 8.0.2.1 (2021/12/10) 335 if( rtn == null || rtn.length == 0 ) { // データが存在しない場合はそのまま終了します。 336 return null; 337 } 338 339 final int confSize = rtn[0].length; 340 if( confSize < 2 ) { 341 final String errMsg = "ラベルキー、ラベル名称の指定は必須です。" 342 + " SQL=" + query ; // 5.1.8.0 (2010/07/01) errMsg 修正 343 throw new HybsSystemException( errMsg ); 344 } 345 346 // 6.4.4.1 (2016/03/18) 変数名がややこしいので、変更します。 347 final Map<String, LabelData> lblMap = new HashMap<>(); 348 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ForLoopCanBeForeach 349// for( int i=0; i<rtn.length; i++ ) { 350// final String[] ldconf = new String[5]; // 8.5.4.2 (2024/01/12) PMD 7.0.0 LocalVariableCouldBeFinal 351// ldconf[0] = rtn[i][0]; // CLM 352// ldconf[1] = ( confSize == 2 ? rtn[i][1] : rtn[i][2] ); // SNAME 353// ldconf[2] = rtn[i][1]; // LNAME 354// ldconf[3] = ""; 355// ldconf[4] = ""; 356// 357// lblMap.put( rtn[i][0], new LabelData( ldconf ) ); 358// } 359 for( final String[] clms : rtn ) { 360 final String[] ldconf = new String[5]; // 8.5.4.2 (2024/01/12) PMD 7.0.0 LocalVariableCouldBeFinal 361 ldconf[0] = clms[0]; // CLM 362 ldconf[1] = ( confSize == 2 ? clms[1] : clms[2] ); // SNAME 363 ldconf[2] = clms[1]; // LNAME 364 ldconf[3] = ""; 365 ldconf[4] = ""; 366 367 lblMap.put( ldconf[0], new LabelData( ldconf ) ); 368 } 369 return lblMap; 370 } 371 372 /** 373 * LabelData オブジェクトのキャッシュを個別にクリアします。 374 * リソースデータの更新など、一部分の更新時に、すべてのキャッシュを 375 * 破棄するのではなく、指定の分のみ破棄できる機能です。 376 * 377 * @param key ラベルのキー 378 */ 379 public void clear( final String key ) { 380 labelMap.remove( key ); 381 } 382 383 /** 384 * LabelData オブジェクトのキャッシュをクリアします。 385 * 386 */ 387 public void clear() { 388 labelMap.clear(); 389 } 390}