    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                                        // 対象となるマスタの内、読取パラメーターに１つでも"NOUPDATE"が指定されている場合は、CB01の状況を更新しない
    251                                        if( "NOUPDATE".equalsIgnoreCase( conf.getReadPrm() ) ) {
    252                                                isUpdate = false;
    253                                        }
    254                                }
    255
    256                                // 対象となるマスタの内、読取パラメーターに１つでも"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}