package com.eactive.eai.batch.job.jobModule.Component;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;

import com.eactive.eai.batch.job.jobConstant.ErrorKeys;
import com.eactive.eai.batch.job.jobConstant.TelegramKeys;
import com.eactive.eai.batch.job.jobHandle.JobProcessData;
import com.eactive.eai.batch.job.jobItem.TelegramService;

import com.eactive.eai.adapter.socket.common.CommonLib;
import com.eactive.eai.batch.common.CommonKeys;
import com.eactive.eai.batch.common.LoggingUtil;
import com.eactive.eai.batch.common.StringUtil;
import com.eactive.eai.batch.doc.BatchDoc;
import com.eactive.eai.batch.rule.phaseinfo.PhaseInfoVO;
import com.eactive.eai.batch.rule.telegraminfo.TelegramInfoManager;
import com.eactive.eai.batch.rule.telegraminfo.TelegramInfoVO;
import com.eactive.eai.batch.telegram.TelegramItemVO;
import com.eactive.eai.batch.telegram.TelegramManager;
import com.eactive.eai.common.exception.ExceptionUtil;

/**
 * Rule에 정의되지 않은 {전문종별코드} 수신 시 작업을 중지하는 RECV Flow Component
 */
public class RECV1 extends JobComponent implements JobComponentService
{

    private TelegramService     ll_telegram = null;
    private TelegramService     rv_telegram = null;

    ByteArrayOutputStream boutHeader = new ByteArrayOutputStream();
    ByteArrayOutputStream boutBody   = new ByteArrayOutputStream();

    /**
     * JobComponentService functions
     */
    public BatchDoc execute() throws Exception
    {

        // UUID, 흐름규칙 코드 구하기
        logger.debug("{} ■ Node Count = " + batchDoc.getBatchMsg().getBody().getNodeCount(), logHeader);

        //==============================================================================================
        // 소켓을 찾는다.
        logger.debug("{} ▶ SOCKET 검색......", logHeader);
        socket = bs_man.getRunningSocket(strUUID);
        if (socket == null ) {
            doErrorAction(null, ErrorKeys.ERROR_NO_SOCKET, new String[] {strUUID});
            return batchDoc;
        }
        DataInputStream  din  = new DataInputStream(socket.getInputStream());

        //==============================================================================================
        // RECV 단계의 타임아웃과 반복시도 횟수값을 알아내기위한 공용저장공간 뒤지기
        logger.debug("{} ▶ JobProcessData 검색......", logHeader);
        HashMap<String, Object> currdata_hm = JobProcessData.getInstance().getBatchStageData(strUUID);
        if (currdata_hm == null) {
            doErrorAction(null, ErrorKeys.ERROR_NO_PROCDATA, new String[] {strUUID});
            return batchDoc;
        }
        @SuppressWarnings("unchecked")
        ArrayList<PhaseInfoVO> curr_poss_stage = (ArrayList<PhaseInfoVO>) currdata_hm.get(JobProcessData.PROCDATA_NEXTSTAGE);
        if (curr_poss_stage == null || curr_poss_stage.size() == 0) {
            doErrorAction(null, ErrorKeys.ERROR_NO_NEXTSTAGE, new String[] {strRuleCode, strPhaseCode});
            return batchDoc;
        }

        //==============================================================================================
        // PhaseInfo 정보에서 RECV 용 TIMEOUT과 반복 횟수를 읽어들인다.
        logger.debug("{} ▶ SOCKET Timeout 설정......", logHeader);
        PhaseInfoVO pvo = curr_poss_stage.get(0);
        int nTimeout = pvo.getTOVal() * 1000; // 타임아웃 시간 (msec)
        int nRepeatCount = pvo.getRepeat();
        socket.setSoTimeout(nTimeout);
        logger.debug("{}    ☞ [Timeout = " + pvo.getTOVal() + "초], [Retry count = " + nRepeatCount +"]", logHeader);

        //##############################################################################################
        // 이하 HEADER 처리 부분                                                                       #
        //##############################################################################################

        //==============================================================================================
        // EAIBatchMessage의 노드정보에서 전문결정용 텔레그램 클래스를 찾아낸다.
        logger.debug("{} ▶ 전문 HEADER 수신 Telegram 정보 검색......", logHeader);
        String strTelegramID     = batchDoc.getBatchMsg().getPhaseinfo().getLengthTelegramID();
        int    nLengthFieldIdx   = batchDoc.getBatchMsg().getPhaseinfo().getLengthFieldIndex();

        TelegramInfoVO lenTelegramVO = TelegramInfoManager.getInstance().getTelegramInfo(strTelegramID);
        if (lenTelegramVO == null) {
            doErrorAction(null, ErrorKeys.ERROR_NO_TELEGRAM_ID, new String[] {strRuleCode, strPhaseCode, strTelegramID});
            return batchDoc;
        }
        String  strLenTelegramClsName = lenTelegramVO.getTelegramClsName();
        String  strLenMsgMetaDstcd    = lenTelegramVO.getMsgMetaDstcd();
        batchDoc.getBatchMsg().getPhaseinfo().setLengthClassName(strLenTelegramClsName);  //Stage Logging 용
        batchDoc.getBatchMsg().getPhaseinfo().setLengthMsgCode  (strLenMsgMetaDstcd   );  //Stage Logging 용
        logger.debug("{}    ☞ [전문 HEADER 수신 Telegram ID = "+ strTelegramID +"], [{전문종별코드} 필드 Index = "+ nLengthFieldIdx +"]", logHeader);
        logger.debug("{}    ☞ [전문 HEADER 수신 Telegram Class = "+ strLenTelegramClsName +"], [전문 HEADER 수신 전문메시지ID = "+ strLenMsgMetaDstcd +"]", logHeader);

        //==============================================================================================
        // 길이결정 텔러그램 클래스를 생성한다.
        logger.debug("{} ▶ 전문 HEADER 수신 Telegram Class 생성...... ("+ strLenTelegramClsName +")", logHeader);
        this.ll_telegram = (TelegramService) Class.forName(strLenTelegramClsName).newInstance();
        if (this.ll_telegram == null) {
            doErrorAction(null, ErrorKeys.ERROR_NO_CLASS_DEFINITION, new String[] {strTelegramID, strLenTelegramClsName});
            return batchDoc;
        }

        // 길이결정용 텔레그램 인스탄스에 BatchDoc 지정하고 메시지메타코드도 지정한다.
        this.ll_telegram.setEAIBatchMessage(batchDoc);
        this.ll_telegram.setTelegramCode(strLenMsgMetaDstcd);
        this.ll_telegram.setNextStageInfo(curr_poss_stage);

        //==============================================================================================
        // 전문헤더 클래스의 doPreExecute() 메소드를 호출한다. (전문필드 배열 생성)
        logger.debug("{} ▶ 전문 HEADER 수신 클래스의 doPreExecute() 메소드 호출......", logHeader);
        if (this.ll_telegram.doPreExecute() == false) {
            doErrorAction(this.ll_telegram, ErrorKeys.ERROR_RECV_PRE_LENGTH, new String[] {strTelegramID, strLenTelegramClsName});
            return batchDoc;
        }

        //==============================================================================================
        // 소켓에서 필드별로 데이터를 읽어들인다.
        logger.debug("{} ▶ SOCKET 에서 전문 HEADER 데이터 수신 대기중......", logHeader);
        for (int i=0; i<this.ll_telegram.getFieldCount(); i++) {

            int nFldLen = -1;
            //해당 필드 사이즈 조회 (integer 또는 'XX')
            String strFldLen = this.ll_telegram.getFieldLength(i);

            //비정형 길이('XX')인 경우 getCalculatedLength 호출 후 -1 리턴시 오류 처리
            if (strFldLen.trim().equalsIgnoreCase(TelegramService.FIELD_NO_LENGTH)) {
                nFldLen = this.ll_telegram.getCalculatedLength(i);
                if ( nFldLen == -1 ) {
                    doErrorAction(this.ll_telegram, ErrorKeys.ERROR_ERR_TELEGRAM_META, new String[] {strTelegramID, strLenTelegramClsName});
                    return batchDoc;
                }
            }else{
                nFldLen = Integer.parseInt(strFldLen);
            }

            byte[] aField = new byte[nFldLen];
            int nRemainBytes = nFldLen;
            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            TelegramItemVO item = TelegramManager.getInstance().getTelegramField(strLenMsgMetaDstcd, i+1); //log용

            //Socket 에서 해당 필드 데이터 Read (필드 길이만큼 읽어들인다.)
            try {
                while (nRemainBytes > 0) {
                    int nReadBytes = this.recvData(din, nTimeout, nRepeatCount, aField, nRemainBytes);  //한번에 필드 길이만큼 읽지 않을수도 있음
                    bout.write(aField, 0, nReadBytes);
                    nRemainBytes -= nReadBytes;
                }
                String msg = new String(aField);
                logger.debug("{}    ☞ 전문 HEADER Formal 필드 수신 완료 => "+ (i+1) +". ["+ item.getFieldType() +"] ["+ item.getFieldLength() +"] ["+ msg +"] ["+ item.getFieldName() +"]", logHeader);
                if ( msg.trim().equals("")){
                    logger.debug("{}    ☞ 전문 DATA HEXADUMP \n"+ CommonLib.getDumpMessage(aField) , logHeader);
                }
                //읽어들인 필드 데이터 저장
                this.ll_telegram.setFieldDataByIndex(i, bout.toByteArray());
                boutHeader.write(bout.toByteArray());  //Stage Logging 용 (header formal 데이터)

            } catch (Exception ex) {
                logger.error("{}    ☞ ★★★★★ 전문 HEADER Formal 필드 수신 실패 ★★★★★ => "+ (i+1) +". ["+ item.getFieldType() +"] ["+ item.getFieldLength() +"] ["+ new String(aField) +"] ["+ item.getFieldName() +"]", logHeader, ex);
                doErrorAction(ex, this.ll_telegram, "BECEAIFJM025", new String[] { item.getFieldName() });  //전문 HEADER Formal 필드 [{1}] 수신 시 오류가 발생하였습니다.
                return batchDoc;
            }
            //ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
        }// end for

        batchDoc.getBatchMsg().getPhaseinfo().setTelegramTypeValue(this.ll_telegram.getTelegramType());  //Stage Logging 용
        batchDoc.getBatchMsg().getPhaseinfo().setBizCodeValue     (this.ll_telegram.getTrClass()     );  //Stage Logging 용
        batchDoc.getBatchMsg().getPhaseinfo().setControlCodeValue (this.ll_telegram.getOpCode()      );  //Stage Logging 용
        batchDoc.getBatchMsg().getPhaseinfo().setResponseCodeValue(this.ll_telegram.getResCode()     );  //Stage Logging 용
        //수신전문의 응답코드 설정
        batchDoc.getBatchMsg().getBody().setLastResCode(this.ll_telegram.getResCode());
        // 전문 header 수신 데이터 설정
        batchDoc.getBatchMsg().setTelegramHeader(new String(boutHeader.toByteArray()));

        //==============================================================================================
        // 수신한 헤더의 '전문종별코드' 값으로 해당 전문 처리용 텔레그램 ID를 찾아낸다.
        logger.debug("{} ▶ {전문종별코드} 값에 의한 전문 BODY 수신 Telegram 정보 검색......", logHeader);
        String recvTelegramID = this.ll_telegram.getRealRecvTelegram();
        if (recvTelegramID.equalsIgnoreCase("")) {
            doErrorAction(this.ll_telegram, ErrorKeys.ERROR_UNEXPECTED_TELEGRAM, new String[] {this.ll_telegram.getTelegramType(), strRuleCode, strPhaseCode});
            return batchDoc;
        }

        TelegramInfoVO recvTelegramVO = TelegramInfoManager.getInstance().getTelegramInfo(recvTelegramID);
        String  strRecvTelegramClsName = recvTelegramVO.getTelegramClsName();
        String  strRecvMsgMetaDstcd    = recvTelegramVO.getMsgMetaDstcd();
        batchDoc.getBatchMsg().getPhaseinfo().setPhaseClassName(strRecvTelegramClsName);  //Stage Logging 용
        batchDoc.getBatchMsg().getPhaseinfo().setPhaseMsgCode  (strRecvMsgMetaDstcd   );  //Stage Logging 용
        logger.debug("{}    ☞ [전문 BODY 수신 Telegram ID = "+ recvTelegramID +"]", logHeader);
        logger.debug("{}    ☞ [전문 BODY 수신 Telegram Class = "+ strLenTelegramClsName +"], [전문 BODY 수신 전문메시지ID = "+ strLenMsgMetaDstcd +"]", logHeader);

        //==============================================================================================
        // 전문헤더 클래스의 doExecute() 메소드를 호출한다.
        logger.debug("{} ▶ 전문 HEADER 수신 클래스의 doExecute() 메소드 호출......", logHeader);
        if (this.ll_telegram.doExecute() == false) {
            doErrorAction(this.ll_telegram, ErrorKeys.ERROR_EXECUTION, new String[] {strTelegramID, strLenTelegramClsName});
            this.ll_telegram.doPostExecute();
            return batchDoc;
        }

        //==============================================================================================
        // 전문헤더 클래스의 doValidation() 메소드를 호출한다.
        logger.debug("{} ▶ 전문 HEADER 수신 클래스의 doValidation() 메소드 호출......", logHeader);
        if (this.ll_telegram.doValidation() == false) {
            doErrorAction(this.ll_telegram, ErrorKeys.ERROR_RECV_VALIDATION);
            this.ll_telegram.doPostExecute();
            return batchDoc;
        }

        //==============================================================================================
        // 전문헤더 클래스의 doPostExecute() 메소드를 호출한다.
        logger.debug("{} ▶ 전문 HEADER 수신 클래스의 doPostExecute() 메소드 호출......", logHeader);
        if (this.ll_telegram.doPostExecute() == false) {
            doErrorAction(this.ll_telegram, ErrorKeys.ERROR_POST_EXECUTION, new String[] {strTelegramID, strLenTelegramClsName});
            return batchDoc;
        }

        //==============================================================================================
        // 전문헤더 클래스에서 오류 응답코드를 설정하고도 return true를 한 경우를 체크
        if (!this.ll_telegram.getResolveResponseCode().equals(TelegramKeys.RPS_HDB_RET_CODE_SUCCESS)) {
            doErrorAction(this.ll_telegram, "BECEAIFJM028", new String[] { strLenTelegramClsName });  //전문 HEADER 수신 클래스에서 오류 응답코드 설정 후 true 가 리턴되었습니다. 해당 클래스를 검토 바랍니다. ({1})
            return batchDoc;
        }

        //##############################################################################################
        // 이하 BODY 처리 부분                                                                         #
        //##############################################################################################

        //==============================================================================================
        // 수신용 텔레그램 인스탄스를 생성시킨다.
        logger.debug("{} ▶ 전문 BODY 수신 Telegram Class 생성...... ("+ strRecvTelegramClsName +")", logHeader);
        this.rv_telegram = (TelegramService)Class.forName(strRecvTelegramClsName).newInstance();
        if (this.rv_telegram == null) {
            doErrorAction(this.ll_telegram, ErrorKeys.ERROR_NO_CLASS_DEFINITION, new String[] {recvTelegramID, strRecvTelegramClsName});
            return batchDoc;
        }

        // 텔레그램인스탄스에 메시지 메타코드를 지정하고, EAIBatchMessage를 지정한다.
        this.rv_telegram.setEAIBatchMessage(batchDoc);
        this.rv_telegram.setTelegramCode(strRecvMsgMetaDstcd);
        this.rv_telegram.setNextStageInfo(curr_poss_stage);

        //==============================================================================================
        // 전문 Body 수신 클래스의 doPreExecute() 메소드를 호출한다. (전문필드 배열 생성)
        logger.debug("{} ▶ 전문 BODY 수신 클래스의 doPreExecute() 메소드 호출......", logHeader);
        if (this.rv_telegram.doPreExecute() == false) {
            doErrorAction(this.rv_telegram, ErrorKeys.ERROR_RECV_PRE_LENGTH, new String[] {recvTelegramID, strRecvTelegramClsName});
            return batchDoc;
        }

        //==============================================================================================
        // 전문결정용 텔레그램 (ll_telegram)의 수신필드 값을 실제 텔레그램(rv_telegram)으로 복사한다.
        logger.debug("{} ▶ (전문 HEADER 수신 데이터 -> 전문 BODY 수신 데이터) 복사......", logHeader);
        for (int i=0; i< this.ll_telegram.getFieldCount(); i++) {
            this.rv_telegram.setFieldDataByIndex(i, this.ll_telegram.getFieldDataByIndex(i));
        }

        //==============================================================================================
        // Header 전문필드 값 복사 후 전문 Body를 수신하기 전에 Header 값을 가지고 전처리할 메소드를 호출한다.
        logger.debug("{} ▶ 전문 BODY 수신 클래스의 doPreExecuteAfterCopy() 메소드 호출......", logHeader);
        if (this.rv_telegram.doPreExecuteAfterCopy() == false) {
            doErrorAction(this.rv_telegram, "BECEAIFJM024", new String[] {recvTelegramID, strRecvTelegramClsName});
            return batchDoc;
        }

        //==============================================================================================
        //더 읽어들일 필드가 있다면...
        if (this.ll_telegram.getFieldCount() < this.rv_telegram.getFieldCount()) {
            logger.debug("{} ▶ SOCKET 에서 전문 BODY 데이터 수신 대기중......", logHeader);
        }
        for (int i=this.ll_telegram.getFieldCount(); i<this.rv_telegram.getFieldCount(); i++) {

            //해당 필드 사이즈 조회 (integer 또는 'XX')
            String strFldLen = this.rv_telegram.getFieldLength(i);

            //정형 길이인 경우
            if (!strFldLen.trim().equalsIgnoreCase(TelegramService.FIELD_NO_LENGTH)) {

                //ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
                int nFldLen = Integer.parseInt(strFldLen);
                byte[] aField = new byte[nFldLen];
                int nRemainBytes = nFldLen;
                ByteArrayOutputStream bout = new ByteArrayOutputStream();
                TelegramItemVO item = TelegramManager.getInstance().getTelegramField(strRecvMsgMetaDstcd, i+1); //log용

                //Socket 에서 해당 필드 데이터 Read (필드 길이만큼 읽어들인다.)
                try {
                    while (nRemainBytes > 0) {
                        int nReadBytes = this.recvData(din, nTimeout, nRepeatCount, aField, nRemainBytes);  //한번에 nRemainBytes 길이만큼 읽지 않을수도 있음
                        bout.write(aField, 0, nReadBytes);
                        nRemainBytes -= nReadBytes;
                    }
                    logger.info("{}    ☞ 전문 BODY Formal 필드 수신 완료 => "+ (i+1) +". ["+ item.getFieldType() +"] ["+ item.getFieldLength() +"] ["+ new String(bout.toByteArray()) +"] ["+ item.getFieldName() +"]", logHeader);

                    //읽어들인 필드 데이터 저장
                    this.rv_telegram.setFieldDataByIndex(i, bout.toByteArray());
                    boutBody.write(bout.toByteArray());  //Stage Logging 용 (body formal 데이터)

                } catch (Exception ex) {
                    logger.error("{}    ☞ ★★★★★ 전문 BODY Formal 필드 수신 실패 ★★★★★ => "+ (i+1) +". ["+ item.getFieldType() +"] ["+ item.getFieldLength() +"] ["+ new String(aField) +"] ["+ item.getFieldName() +"]", logHeader, ex);
                    doErrorAction(ex, this.ll_telegram, "BECEAIFJM026", new String[] { item.getFieldName() });  //전문 BODY Formal 필드 [{1}] 수신 시 오류가 발생하였습니다.
                    return batchDoc;
                }
                //ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ

            //비정형 길이('XX')인 경우
            } else {

                //Informal 필드의 길이 계산 (읽어들일 길이 = '전문길이'필드 값 - Informal필드 이전에 읽어들인 전체 길이)
                int nInformalFldLen = this.rv_telegram.getCalculatedLength(i);
                if (nInformalFldLen < 0) {
                    doErrorAction(this.rv_telegram, ErrorKeys.ERROR_INFORMAL_FIELD_LENGTH, new String[] {recvTelegramID, strRecvTelegramClsName});
                    this.rv_telegram.doPostExecute();
                    return batchDoc;
                }

                //ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
                byte[] aInformalBuffer = new byte[4096];
                int nRemainBytes = nInformalFldLen;
                TelegramItemVO item = TelegramManager.getInstance().getTelegramField(strRecvMsgMetaDstcd, i+1); //log용

                //Socket 에서 Informal 필드 데이터 Read (계산된 필드 길이만큼 읽어들인다.)
                try {
                    while (nRemainBytes > 0) {
                        int nInformalBuffer = (nRemainBytes > 4096)? 4096 : nRemainBytes;
                        int nReadBytes = this.recvData(din, nTimeout, nRepeatCount, aInformalBuffer, nInformalBuffer);  //한번에 nInformalBuffer 길이만큼 읽지 않을수도 있음
                        nRemainBytes -= nReadBytes;

                        //읽어들인 데이터를 파일로 저장
                        boolean successSave = this.rv_telegram.doInformalField(i, aInformalBuffer, nReadBytes);
                        if (successSave == false) {
                            doErrorAction(this.rv_telegram, ErrorKeys.ERROR_INFORMAL_ACTION, new String[] {recvTelegramID, strRecvTelegramClsName});
                            this.rv_telegram.doPostExecute();
                            return batchDoc;
                        }
                    }
                    logger.info("{}    ☞ 전문 BODY Informal 필드 수신 완료 => "+ (i+1) +". ["+ item.getFieldType() +"] ["+ item.getFieldLength() +"(비정형)] ["+ nInformalFldLen +"(사이즈)] ["+ item.getFieldName() +"]", logHeader);

                } catch (Exception ex) {
                    logger.error("{}    ☞ ★★★★★ 전문 BODY Informal 필드 수신 실패 ★★★★★ => "+ (i+1) +". ["+ item.getFieldType() +"] ["+ item.getFieldLength() +"(비정형)] ["+ nInformalFldLen +"(사이즈)] ["+ item.getFieldName() +"]", logHeader, ex);
                    doErrorAction(ex, this.ll_telegram, "BECEAIFJM027", new String[] { item.getFieldName() });  //전문 BODY Informal 필드 [{1}] 수신 시 오류가 발생하였습니다.
                    return batchDoc;
                }
                //ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
            }//end if
        }//end for

        // 전문 body 수신 데이터 설정
        batchDoc.getBatchMsg().setTelegramBody(new String(boutBody.toByteArray()));

        //==============================================================================================
        // 전문 Body 수신 클래스의 doExecute() 메소드를 호출한다.
        logger.debug("{} ▶ 전문 BODY 수신 클래스의 doExecute() 메소드 호출......", logHeader);
        if (this.rv_telegram.doExecute() == false) {
            doErrorAction(this.rv_telegram, ErrorKeys.ERROR_EXECUTION, new String[] {recvTelegramID, strRecvTelegramClsName});
            this.rv_telegram.doPostExecute();
            return batchDoc;
        }

        //==============================================================================================
        // 전문 Body 수신 클래스의 doValidation() 메소드를 호출한다.
        logger.debug("{} ▶ 전문 BODY 수신 클래스의 doValidation() 메소드 호출......", logHeader);
        if (this.rv_telegram.doValidation() == false) {
            doErrorAction(this.rv_telegram, ErrorKeys.ERROR_RECV_VALIDATION);
            this.rv_telegram.doPostExecute();
            return batchDoc;
        }

        //==============================================================================================
        // 전문 Body 수신 클래스의 doPostExecute() 메소드를 호출한다.
        logger.debug("{} ▶ 전문 BODY 수신 클래스의 doPostExecute() 메소드 호출......", logHeader);
        if (this.rv_telegram.doPostExecute() == false) {
            doErrorAction(this.rv_telegram, ErrorKeys.ERROR_POST_EXECUTION, new String[] {recvTelegramID, strRecvTelegramClsName});
            return batchDoc;
        }

        batchDoc.getBatchMsg().getPhaseinfo().setControlCodeValue (this.rv_telegram.getOpCode()      );  //Stage Logging 용

        //==============================================================================================
        //현재 Telegram Component를 JobProcessData (PROCDATA_PRE_TELEGRAM) 에 저장해둔다.
        //(다음 수행할 Telegram Component 가 현재 데이터를 참조할 수 있도록)
        super.setPreviousTelegramComponent(this.rv_telegram);

        //==============================================================================================
        // 조건(전문종별코드,업무관리코드,거래구분코드,응답코드) 에 맞는 다음 단계 결정
        logger.debug("{} ▶ 조건(전문종별코드,업무관리코드,거래구분코드,응답코드) 에 따른 다음 단계 결정......", logHeader);
        this.nextPhaseInfoVO = this.rv_telegram.getNextStage();

        //조건에 맞는 다음 단계를 찾은 경우
        if (this.nextPhaseInfoVO != null) {
            batchDoc.getBatchMsg().getBody().setNextPhaseCode(nextPhaseInfoVO.getNextPhaseCode());
            logger.info("{}    ☞ [다음 단계 Pahse Code = "+ nextPhaseInfoVO.getNextPhaseCode() +"]", logHeader);
            return batchDoc;

        //조건에 맞는 다음 단계가 없는데 응답코드가 '정상'이 아니고, 작업유형이 응답송수신인 경우 오류전문을 송신한다.
        } else if (!this.rv_telegram.getSendResponseCode().equals(TelegramKeys.RPS_HDB_RET_CODE_SUCCESS) &&
                   (batchDoc.getBatchMsg().getHeader().getProcessType().equals(CommonKeys.PROCESS_RESPONSE_SEND) ||
                    batchDoc.getBatchMsg().getHeader().getProcessType().equals(CommonKeys.PROCESS_RESPONSE_RECEIVE))) {
            logger.error("{}    ☞ ★★★★★ 다음 단계 못 찾았음 ★★★★★ => 오류전문 전송", logHeader);
            this.nextPhaseInfoVO = (PhaseInfoVO)curr_poss_stage.get(0);
            batchDoc.getBatchMsg().getBody().setNextPhaseCode(nextPhaseInfoVO.getNextPhaseCode());
            logger.info("{}    ☞ [다음 단계 Pahse Code = "+ nextPhaseInfoVO.getNextPhaseCode() +"] (오류전문 송신용)", logHeader);
            return batchDoc;

        //조건에 맞는 다음 단계를 못찾은 경우
        } else {
            doErrorAction(this.rv_telegram, ErrorKeys.ERROR_NOTFOUND_NEXTSTAGE, new String[] {strRuleCode, strPhaseCode});
            return batchDoc;
        }
    }

    public int recvData(DataInputStream din, int timeoutMilisec, int timeoutRepeatCount,  byte[] buffer, int len) throws Exception
    {
        if (buffer == null || buffer.length == 0) throw new Exception("");

        int readBytes = 0;
        int timoutCount = 0;

        String recievedData = batchDoc.getBatchMsg().getBody().getRecvedMsg();
        int rlen = recievedData.getBytes().length;
        if ( rlen > len ) { // 이미 읽어 메모리에 가지고 있는 데이터가 읽어야 할 데이터 보다 많으면
            System.arraycopy( recievedData.getBytes(), 0, buffer, 0, len);
            batchDoc.getBatchMsg().getBody().setRecvedMsg( StringUtil.substring( recievedData, len));
            return len;
        } else{
            if ( rlen > 0 ){
                System.arraycopy( recievedData.getBytes(), 0, buffer, 0, rlen);
            }
            batchDoc.getBatchMsg().getBody().setRecvedMsg( "" );
        }
        if ( rlen == len )
            return len;
        int i = 0;
        while (true) {
            try {
            	i++;
                readBytes = din.read(buffer, rlen, len - rlen);
                if (readBytes < 0) {
                    String errMsg = ExceptionUtil.getErrorCode(ErrorKeys.ERROR_END_OF_STREAM);
                    throw new Exception(errMsg);  //대외기관으로부터 전문 수신 시 End-Of-Stream 에 도착하여 읽어들일 데이터가 없습니다. 해당 대외기관에 연락바랍니다.
                }
                if (logger.isInfo()) {
                	byte[] dumpBuffer = Arrays.copyOfRange(buffer, rlen, len - rlen);
                	logger.info("Recv <<<<<< {}", LoggingUtil.asciiDump(dumpBuffer));
                	logger.info("Recv <<<<<< {}", LoggingUtil.hexDump(dumpBuffer));
                }
                return readBytes+ rlen;  //socket 에서 읽어들인 byte 수 리턴 (1 이상)

            } catch (SocketTimeoutException ex) {
                timoutCount++;
                logger.warn("{"+logHeader+"} ☆☆☆☆☆ SocketTimeoutException 발생 !! ☆☆☆☆☆", ex);
                logger.warn("{"+logHeader+"} >>> [Timeout 발생회수 : "+ timoutCount +"], [Timeout 반복설정회수 : "+ timeoutRepeatCount +"], [soTimeout : "+ (timeoutMilisec/1000) +"]");
                if (timoutCount > timeoutRepeatCount) {
                    String errMsg = ExceptionUtil.getErrorCode(ex, ErrorKeys.ERROR_TIMEOUT_OVER, new String[] { timoutCount+"",  (timeoutMilisec/1000)+"" });
                    throw new Exception(errMsg);  //대외기관으로부터 전문 수신 시 Timeout 이 발생하였습니다 (읽어들인 데이터 없음). 해당 대외기관에 연락바랍니다. [Timeout회수:{1}, soTimeout:{2}초]
                }
                continue;
            } catch (java.io.IOException ex) { // 20240627
            	logger.warn("{"+logHeader+"} ★★★★★ Socket 에서 데이터 수신 시 IOExceptionIOException 발생 ",ex);
            	logger.warn("★★★★★ Socket 에서 데이터 수신 시 IOExceptionIOException 발생 ! readBytes:"+readBytes);
            	if(i < 5)
            		continue;
            	else {
            		String errMsg = ExceptionUtil.getErrorCode(ex, ErrorKeys.ERROR_EXCEPTION);
            		throw new Exception(errMsg);
            	}
            } catch (Exception ex) {  //throws IOException, NullPointerException, IndexOutOfBoundsException
                logger.error("{} ★★★★★ Socket 에서 데이터 수신 시 오류 발생 !! ★★★★★", logHeader, ex);
                String errMsg = ExceptionUtil.getErrorCode(ex, ErrorKeys.ERROR_EXCEPTION);
                /*
                System.out.println("recv socket                    : "+ socket                   );
                System.out.println("recv socket.isBound()          : "+ socket.isBound()         );
                System.out.println("recv socket.isClosed()         : "+ socket.isClosed()        );
                System.out.println("recv socket.isConnected()      : "+ socket.isConnected()     );
                System.out.println("recv socket.isInputShutdown()  : "+ socket.isInputShutdown() );
                System.out.println("recv socket.isOutputShutdown() : "+ socket.isOutputShutdown());
                */
                //예외발생시 socket 종료 (remote에서 socket을 끊어도 socket.isClosed() 가 false 를 반환할 수 있다.)
                bs_man.closeRunningSocket(strUUID);
                throw new Exception(errMsg);  //대외기관으로부터 전문 수신 시 Socket 오류가 발생하여 데이터를 읽어들일 수 없습니다.
            }
        }
    }
}