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