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.report2; 017 018import org.opengion.fukurou.system.OgRuntimeException ; // 6.4.2.0 (2016/01/29) 019import java.io.File; 020import java.util.Arrays; 021 022import org.opengion.fukurou.db.DBFunctionName; 023import org.opengion.fukurou.db.DBUtil; 024import org.opengion.fukurou.mail.MailTX; 025import org.opengion.fukurou.db.ApplicationInfo; 026import org.opengion.fukurou.system.DateSet; // 6.4.2.0 (2016/01/29) 027import org.opengion.fukurou.system.LogWriter; 028import org.opengion.fukurou.util.StringUtil; 029import org.opengion.fukurou.util.UnicodeCorrecter; // 5.9.3.3 (2015/12/26) package を、mail → util に移動のため 030import org.opengion.hayabusa.common.HybsSystem; 031import org.opengion.hayabusa.db.DBTableModel; 032import org.opengion.hayabusa.db.DBTableModelUtil; 033import org.opengion.hayabusa.resource.ResourceFactory; 034import org.opengion.hayabusa.resource.ResourceManager; 035import static org.opengion.fukurou.system.HybsConst.CR ; // 6.1.0.0 (2014/12/26) 036 037/** 038 * DBからキューを作成するためのクラスです。 039 * キューはGE5xテーブルから作成されます。 040 * 041 * キュー生成時点(処理スレッドにスタックした時点)では、帳票データのテーブルモデルは作成されません。 042 * 帳票データは、各スレッドからset()メソッドを呼び出したタイミングで生成されます。 043 * 044 * 処理開始及び、完了のステータスは、GE50の完成フラグに更新されます。 045 * また、エラー発生時のメッセージは、GE56に更新されます。 046 * 047 * @og.group 帳票システム 048 * 049 * @version 4.0 050 * @author Hiroki.Nakamura 051 * @since JDK1.6 052 */ 053public final class QueueManager_DB implements QueueManager { 054 055 /** コネクションにアプリケーション情報を追記するかどうか指定 */ 056 private static final boolean USE_DB_APPLICATION_INFO = HybsSystem.sysBool( "USE_DB_APPLICATION_INFO" ) ; 057 058 private static final String DBID = HybsSystem.sys( "RESOURCE_DBID" ); // 5.5.5.1 (2012/08/07) リソース系DBID 付け忘れ対応 059 060 // 4.3.7.0 (2009/06/01) HSQLDB対応 061 // 5.1.4.0 (2010/03/01) データベース名 でなく、DBID名で検索するように変更します。 062 private static final String CON = DBFunctionName.getFunctionName( "CON", null ); 063 064 // 5.2.0.0 (2010/09/01) Ver4互換モード対応 065 private static final String OUT_FILE = HybsSystem.sysBool( "VER4_COMPATIBLE_MODE" ) ? "OUTFILE" : "OUT_FILE"; 066 private static final String OUT_DIR = HybsSystem.sysBool( "VER4_COMPATIBLE_MODE" ) ? "OUTDIR" : "OUT_DIR"; 067 068 // 4.3.3.6 (2008/11/15) マルチサーバ対応追加(GE12から処理対象デーモングループ取得) 069 // 4.3.7.0 (2009/06/01) HSQLDB対応 070 // 5.2.0.0 (2010/09/01) Ver4互換モード対応 071 // 5.4.2.0 (2011/12/26) PRTID,PRGDIR,PRGFILE取得 072 // 5.9.2.2 (2015/11/20) GROUPID追加 073 private static final String SQL_SELECT_GE50 = 074 "SELECT A.SYSTEM_ID, A.YKNO, A.LISTID, A."+OUT_DIR+", A."+OUT_FILE+", A.PDF_PASSWD" 075 + ", B.LANG, B.FGRUN, B.DMN_GRP " 076 + ", C.MODELDIR, C.MODELFILE, D.PRTNM, C.FGLOCAL, C.FGCUT, C.BSQL, C.HSQL, C.FSQL " 077 + " ,B.PRTID, B.PRGDIR, B.PRGFILE " 078 + " ,A.GROUPID " // 5.9.2.2 (2015/11/20) 079 + "FROM GE50 A " 080 + "INNER JOIN GE53 B " 081 + "ON A.SYSTEM_ID = B.SYSTEM_ID AND A.JOKEN = B.JOKEN " 082 + "INNER JOIN GE54 C " 083 + "ON A.SYSTEM_ID = C.SYSTEM_ID AND A.LISTID = C.LISTID " 084 + "LEFT OUTER JOIN GE55 D " 085 + "ON B.SYSTEM_ID = D.SYSTEM_ID AND B.PRTID = D.PRTID " 086 + "WHERE A.FGKAN='1' " 087 + "AND EXISTS ( SELECT 'X' FROM GE12 E " 088 + "WHERE E.FGJ ='1' " 089 + "AND E.SYSTEM_ID = '" 090 + HybsSystem.sys( "SYSTEM_ID" ) 091 + "' " 092 + "AND E.CONTXT_PATH = '" 093 + HybsSystem.sys( "HOST_URL" ) 094 + "' " 095 + "AND E.PARAM_ID LIKE 'REPORT2_HANDLE_DAEMON_%' " 096 + "AND E.PARAM = 'RUN_'" + CON + "A.SYSTEM_ID" + CON + "'_'" + CON + "B.DMN_GRP" 097 + ") " 098 + "ORDER BY " 099 + HybsSystem.sys( "REPORT_DAEMON_ORDER_BY" ); 100 101 // 5.1.2.0 (2010/01/01) ページ数、データ数をGE50に更新する。 102 private static final String SQL_UPDATE_GE50 = 103 "UPDATE GE50 SET FGKAN = ?, DMN_NAME = ?, DMN_HOST = ?, SUDATA = ?, SUPAGE = ?, DYUPD = ? WHERE SYSTEM_ID = ? AND YKNO = ?"; 104 105 private static final String SQL_INSERT_GE56 = 106 "INSERT INTO GE56 ( FGJ, SYSTEM_ID, YKNO, ERRMSG, DYSET, DYUPD, USRSET, USRUPD, PGUPD ) " 107 + " VALUES ( '1', ?, ? ,? ,? ,? ,? ,? ,? )" ; 108 109 private static final int STATUS_COMPLETE = 2; 110 private static final int STATUS_EXECUTE = 3; 111 private static final int STATUS_ERROR = 8; 112 113 private static QueueManager manager = new QueueManager_DB(); 114 115 /** アプリケーション情報 */ 116 private static final ApplicationInfo APP_INFO; // 6.4.1.1 (2016/01/16) appInfo → APP_INFO refactoring 117 static { 118 if( USE_DB_APPLICATION_INFO ) { 119 APP_INFO = new ApplicationInfo(); 120 // ユーザーID,IPアドレス,ホスト名 121 APP_INFO.setClientInfo( "ReportDaemon", HybsSystem.HOST_ADRS, HybsSystem.HOST_NAME ); 122 // 画面ID,操作,プログラムID 123 APP_INFO.setModuleInfo( "ReportDaemon", "QueueManager", "QueueManager" ); 124 } 125 else { 126 APP_INFO = null; 127 } 128 } 129 130 /** 131 * インスタンスの生成を禁止します。 132 */ 133 private QueueManager_DB() {} 134 135 /** 136 * インスタンスを返します。 137 * 138 * @return 帳票処理キューの管理マネージャ 139 */ 140 public static QueueManager getInstance() { 141 return manager; 142 } 143 144 /** 145 * 帳票処理キューを作成します。 146 * 147 * @og.rev 4.3.0.0 (2008/07/15) スレッドIDにシステムIDを付加します。 148 * @og.rev 5.1.2.0 (2010/01/01) HSQL,FSQL,BSQLのセットを廃止します。(このクラス内でデータを直接分割) 149 * @og.rev 5.4.3.0 (2011/12/26) PRTIDの取得 150 * @og.rev 5.5.5.1 (2012/08/07) リソース系DBID 付け忘れ対策 151 * @og.rev 6.3.9.0 (2015/11/06) Use block level rather than method level synchronization.(PMD) 152 * @og.rev 5.9.2.2 (2015/11/20) GrpId,DmnGrp 追加 153 */ 154 public void create() { 155 // キューをスタックするまでの例外は、ScheduleTagでcatchされデーモンがスリープする。 156 final String[][] ge50vals = DBUtil.dbExecute( SQL_SELECT_GE50, new String[0], APP_INFO, DBID ); // 5.5.5.1 (2012/08/07) 157 158 // 6.3.9.0 (2015/11/06) 元々のsynchronizedの必要性が分からないが、帳票なのでとりあえず入れておきます。 159 synchronized( ge50vals ) { 160 for( int i=0; i<ge50vals.length; i++ ) { 161 final ExecQueue queue = new ExecQueue(); 162 queue.setSystemId( ge50vals[i][0] ); 163 queue.setYkno( ge50vals[i][1] ); 164 queue.setListId( ge50vals[i][2] ); 165 queue.setOutputName( new File( ge50vals[i][3] ).getAbsolutePath() , ge50vals[i][4] , ge50vals[i][7] , ge50vals[i][1] ); // 4.3.0.0 (2008/07/18) 要求番号を出力ファイル名に利用 166 queue.setPdfPasswd( ge50vals[i][5] ); 167 queue.setLang( ge50vals[i][6] ); 168 queue.setOutputType( ge50vals[i][7] ); 169 queue.setThreadId( ge50vals[i][0] + "_" + StringUtil.nval( ge50vals[i][8] , "_DEFALUT_" ) ); // 4.3.0.0 (2008/07/15) 170 queue.setTemplateName( new File( ge50vals[i][9] ).getAbsolutePath() + File.separator + ge50vals[i][10] ); 171 queue.setPrinterName( ge50vals[i][11] ); 172 queue.setFglocal( "1".equals( ge50vals[i][12] ) ); 173 queue.setFgcut( "1".equals( ge50vals[i][13] ) ); 174 175 queue.setPrtId( ge50vals[i][17] ); // 5.4.3.0 176 queue.setPrgDir( ge50vals[i][18] ); // 5.4.3.0 177 queue.setPrgFile( ge50vals[i][19] ); // 5.4.3.0 178 179 queue.setGrpId( ge50vals[i][20] ); // 5.9.2.2 (2015/11/20) 180 queue.setDmnGrp( ge50vals[i][8] ); // 5.9.2.2 (2015/11/20) 181 182 queue.setManager( this ); 183 184 ExecThreadManager.insertQueue( queue ); 185 } 186 } 187 } 188 189 /** 190 * 帳票処理データをキューにセットします。 191 * 192 * @og.rev 5.1.2.0 (2010/01/01) HSQL,FSQL,BSQLのセットを廃止します。(このクラス内でデータを直接分割) 193 * 194 * @param queue ExecQueueオブジェクト 195 */ 196 public void set( final ExecQueue queue ) { 197 final String systemId = queue.getSystemId(); 198 final String lang = queue.getLang(); 199 final String listId = queue.getListId(); 200 final String ykno = queue.getYkno(); 201 202 ResourceManager resource = null; 203 if( queue.isFglocal() ) { 204 resource = ResourceFactory.newInstance( systemId, lang, false ); 205 } 206 else { 207 resource = ResourceFactory.newInstance( lang ); 208 } 209 210 // ヘッダー情報の取得 211 final DBTableModel header = new DBTableModelCreator( systemId, listId, ykno, "H", resource ).getTable(); 212 213 if( header != null && header.getRowCount() > 0 ) { 214 queue.setHeader( header ); 215 } 216 217 // フッター情報の取得 218 final DBTableModel footer = new DBTableModelCreator( systemId, listId, ykno, "F", resource ).getTable(); 219 if( footer != null && footer.getRowCount() > 0 ) { 220 queue.setFooter( footer ); 221 } 222 223 // ボディー情報の取得 224 final DBTableModel body = new DBTableModelCreator( systemId, listId, ykno, "B", resource ).getTable(); 225 // レイアウトテーブルがないと固定長を分割するSQL文が設定されず、DBTableModelがnullになる 226 if( body == null ) { 227 queue.addMsg( "[ERROR] DBTableModel doesn't exists! maybe Layout-Table(GE52) is not configured..." + CR ); 228 queue.setError(); 229 throw new OgRuntimeException(); 230 } 231 if( body.getRowCount() <= 0 ) { 232 queue.addMsg( "[ERROR] Database Body row count is Zero." + ykno + CR ); 233 queue.setError(); 234 throw new OgRuntimeException(); 235 } 236 if( body.isOverflow() ) { 237 queue.addMsg( "[ERROR]Database is Overflow. [" + body.getRowCount() + "]" + CR ); 238 queue.addMsg( "[ERROR]Check SystemParameter Data in DB_MAX_ROW_COUNT Overflow" + CR ); 239 queue.setError(); 240 throw new OgRuntimeException(); 241 } 242 queue.setBody( body ); 243 } 244 245 /** 246 * キューを実行中の状態に更新します。 247 * 248 * @param queue ExecQueueオブジェクト 249 */ 250 public void execute( final ExecQueue queue ) { 251 status( queue, STATUS_EXECUTE ); 252 } 253 254 /** 255 * キューを完了済の状態に更新します。 256 * 257 * @param queue ExecQueueオブジェクト 258 */ 259 public void complete( final ExecQueue queue ) { 260 status( queue, STATUS_COMPLETE ); 261 } 262 263 /** 264 * キューをエラーの状態に更新します。 265 * 266 * @param queue ExecQueueオブジェクト 267 */ 268 public void error( final ExecQueue queue ) { 269 status( queue, STATUS_ERROR ); 270 insertErrorMsg( queue ); 271 } 272 273 /** 274 * GE50の状況Cを更新します。 275 * 276 * @og.rev 4.2.4.1 (2008/07/09) 更新日時をセット 277 * @og.rev 5.1.2.0 (2010/01/01) 行数、ページ数も更新する 278 * @og.rev 5.5.5.1 (2012/08/07) リソース系DBID 付け忘れ対策 279 * @og.rev 6.4.2.0 (2016/01/29) DateSet.getDate( String ) を利用するように修正します。 280 * 281 * @param queue ExecQueueオブジェクト 282 * @param status 状況C 283 */ 284 private void status( final ExecQueue queue, final int status ) { 285 286 final String dyupd = DateSet.getDate( "yyyyMMddHHmmss" ) ; // 6.4.2.0 (2016/01/29) 287 288 final String[] args 289 = new String[]{ String.valueOf( status ), queue.getThreadId(), HybsSystem.sys( "HOST_NAME" ) 290 , String.valueOf( queue.getExecRowCnt() ), String.valueOf( queue.getExecPagesCnt() ) 291 , dyupd , queue.getSystemId(), queue.getYkno() }; 292 293 DBUtil.dbExecute( SQL_UPDATE_GE50, args, APP_INFO, DBID ); // 5.5.5.1 (2012/08/07) 294 } 295 296 /** 297 * GE56にエラーメッセージを出力します。 298 * 299 * @og.rev 4.4.0.1 (2009/08/08) エラーメッセージ機能追加 300 * @og.rev 5.5.5.1 (2012/08/07) リソース系DBID 付け忘れ対策 301 * @og.rev 6.4.2.0 (2016/01/29) DateSet.getDate( String ) を利用するように修正します。 302 * 303 * @param queue ExecQueueオブジェクト 304 */ 305 private void insertErrorMsg( final ExecQueue queue ) { 306 String errmsg = queue.getMsg(); 307 if( errmsg.length() > 1300 ) { 308 errmsg = errmsg.substring( errmsg.length() - 1300, errmsg.length() ); 309 } 310 311 final String dyset = DateSet.getDate( "yyyyMMddHHmmss" ) ; // 6.4.2.0 (2016/01/29) 312 313 final String[] args 314 = new String[]{ queue.getSystemId(), queue.getYkno(), errmsg 315 , dyset, dyset, "UNKNOWN", "UNKNOWN", "UNKNOWN" }; 316 317 DBUtil.dbExecute( SQL_INSERT_GE56, args, APP_INFO, DBID ); // 5.5.5.1 (2012/08/07) 318 319 sendMail( queue, errmsg ); // 4.4.0.1 (2009/08/08) 320 } 321 322 /** 323 * エラー情報のメール送信を行います。 324 * エラーメールは、システムパラメータ の COMMON_MAIL_SERVER(メールサーバー)と 325 * ERROR_MAIL_FROM_USER(エラーメール発信元)と、ERROR_MAIL_TO_USERS(エラーメール受信者) 326 * がすべて設定されている場合に、送信されます。 327 * 328 * @og.rev 4.4.0.1 (2009/08/08) 追加 329 * @og.rev 5.7.0.4 (2013/11/29) listIdの絞込み 330 * 331 * @param queue ExecQueueオブジェクト 332 * @param inErrMsg エラーメッセージ 333 */ 334 private void sendMail( final ExecQueue queue, final String inErrMsg ) { 335 336 final String host = HybsSystem.sys( "COMMON_MAIL_SERVER" ); 337 final String from = HybsSystem.sys( "ERROR_MAIL_FROM_USER" ); 338 final String[] to = StringUtil.csv2Array( HybsSystem.sys( "ERROR_MAIL_TO_USERS" ) ); 339 final String match_txt = HybsSystem.sys( "REPORT_ERRMAIL_REGEX" ); // 5.7.0.4 (2013/11/29) 340 if( host != null && from != null && to.length > 0 ) { 341 if( match_txt == null || match_txt.isEmpty() 342 || queue.getListId() == null || queue.getListId().isEmpty() 343 || queue.getListId().matches( match_txt )){ // 5.7.0.4 (2013/11/29) 344 // 5.7.0.4 (2013/11/29) listid追加 345 final String subject = "SYSTEM_ID=[" + queue.getSystemId() + "] , YKNO=[" + queue.getYkno() + "] , " 346 + "THREAD_ID=[" + queue.getThreadId() + "] , DMN_HOST=[" + HybsSystem.HOST_NAME + "]" 347 + "LISTID=["+ queue.getListId() + "]"; 348 try { 349 final MailTX tx = new MailTX( host ); 350 tx.setFrom( from ); 351 tx.setTo( to ); 352 tx.setSubject( "帳票エラー:" + subject ); 353 tx.setMessage( inErrMsg ); 354 tx.sendmail(); 355 } 356 catch( final Throwable ex ) { 357 final String errMsg = "エラー時メール送信に失敗しました。" + CR 358 + " SUBJECT:" + subject + CR 359 + " HOST:" + host + CR 360 + " FROM:" + from + CR 361 + " TO:" + Arrays.toString( to ) + CR 362 + ex.getMessage(); // 5.1.8.0 (2010/07/01) errMsg 修正 363 LogWriter.log( errMsg ); 364 LogWriter.log( ex ); 365 } 366 } 367 } 368 } 369 370 /** 371 * 帳票明細データを帳票レイアウトテーブルに従って分割し、その結果をDBTableModelとして 372 * 生成します。 373 * データの分割は、バイト数ベースで行われるため、エンコードを正しく指定する必要があります。 374 * エンコード指定は、システムリソースのDB_ENCODEで指定します。 375 * 376 * レイアウトテーブルが存在しない場合、又は、帳票データが存在しない場合、DBTableModelは 377 * nullで返されます。 378 * 379 * @og.rev 6.9.0.2 (2018/02/13) GE51の検索順(order by)を追加します。 380 */ 381 public static final class DBTableModelCreator { 382 // 5.2.0.0 (2010/09/01) Ver4互換モード対応 383 private static final String CLM = HybsSystem.sysBool( "VER4_COMPATIBLE_MODE" ) ? "COLUMN_NAME" : "CLM"; 384 private static final String TEXT_DATA = HybsSystem.sysBool( "VER4_COMPATIBLE_MODE" ) ? "TEXT" : "TEXT_DATA"; 385 386 // 5.2.0.0 (2010/09/01) Ver4互換モード対応 387 // 5.4.4.3 (2012/02/09) FGUSE条件追加対応 388 private static final String SQL_SELECT_GE52 = 389 " select " + CLM + ", START_POS, USE_LENGTH" 390 + " from GE52" 391 + " where SYSTEM_ID = ?" 392 + " and LISTID = ?" 393 + " and KBTEXT = ?" 394 + " and FGJ = '1'" 395 + " and FGUSE = '1'" // 5.4.4.3 396 + " order by SEQ"; 397 398 // 5.2.0.0 (2010/09/01) Ver4互換モード対応 399 // 6.9.0.2 (2018/02/13) GE51の検索順(order by)を追加します。 400 private static final String SQL_SELECT_GE51 = 401 " select " + TEXT_DATA 402 + " from GE51" 403 + " where SYSTEM_ID = ?" 404 + " and YKNO = ?" 405 + " and KBTEXT = ?" 406 + " and FGJ = '1'" 407 + " order by SYSTEM_ID,YKNO,EDNO" ; // 6.9.0.2 (2018/02/13) 408 409 private static final String ENCODE = HybsSystem.sys( "DB_ENCODE" ); 410 411 private final String systemId; 412 private final String listId; 413 private final String ykno; 414 private final String kbtext; 415 private final ResourceManager resource; 416 417 private DBTableModel table ; 418 419 /** 420 * コンストラクタです。 421 * 422 * @param sid システムID 423 * @param lid 帳票ID 424 * @param yk 要求NO 425 * @param kt テキスト区分(H:ヘッダー F:フッター B:ボディー) 426 * @param res リソースマネージャー 427 */ 428 public DBTableModelCreator( final String sid, final String lid, final String yk, final String kt, final ResourceManager res ) { 429 systemId = sid; 430 listId = lid; 431 ykno = yk; 432 kbtext = kt; 433 resource = res; 434 create(); 435 } 436 437 /** 438 * 帳票データをレイアウト定義に従い分割します。 439 * 440 * @og.rev 5.5.5.1 (2012/08/07) リソース系DBID 付け忘れ対策 441 * @og.rev 5.9.3.1 (2015/12/18) SJIS-UTF変換時の波ダッシュ問題対応 442 */ 443 private void create() { 444 final String[] ge52Where = new String[] { systemId, listId, kbtext } ; 445 final String[][] ge52Vals = DBUtil.dbExecute( SQL_SELECT_GE52, ge52Where, APP_INFO, DBID ); // 5.5.5.1 (2012/08/07) 446 if( ge52Vals == null || ge52Vals.length == 0 ) { 447 return; 448 } 449 450 final String[] ge51Where = new String[] { systemId, ykno, kbtext } ; 451 final String[][] ge51Vals = DBUtil.dbExecute( SQL_SELECT_GE51, ge51Where, APP_INFO, DBID ); // 5.5.5.1 (2012/08/07) 452 if( ge51Vals == null || ge51Vals.length == 0 ) { 453 return; 454 } 455 456 String[] clms = new String[ge52Vals.length]; 457 for( int i=0; i<ge52Vals.length; i++ ) { 458 clms[i] = ge52Vals[i][0]; 459 } 460 461 String[][] vals = new String[ge51Vals.length][ge52Vals.length]; 462 for( int i=0; i<ge51Vals.length; i++ ) { 463 final byte[] bytes = StringUtil.makeByte( UnicodeCorrecter.correctToCP932( ge51Vals[i][0], ENCODE ), ENCODE ); // 5.9.3.1 (2015/12/18) 464 for( int j=0; j<ge52Vals.length; j++ ) { 465 final int strpos = Integer.parseInt( ge52Vals[j][1] ) - 1; // 6.0.2.4 (2014/10/17) メソッド間違い 466 int len = Integer.parseInt( ge52Vals[j][2] ); // 6.0.2.4 (2014/10/17) メソッド間違い 467 if( strpos >= bytes.length ) { 468 vals[i][j] = ""; 469 } 470 else { 471 if( strpos + len > bytes.length ) { 472 len = bytes.length - strpos; 473 } 474 vals[i][j] = StringUtil.rTrim( StringUtil.makeString( bytes, strpos, len, ENCODE ) ); 475 } 476 } 477 } 478 table = DBTableModelUtil.makeDBTable( clms, vals, resource ); 479 } 480 481 /** 482 * 分割後のDBTableModelを返します。 483 * 484 * @return 分割後のDBTableModel 485 */ 486 public DBTableModel getTable() { 487 return table; 488 } 489 } 490}