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.plugin.daemon; 017 018import java.util.ArrayList; 019import java.util.Date; 020import java.util.HashMap; 021import java.util.LinkedHashMap; 022import java.util.LinkedHashSet; 023import java.util.List; 024import java.util.Map; 025import java.util.Set; 026 027import org.opengion.fukurou.db.DBUtil; 028import org.opengion.fukurou.db.Transaction; 029import org.opengion.fukurou.db.TransactionReal; 030import org.opengion.fukurou.transfer.TransferConfig; 031import org.opengion.fukurou.transfer.TransferExec; 032import org.opengion.fukurou.util.ApplicationInfo; 033import org.opengion.fukurou.util.LogWriter; 034import org.opengion.fukurou.util.StringUtil; 035import org.opengion.hayabusa.common.HybsSystem; 036import org.opengion.hayabusa.common.HybsSystemException; 037 038/** 039 * 【伝送システム】旧伝送DB(CB01)を監視して、実行方法に応じた処理プログラムを呼び出します。 040 * 041 * このデーモンは、伝送定義マスタの読取方法が、旧伝送DB読取(CB01)の定義を対象として実行されます。 042 * 読取対象は、旧伝送DB(CB01)で、データコード、送り先、テキスト種別、状況='1'を条件に読み込まれます。 043 * 伝送定義マスタ上では、読取対象にて、以下の形式で定義する必要があります。 044 * (データコード) (送り先) (テキスト種別) 例):"3 D9 B119" 045 * 処理実行後は、読み取ったヘッダーデータの状況を'2'に更新します。 046 * 但し、読取パラメーターに"NOUPDATE"を指定した場合、処理後の更新は行われません。 047 * また、エラーが発生した場合はヘッダーデータの状況を'9'に更新します。 048 * 049 * トランザクションは、読取対象の単位になります。 050 * 同じ読取対象で、異なる実行方法、実行対象を定義した場合、同じデータに対して複数回処理が行われます。 051 * しかし、この場合においても、トランザクションは読取対象の単位で生成されるため、複数回の処理の内、 052 * 1回でもエラーが発生した場合は、同じ読取対象でそれまでに処理した分についてもrollbackされます。 053 * 054 * また、この伝送デーモン(読取方法)は、旧伝送DB(CB01)に対するクエリ回数を減らすため、旧伝送DB(CB01)と 055 * 伝送定義マスタ(GE62)をJOINして一括でデータを取得しています。 056 * このため、他の伝送デーモン(読取方法)とは読取部分の実装方法が異なっています。 057 * 具体的には、{@link org.opengion.fukurou.transfer.TransferRead}インターフェースを利用せずに、 058 * このデーモン自体に読取及びステータス更新の処理を実装しています。 059 * 060 * ※処理中に何らかのエラーが1度でも発生した場合、このデーモンは停止します。 061 * 062 * このクラスは、HybsTimerTask を継承した タイマータスククラスです。 063 * startDaemon() がタイマータスクによって、呼び出されます。 064 * 065 * @og.rev 5.4.1.0 (2011/11/01) 伝送システム対応 066 * @og.group デーモン 067 * 068 * @version 5.0 069 * @author Hiroki Nakamura 070 * @since JDK6.0, 071 */ 072public class Daemon_Transfer_CB01 extends Daemon_Transfer { 073 //* このプログラムのVERSION文字列を設定します。 {@value} */ 074 private static final String VERSION = "5.4.1.0 (2011/11/01)" ; 075 076 // 実行方法に対応する実装クラスの基準名 077 private static final String EXEC_CLS_BASE = "org.opengion.fukurou.transfer.TransferExec_" ; 078 079 // 伝送データ取得用SQL(クエリ回数を減らすため旧伝送DBと伝送定義マスタをJOINして検索) 080 private static final String GE62CB01_SELECT = 081 "SELECT B.KBREAD,B.READOBJ,B.READPRM,B.KBEXEC,B.EXECDBID,B.EXECOBJ,B.EXECPRM,B.ERROR_SENDTO,A.HTCNO" + 082 " FROM CB01 A,GE62 B" + 083 " WHERE A.HCDD = SUBSTR(B.READOBJ,1,INSTR(B.READOBJ,' ',1,1)-1)" + // "3 D9 B119"の"3" 084 " AND A.HTO = RPAD(SUBSTR(B.READOBJ,INSTR(B.READOBJ,' ',1,1)+1,INSTR(B.READOBJ,' ',1,2)-INSTR(B.READOBJ,' ',1,1)-1),8)" + // "3 D9 B119"の"D9" 085 " AND A.HSYU = RPAD(SUBSTR(B.READOBJ,INSTR(B.READOBJ,' ',1,2)+1),4)" + // "3 D9 B119"の"B119" 086 " AND A.HCDJ = '1'" + 087 " AND B.FGJ = '1'"; 088 089 // コネクションにアプリケーション情報を追記するかどうか指定 090 private static final boolean USE_DB_APPLICATION_INFO = HybsSystem.sysBool( "USE_DB_APPLICATION_INFO" ) ; 091 092 // HTTP接続時のプロキシホスト 093 private static final String HTTP_PROXY_HOST = HybsSystem.sys( "HTTP_PROXY_HOST" ); 094 095 // HTTP接続時のプロキシポート 096 private static final int HTTP_PROXY_PORT = HybsSystem.sysInt( "HTTP_PROXY_PORT" ); 097 098 // 呼び出し元ホストコード 099 private static final String HFROM = HybsSystem.sys( "TRANSFER_HOST_CODE" ); 100 101 // ループカウンタを24回に設定 102 private static final int LOOP_COUNTER = 24; 103 104 private boolean running = true; 105 private int loopCnt = 0; 106 107 private String ge62Cb01Select = null; 108 private String dmnName = null; 109 110 private ApplicationInfo appInfo = null; 111 private boolean debug = false; 112 113 /** 114 * このタイマータスクによって初期化されるアクションです。 115 * パラメータを使用した初期化を行います。 116 * 117 */ 118 @Override 119 public void initDaemon() { 120 debug = StringUtil.nval( getValue( "DEBUG" ),debug ); 121 122 dmnName = getName(); 123 124 StringBuilder buf = new StringBuilder(); 125 buf.append( GE62CB01_SELECT ); 126 127 // システムIDは必須指定 128 String systemId = getValue( "SYSTEM_ID" ); 129 if( StringUtil.isNull( systemId ) ) { 130 String errMsg = "システムID方法は必須指定です。" ; 131 throw new HybsSystemException( errMsg ); 132 } 133 else { 134 buf.append( " AND B.SYSTEM_ID='" ).append( systemId ).append( "'" ); 135 } 136 137 // 読取方法は必須指定 138 String kbRead = getValue( "KBREAD" ); 139 if( StringUtil.isNull( kbRead ) ) { 140 String errMsg = "読取方法は必須指定です。" ; 141 throw new HybsSystemException( errMsg ); 142 } 143 else { 144 buf.append( " AND B.KBREAD='" ).append( kbRead ).append( "'" ); 145 } 146 147 // デーモングループは必須指定 148 String dmnGroup = getValue( "DMN_GRP" ); 149 if( StringUtil.isNull( dmnGroup ) ) { 150 String errMsg = "デーモングループは必須指定です。" ; 151 throw new HybsSystemException( errMsg ); 152 } 153 else { 154 buf.append( " AND B.DMN_GRP='" ).append( dmnGroup ).append( "'" ); 155 } 156 157 buf.append( " ORDER BY A.HTC" ); 158 159 ge62Cb01Select = buf.toString() ; 160 161 if( debug ) { 162 System.out.println( "DMN_NAME=[" + dmnName + "]" ); 163 System.out.println( "QUERY=[" + ge62Cb01Select + "]" ); 164 } 165 166 if( USE_DB_APPLICATION_INFO ) { 167 appInfo = new ApplicationInfo(); 168 // ユーザーID,IPアドレス,ホスト名 169 appInfo.setClientInfo( systemId,HybsSystem.HOST_ADRS,HybsSystem.HOST_NAME ); 170 // 画面ID,操作,プログラムID 171 appInfo.setModuleInfo( "TransferDaemon",dmnName,dmnName ); 172 } 173 else { 174 appInfo = null; 175 } 176 } 177 178 /** 179 * タイマータスクのデーモン処理の開始ポイントです。 180 * 181 */ 182 @Override 183 protected void startDaemon() { 184 if( loopCnt % LOOP_COUNTER == 0 ) { 185 loopCnt = 1; 186 System.out.println(); 187 System.out.print( toString() + " " + new Date() + " " ); 188 } 189 else { 190 System.out.print( "." ); 191 loopCnt++ ; 192 } 193 194 // 伝送DB読取 195 String[][] vals = null; 196 GE62CB01Data ge62Cb01Data = new GE62CB01Data(); 197 try { 198 vals = DBUtil.dbExecute( ge62Cb01Select,null,appInfo ); 199 if( vals != null && vals.length > 0 ) { 200 for( int row=0; running && row<vals.length; row++ ) { 201 ge62Cb01Data.addData( vals[row] ); 202 } 203 } 204 } 205 catch( Throwable ex ) { 206 String header = "伝送読取エラー:DMN_NAME=[" + dmnName + "] , DMN_HOST=[" + HybsSystem.HOST_NAME + "] , QUERY=[" + ge62Cb01Select + "]"; 207 String errMsg = header + HybsSystem.CR + StringUtil.stringStackTrace( ex ) ; 208 System.out.println( errMsg ); 209 LogWriter.log( errMsg ); 210 String errorSendto = HybsSystem.sys( "ERROR_MAIL_TO_USERS" ); 211 sendMail( header, errMsg, errorSendto ); 212 } 213 214 // 処理実行 215 // 読取対象の単位でトランザクションを生成します。 216 for( String tranKey : ge62Cb01Data.getTranSet() ) { 217 Transaction tran = null; 218 TransferConfig conf = null; 219 String[] htcnoArr = null; 220 boolean isUpdate = true; 221 try { 222 tran = new TransactionReal( appInfo ); 223 224 // 読取対象+実行方法+実行対象の単位で処理を行います。 225 for( String confKey : ge62Cb01Data.getExecKeySet( tranKey ) ) { 226 conf = ge62Cb01Data.getConfig( confKey ); 227 htcnoArr = ge62Cb01Data.getHtcno( confKey ); 228 229 // デバッグ情報を出力します。 230 if( debug ) { 231 System.out.println(); 232 System.out.print( " START = " + new Date() ); 233 System.out.print( "[" + dmnName + "]:[" + StringUtil.array2csv( htcnoArr ) + "]:[" + conf.toString() + "]" ); 234 } 235 236 // 伝送データを読み出します。 237 String[] val = read( htcnoArr, tran ); 238 // 実行方法のオブジェクトを生成します。 239 TransferExec exec = (TransferExec)StringUtil.newInstance( EXEC_CLS_BASE + conf.getKbExec() ); 240 // 処理を実行します。 241 exec.execute( val, conf, tran ); 242 243 // デバッグ情報を出力します。 244 if( debug ) { 245 System.out.println(); 246 System.out.print( " END = " + new Date() ); 247 System.out.print( "[" + dmnName + "]:[" + StringUtil.array2csv( htcnoArr ) + "]:[" + conf.toString() + "]" ); 248 } 249 250 // 対象となるマスタの内、読取パラメーターに1つでも"NOUPDATE"が指定されている場合は、CB01の状況を更新しない 251 if( "NOUPDATE".equalsIgnoreCase( conf.getReadPrm() ) ) { 252 isUpdate = false; 253 } 254 } 255 256 // 対象となるマスタの内、読取パラメーターに1つでも"NOUPDATE"が指定されている場合は、CB01の状況を更新しない 257 if( isUpdate ) { 258 complete( htcnoArr, tran ); 259 } 260 } 261 catch( Throwable ex ) { 262 // エラーが発生した場合はデーモンを停止します。 263 cancel(); 264 265 if( tran != null ) { 266 tran.rollback(); 267 tran.close(); 268 tran = null; // エラー発生時は、接続を終了します。(次の状況更新でデッドロックになるため) 269 } 270 271 if( htcnoArr != null && htcnoArr.length > 0 ) { 272 error( htcnoArr ); // エラー発生時はCB01>状態を9:エラーに更新 273 } 274 275 String header = "伝送エラー:DMN_NAME=[" + dmnName + "] , DMN_HOST=[" + HybsSystem.HOST_NAME + "]"; 276 String errorSendto = null; 277 if( htcnoArr != null && htcnoArr.length > 0 ) { 278 header += " , HTCNO=[" + StringUtil.array2csv( htcnoArr ) + "]"; 279 } 280 if( conf != null ) { 281 header += " , CONFIG=[" + conf.toString() + "]"; 282 errorSendto = conf.getErrorSendto(); 283 } 284 285 String errMsg = header + HybsSystem.CR + StringUtil.stringStackTrace( ex ) ; 286 System.out.println( errMsg ); 287 LogWriter.log( errMsg ); 288 sendMail( header, errMsg, errorSendto ); 289 } 290 finally { 291 if( tran != null ) { tran.close(); } 292 } 293 } 294 } 295 296 /** 297 * このタイマータスクのcancel() メソッドをオーバーライドします。 298 * HybsTimerTaskManager#cancelTask( int ) を実行します。 299 * 300 * @return スケジュールされている 1 回以上実行されない場合に true 301 * @see java.util.TimerTask#cancel() 302 */ 303 @Override 304 public boolean cancel() { 305 running = false; 306 return super.cancel(); 307 } 308 309 /** 310 * CB01を読み込みデータを配列で返します。 311 * 312 * @param htcnoArr 読取対象の通番NO(配列) 313 * @param tran トランザクション 314 * 315 * @return データ(配列) 316 */ 317 private String[] read( final String[] htcnoArr, final Transaction tran ) { 318 if( htcnoArr == null || htcnoArr.length == 0 ) { return new String[0]; } 319 320 String htcnos = StringUtil.array2csv( htcnoArr ); 321 StringBuilder buf = new StringBuilder(); 322 buf.append( "SELECT A.HTEXT" ); 323 buf.append( " FROM CB01 A" ); 324 buf.append( " WHERE A.HCDJ = '5'" ); 325 buf.append( " AND A.HTCNO IN (" ); 326 buf.append( htcnos ); 327 buf.append( ") ORDER BY A.HTC, A.HTCNO" ); 328 329 String[][] vals = DBUtil.dbExecute( buf.toString(),null,tran ); 330 String[] rtn = new String[vals.length]; 331 for( int i=0; i<vals.length; i++ ) { 332 rtn[i] = vals[i][0]; 333 } 334 return rtn; 335 } 336 337 /** 338 * CB01のヘッダーデータの状況を2:抜出済みに更新します。 339 * 340 * @param htcnoArr 更新対象の通番NO(配列) 341 * @param tran トランザクション 342 */ 343 private void complete( final String[] htcnoArr, final Transaction tran ) { 344 if( htcnoArr == null || htcnoArr.length == 0 ) { return; } 345 346 String htcnos = StringUtil.array2csv( htcnoArr ); 347 StringBuilder buf = new StringBuilder(); 348 buf.append( "UPDATE CB01 A" ); 349 buf.append( " SET A.HCDJ = '2'" ); 350 buf.append( " WHERE A.HCDJ = '1'" ); 351 buf.append( " AND A.HTCNO IN (" ); 352 buf.append( htcnos ); 353 buf.append( ")" ); 354 355 DBUtil.dbExecute( buf.toString(),null,tran ); 356 } 357 358 /** 359 * CB01のヘッダーデータの状況を9:エラーに更新します。 360 * 361 * @param htcnoArr 更新対象の通番NO(配列) 362 */ 363 private void error( final String[] htcnoArr ) { 364 if( htcnoArr == null || htcnoArr.length == 0 ) { return; } 365 366 String htcnos = StringUtil.array2csv( htcnoArr ); 367 StringBuilder buf = new StringBuilder(); 368 buf.append( "UPDATE CB01 A" ); 369 buf.append( " SET A.HCDJ = '9'" ); 370 buf.append( " WHERE A.HCDJ in ('1','2')" ); // 既に実行PGで抜出済みに更新されている可能性がある 371 buf.append( " AND A.HTCNO IN (" ); 372 buf.append( htcnos ); 373 buf.append( ")" ); 374 375 DBUtil.dbExecute( buf.toString(),null,appInfo ); // エラー更新はトランザクションを分けて処理する 376 } 377 378 /** 379 * 伝送定義マスタ及び旧伝送DBから読み出したデータを管理します。 380 */ 381 private static class GE62CB01Data { 382 383 // トランザクションを生成するキーのセット(読取対象単位) 384 private final Set<String> tranSet = new LinkedHashSet<String>(); 385 // トランザクションキー(読取対象)に対する、処理キー(読取対象+実行方法+実行対象)のセット 386 private final Map<String,Set<String>> tran2ExecKeySet = new LinkedHashMap<String,Set<String>>(); 387 // 処理キー(読取対象+実行方法+実行対象)に対する設定オブジェクトのマッピング 388 private final Map<String,TransferConfig> execKey2Conf = new HashMap<String,TransferConfig>(); 389 // 処理キー(読取対象+実行方法+実行対象)に対する通番NO(配列)のマッピング 390 private final Map<String,List<String>> execKey2HtcnoArr = new LinkedHashMap<String,List<String>>(); 391 392 /** 393 * GE62及びCB01読取データを追加します。 394 * 395 * @param data GE62及びCB01読取データ 396 */ 397 private void addData( final String[] data ) { 398 String kbRead = data[0]; 399 String readObj = data[1]; 400 String readPrm = data[2]; 401 String kbExec = data[3]; 402 String execDbid = data[4]; 403 String execObj = data[5]; 404 String execPrm = data[6]; 405 String errorSendto = data[7]; 406 String htcno = data[8]; 407 408 String tranKey = readObj; 409 tranSet.add( tranKey ); 410 411 // 読取対象 + 実行方法 + 実行対象 単位に処理対象通番NOを集約する 412 String execKey = readObj + kbExec + execObj; 413 Set<String> execKeySet = tran2ExecKeySet.get( tranKey ); 414 if( execKeySet == null ) { 415 execKeySet = new LinkedHashSet<String>(); 416 } 417 execKeySet.add( execKey ); 418 tran2ExecKeySet.put( tranKey, execKeySet ); 419 420 // 伝送設定オブジェクトのマップ 421 TransferConfig conf = execKey2Conf.get( execKey ); 422 if( conf == null ) { 423 conf = new TransferConfig( 424 kbRead, readObj, readPrm 425 , kbExec, execDbid, execObj, execPrm 426 , errorSendto, HFROM, HTTP_PROXY_HOST, HTTP_PROXY_PORT ); 427 execKey2Conf.put( execKey, conf ); 428 } 429 430 // 通番NOのマップ 431 List<String> htcnoArr = execKey2HtcnoArr.get( execKey ); 432 if( htcnoArr == null ) { 433 htcnoArr = new ArrayList<String>(); 434 } 435 htcnoArr.add( htcno ); 436 execKey2HtcnoArr.put( execKey, htcnoArr ); 437 } 438 439 /** 440 * トランザクション生成キー(読取対象)のセットを返します。 441 * 442 * @return トランザクション生成キー(読取対象)のセット 443 */ 444 private Set<String> getTranSet() { 445 return tranSet; 446 } 447 448 /** 449 * トランザクション生成キー(読取対象)に対する処理キー(読取対象+実行方法+実行対象)のセットを返します。 450 * 451 * @param tranKey トランザクション生成キー(読取対象) 452 * @return トランザクション生成キー(読取対象)に対する処理キー(読取対象+実行方法+実行対象)のセット 453 */ 454 private Set<String> getExecKeySet( final String tranKey ) { 455 return tran2ExecKeySet.get( tranKey ); 456 } 457 458 /** 459 * 処理キー(読取対象+実行方法+実行対象)に対する設定オブジェクトを返します。 460 * 461 * @param execKey 処理キー(読取対象+実行方法+実行対象) 462 * @return 設定オブジェクト 463 */ 464 private TransferConfig getConfig( final String execKey ) { 465 return execKey2Conf.get( execKey ); 466 } 467 468 /** 469 * 処理キー(読取対象+実行方法+実行対象)に対する通番NO(配列)を返します。 470 * 471 * @param execKey 処理キー(読取対象+実行方法+実行対象) 472 * @return 通番NO(配列) 473 */ 474 private String[] getHtcno( final String execKey ) { 475 List<String> lst = execKey2HtcnoArr.get( execKey ); 476 if( lst == null ) { return new String[0]; } 477 else { return lst.toArray( new String[lst.size()] ); } 478 } 479 } 480}