package com.eactive.eai.adapter.socket.service;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Properties;

import com.eactive.eai.adapter.AdapterManager;
import com.eactive.eai.adapter.AdapterVO;
import com.eactive.eai.adapter.socket.common.CommonLib;
import com.eactive.eai.adapter.socket.common.DiagLogger;
import com.eactive.eai.adapter.socket.config.ConfigurationContext;
import com.eactive.eai.adapter.socket.config.SocketAdapterManager;
import com.eactive.eai.batch.common.BatchDirUtil;
import com.eactive.eai.batch.common.CalendarUtil;
import com.eactive.eai.batch.common.CommonKeys;
import com.eactive.eai.batch.common.LogUtil;
import com.eactive.eai.batch.common.StringUtil;
import com.eactive.eai.batch.doc.BatchDoc;
import com.eactive.eai.batch.flowController.BatchTargetVO;
import com.eactive.eai.batch.flowController.FlowControllerManager;
import com.eactive.eai.batch.message.EAIBatchMsgManager;
import com.eactive.eai.batch.osd.OutsideManager;
import com.eactive.eai.batch.osd.OutsideVO;
import com.eactive.eai.batch.rule.dirInfo.BatchJobInfoVO;
import com.eactive.eai.batch.rule.dirInfo.DirInfoManager;
import com.eactive.eai.batch.rule.ruleinfo.RuleInfoManager;
import com.eactive.eai.batch.rule.ruleinfo.RuleInfoVO;
import com.eactive.eai.batch.running.BatchRunningJobManager;
import com.eactive.eai.batch.scheduler.SchedulerMessageManager;
import com.eactive.eai.batch.scheduler.SchedulerMessageVO;
import com.eactive.eai.common.exception.ExceptionUtil;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.inbound.ResponseHandler;

public class InboundClient extends Thread implements SocketService {

    protected ConfigurationContext  context;
    private boolean                 active;
    private SocketChannel           channel;

    protected long                  firstActivity;
    protected long                  lastActivity;

    private boolean                 retryFlag;

    private ByteBuffer              incomingMessage;
    private ByteBuffer              pollingMessage;

    private Logger                  logger;
    private static Logger           errLogger = SocketLogManager.getInstance().getErrLogger();
    AdapterVO                       adapter;

    // 응답 송/수신 각 배치작업을 구별하기 위한 ID
    //----------------------------------------------------------------
    //배치 응답송수신 전용, 전체 단계 작업에 대한 BatchMsg 객체 (Socket Coneection 내내 동일함)
    //(전제조건)  1. 배치 전체 단계 전문 Flow 에 대해서 동일한 Socket Connection이 사용되어야 함
    //           2. 전체 단계 Flow 종료시 해당 Socket Connection 이 종료되고 Inbound Server 가 종료되어야 함
    private BatchDoc batchMsgDoc;
    private byte[] MEGA_IN_MSG = { (byte) 0xff, (byte) 0xfb, (byte) 0x00, (byte) 0xff,
    		                       (byte) 0xfd, (byte) 0x00, (byte) 0xff, (byte) 0xfb,
    		                       (byte) 0x19, (byte) 0xff, (byte) 0xfd, (byte) 0x19 }; 
    private byte[] MEGA_OUT_MSG = { (byte) 0xff, (byte) 0xfd, (byte) 0x19 }; 
    private byte[] MEGA_EOR_MSG = { (byte) 0xff, (byte) 0xef };
    
    private boolean mega_init = false;
    //----------------------------------------------------------------

    public InboundClient(ConfigurationContext context) {

        String name      = this.getName();
        this.setName( context.getAdapterName() + "-CONNECTION" + name.substring( name.lastIndexOf("-")) );

        this.context     = context;
        active           = true;
        retryFlag        = true;

        incomingMessage  = null;

        logger           = SocketLogManager.getInstance().getLogger( context.getAdapterGroupName() );

    }

    private int getTraceLevel() {
        return context.getTraceLevel();
    }

    private long getTimeout() {
        return ( this.context.getTimeout() * 1000L );
    }

    private boolean isSocketReuse() {
        return this.context.getSocketReuse().equals( ConfigurationContext.TRUE_FLAG );
    }

    /**
    * @return Returns the context.
    */
    public ConfigurationContext getContext() {
        return context;
    }

    public void run() {
        //*** 어댑터에 대해 하나의 대외기관 연결정보(BJ05)만이 매핑되어 있어야 하는 것이 보장되어야 한다.
        logger.info("[InboundClient] Group ["+context.getAdapterGroupName()+"] Adapter["+context.getAdapterName()+"]");
        adapter = AdapterManager.getInstance().getAdapterVO(context.getAdapterGroupName(), context.getAdapterName());

        mega_init = false;
        
        int readLength = 0;
        try {
        	readLength	= adapter.getContext().getLlFieldIndex() + adapter.getContext().getLlFieldLength();
        } catch (Exception e) {}
        if ( readLength < 1 ){
          	readLength = 9;
        }

        firstActivity = System.currentTimeMillis();
        lastActivity  = firstActivity;

        String  socketInfo = "";


        while ( retryFlag ) {

            active = true;

            // channel 을 열고
            try {
                openChannel();
                socketInfo = this.getSocketInfo( this.getCurrentSocket() );
            } catch(Exception e) {
                String errMsg = CommonLib.getMessage("BECEAIASI002", new String[]{context.getAdapterName(), "openChannel", e.getMessage(), "", "" } );
                logger.error( errMsg, e );

                if ( retryFlag ) {
                    waitRecoverConnection();
                }
                continue;
            }

            if ( getTraceLevel() >= DiagLogger.INFO ) {
                logger.info( CommonLib.getMessage("BICEAIASI002", new String[]{ context.getAdapterName(), socketInfo , Thread.currentThread().getName() }) );
            }

            // active 상태에서 LOOP
            while ( active ) {
                try {
                	if ( !mega_init )
                		incomingMessage = ByteBuffer.allocate( readLength );
                    // length & message buffer clear
                    if ( incomingMessage != null ) {
                        incomingMessage.clear();

                        processReadMessage( incomingMessage.array() );// 29259819 npe
                    }
                    // 통계데이타 갱신
                    this.checkActivity();
                    lastActivity = System.currentTimeMillis();

                    // (DEBUG 레벨일 경우) 메시지수신 로그
                    if ( getTraceLevel() >= DiagLogger.DEBUG ) {
                        logger.debug( CommonLib.getMessage("BDCEAIASI001", new String[]{context.getAdapterName(), "RECV", CommonLib.getTimestamp(), socketInfo, CommonLib.getDumpMessage(incomingMessage.array())}));
                    }

                    try {
				        //logger.info( "[InboundClient][adapter.getContext().getLlFieldLength()]-->" + adapter.getContext().getLlFieldLength() + ")");
    					if ( adapter.getContext().getLlFieldLength() > 0  && !mega_init ){
    						int ll_len = adapter.getContext().getLlFieldLength();
    						byte[] ll_fld = new byte[ll_len];
    						System.arraycopy(incomingMessage.array(), adapter.getContext().getLlFieldIndex(), ll_fld, 0, adapter.getContext().getLlFieldLength());
    						
    						if ( byteCompare( ll_fld, MEGA_EOR_MSG )){
								continue;
    						}
    						if ( byteCompare( ll_fld, MEGA_IN_MSG )){
						        Socket socket = channel.socket();
								DataOutputStream dout = new DataOutputStream(socket.getOutputStream());
								dout.write(MEGA_OUT_MSG, 0, MEGA_OUT_MSG.length);
								mega_init = true;
								incomingMessage = ByteBuffer.allocate( 2 );
								continue;
    						}
    						if ( Integer.parseInt(new String(ll_fld)) == 0 ) continue;
    						// polling 메세지 처리
    				        //logger.info( "[InboundClient][adapter.getContext().isUsePollMsg()]-->" + adapter.getContext().isUsePollMsg() + ")");
    						if ( adapter.getContext().isUsePollMsg() ){
    							
    							int msglen = Integer.parseInt(new String(ll_fld));
    							pollingMessage = ByteBuffer.allocate(msglen);
    							try {
    				    			processReadMessage( pollingMessage.array() );
    							} catch (Exception e) {
    								logger.error( e.getMessage(), e);
    							}
    							//logger.debug("[IOboundClient][run()]polling Msg-->" + new String(pollingMessage.array()));
    							String pollmsg = new String(pollingMessage.array()); 
    							if ( pollmsg.startsWith("REQPOLL") || pollmsg.startsWith("HDRREQPOLL")){
    								byte[] resPoll = new byte[ll_len+msglen];
    								System.arraycopy(incomingMessage.array(), 0, resPoll, 0, ll_len);
    								System.arraycopy(pollingMessage.array(), 0, resPoll, ll_len, msglen);
    								if ( resPoll[ll_len+2] == 'Q' )
    									resPoll[ll_len+2] = 'S';
    								if ( resPoll[ll_len+5] == 'Q' )
    									resPoll[ll_len+5] = 'S';
    								
    						        Socket socket = channel.socket();
    								DataOutputStream dout = new DataOutputStream(socket.getOutputStream());
    								dout.write(resPoll, 0, resPoll.length);
//    								logger.debug("[IOboundClient][run()]resPoll Msg-->" + new String(resPoll));
    								continue;
    							}
    						}
    					}
    				} catch (Exception e) {
    					logger.error( e.getMessage(), e);
    				}

                    doResposeRecieve();

                    // Socket REUSE 모드가 아니라면 Connection Close
                    if ( !isSocketReuse() ) {
                        active = false;
                        closeChannel();
                    }
                } catch (SocketTimeoutException ex) {
                    checkRequest();
                    if ( adapter.getContext().getLlFieldLength() == 12){
						try {
							if ( channel != null ){
						        Socket socket = channel.socket();
								DataOutputStream dout;
								dout = new DataOutputStream(socket.getOutputStream());
								dout.write(MEGA_EOR_MSG, 0, MEGA_EOR_MSG.length);
							}
							continue;
						} catch (IOException e) {
		                    waitRecoverConnection();
		                    continue;
						}
                    }
                    long idleTimeOutMin = context.getSessionTimeout();
                    if ((idleTimeOutMin > 0) && this.idle(idleTimeOutMin * 60 * 1000)){
                        logger.debug( "InboundClient(" + this.getName() + "), SessionTimeout :" + context.getSessionTimeout()
                                      + ", timeout :" + context.getTimeout());
                        waitRecoverConnection();
                    }
                    continue;
                } catch( Exception e ) {
                    String msgEOF = ExceptionUtil.getErrorCode("BECEAIFJM010");
                    if ( !e.getMessage().equals(msgEOF)){
                        String errMsg = CommonLib.getMessage("BECEAIASI002", new String[]{context.getAdapterName(), "processReadMessage", e.getMessage(), "수신메시지", getReadingMessage() } );
                        if ( errMsg.length() > 0 ) {
                            logger.error( errMsg , e);

                            if ( logger != errLogger ) {
                                errLogger.error( errMsg, e );
                            }
                        }
                    }
                    //active = false;

                    waitRecoverConnection();
                    continue;
                }
            }// end of active
        }// end of retry

        // LOOP을 빠져나오면 (active가 아닌 경우), Channel 을 Close 한다.
        try {
            closeChannel();
        } catch( Throwable e ) {}

        SocketAdapterManager.getInstance().removeConnection( this );

        logger.info( CommonLib.getMessage("BICEAIASI003", new String[]{ context.getAdapterName(), socketInfo, Thread.currentThread().getName() }) );

        interrupt();

    } // end of startup ...

    private String  getReadingMessage() {
        String rMsg = "";
        if ( incomingMessage != null && incomingMessage.position() > 0 ) {
            byte[] dMsg = new byte[ incomingMessage.position() ];
            System.arraycopy( incomingMessage.array(), 0, dMsg, 0, dMsg.length );
            rMsg = CommonLib.getDumpMessage( dMsg );
        }
        return rMsg;
    }

    protected String  getSocketInfo(Socket socket) {
        StringBuffer connInfo = new StringBuffer();

        connInfo.append("Local Address=");
        connInfo.append( socket.getLocalAddress().getHostAddress()).append(":").append(socket.getLocalPort()).append(", ");
        connInfo.append("Remote Address=");
        connInfo.append( socket.getInetAddress().getHostAddress()).append(":").append(socket.getPort());

        return connInfo.toString();
    }

    public int getCurrentState() {
        return CommonLib.PROCESSING_PROTOCOL;
    }

    public boolean isConnected() {
        return true;
    }

    public void checkActivity() {
    }

    public boolean idle(long timeout) {
        long idleTime   = System.currentTimeMillis() - this.lastActivity;

        return ( idleTime > timeout );
    }

    public void setControl(Object control) {
    }

    public void notifyMessage() {
    }

    /**
     * Socket Recovery Manager에 의해 Connection Recovery가 되도록 기다린다.
     *
     */
    public synchronized void waitRecoverConnection() {
    	mega_init = false;

        try {
            if ( !retryFlag ) return;

            try { closeChannel(); } catch(Exception ie) {}
            if ( retryFlag && SocketAdapterManager.getInstance().addError( this ) ) {
                wait();
            }
        } catch ( InterruptedException e ) {}
    }

    /**
     * Socket Recovery Manager에 의해 recovery되면 Socket Recovery Manager에 의해 통지된다.     *
     */
    public synchronized void notifyRecoverConnection() {
        notify();
    }
    public void processReadMessage( byte[] buffer ) throws Exception {
    	processReadMessage( buffer, -1 );
    }

    public void processReadMessage( byte[] buffer, int rqTime ) throws Exception {
        if (buffer == null || buffer.length == 0) throw new Exception("");

        int readBytes = 0;
        boolean isEOF = false;
        Socket socket = channel.socket();
        DataInputStream din = new DataInputStream( socket.getInputStream());
        int len = buffer.length;
        int timeOut = (int)getTimeout();

        if ( timeOut < 1000 ) {
            timeOut = 1000;
        }
        if ( timeOut > 60000 ) {
            timeOut = 60000;
        }
        if ( rqTime > -1 ){
        	timeOut = rqTime;
        }

        logger.info( "[InboundClient][" + context.getAdapterName() + "]데이터 수신 대기 중(타임아웃:" + timeOut + ")");
        socket.setSoTimeout( timeOut );

        while (readBytes < len) {
            try {
                readBytes += din.read(buffer, readBytes, len-readBytes);
                if (readBytes < 0) {
                    isEOF = true;
                    break;
                }

            } catch (SocketTimeoutException ex) {
                throw ex;
            } catch (Exception ex) {  //throws IOException, NullPointerException, IndexOutOfBoundsException
                logger.error("★★★★★ Socket 에서 데이터 수신 시 오류 발생 !! ★★★★★", ex);
                String errMsg = ExceptionUtil.getErrorCode(ex, "BECEAIFJM011");
                throw new Exception(errMsg);  //대외기관으로부터 전문 수신 시 Socket 오류가 발생하여 데이터를 읽어들일 수 없습니다.
            }
        }

        if ( isEOF ) {
            String errMsg = ExceptionUtil.getErrorCode("BECEAIFJM010");
            throw new Exception(errMsg);  //대외기관으로부터 전문 수신 시 End-Of-Stream 에 도착하여 읽어들일 데이터가 없습니다. 해당 대외기관에 연락바랍니다.
        }

    } // end of readData

    public synchronized void shutdown() {

        active    = false;
        retryFlag = false;

        SocketAdapterManager.getInstance().removeError( this );
        try {
            notify();
        } catch( Exception e ) {}

        //to gracefully shutdown the thread
        try {
            closeChannel();
        } catch (Exception e) {}
        SocketAdapterManager.getInstance().removeError( this );

    }

    public boolean isActive() {
        return retryFlag;
    }

    public Socket getCurrentSocket() {
        return channel.socket();
    }

    public boolean connect() {
        if ( !retryFlag ) return true;

        try {
            openChannel();
            return true;
        } catch ( Exception e ) {
            return false;
        }
    }

    public void disconnect() {
    }

    private synchronized void openChannel() throws IOException {

        if ( channel == null || !channel.isOpen() ) {
            if ( !retryFlag ) return;

            channel            = SocketChannel.open();
            if ( context.getLocalHostName() != null && !context.getLocalHostName().equals("") ) {
                channel.socket().bind( new InetSocketAddress( InetAddress.getAllByName(context.getLocalHostName())[0], context.getLocalPortNumber() ) );
                channel.connect( new InetSocketAddress( context.getHostName(), context.getPortNumber() ) );
            } else {
                channel.connect( new InetSocketAddress( context.getHostName(), context.getPortNumber() ) );
            }

            channel.socket().setTcpNoDelay(true);

            if ( isSocketReuse() ) {
                channel.socket().setSoLinger(true, 100);
            }

            // Connection 시 통계자료 RESET
            this.firstActivity  = System.currentTimeMillis();
            this.lastActivity   = this.firstActivity;

            try {
                Thread.sleep( 300L );
            } catch( Exception e ) {}

            if ( !this.isConnected() ) {
                throw new IOException("Closed by Server");
            }

            if ( retryFlag ) {
                SocketAdapterManager.getInstance().addConnection( this );
            } else {
                closeChannel();
            }
        }
    }

    public String getRemoteIPAddress() {
        return null;
    }

    private synchronized void closeChannel() throws IOException {
        if(channel != null ) {
            channel.socket().close();
            channel.close();
        }
        channel = null;
    } //

    // 1. Connection 리스트정보 - AdapterGroupName,AdapterName,업무코드,INBOUND, CLIENT, SYNC, LocalIP:Port, RemoteIP:Port, 송신건수, 수신건수, 상태, 최초연결시각, 최종사용시각
    public String toString() {
        boolean status = true;
        StringBuffer strBuff = new StringBuffer();
        strBuff.append(context.getAdapterGroupName()).append(",");
        strBuff.append(context.getAdapterName()).append(",");
        strBuff.append(context.getBwkClsCd()).append(",").append(context.getBoundUsage()).append(",");
        strBuff.append(context.getSocketType()).append(",");
        strBuff.append(context.getResponseType()).append(",");

        try {
            strBuff.append( getCurrentSocket().getLocalAddress().getHostAddress()).append(":").append(getCurrentSocket().getLocalPort()).append(",");
        } catch( Throwable e ) {
            status = false;
            strBuff.append("?:?,");
        }

        try {
            strBuff.append( getCurrentSocket().getInetAddress().getHostAddress()).append(":").append(getCurrentSocket().getPort()).append(",");
        } catch( Throwable e ) {
            status = false;
            strBuff.append("?:?,");
        }

        strBuff.append( status ).append(",");
        strBuff.append( CommonLib.getCurrentTime( this.firstActivity )).append(",");
        strBuff.append(CommonLib.getCurrentTime( this.lastActivity ));

        return strBuff.toString();
    }

    public void setLastActivity() {
    }

    public long getFirstActivity() {
        return firstActivity;
    }

    public long getLastActivity() {
        return lastActivity;
    }

    private void doResposeRecieve(){
        PropManager manager = PropManager.getInstance();
        Properties  prop    = manager.getProperties( adapter.getPropGroupName() );
        String rcvFlowRuleCode = prop.getProperty("reponse.rule.code");
        String strUUID = null;

        //----------------------------------------------------------------
        //BatchMsgDoc 생성
        batchMsgDoc = EAIBatchMsgManager.createBatchMsgWithUUID(true);  //true:초기화함

        //BatchMsgDoc Layer, SubLayer, StartTime 설정
        EAIBatchMsgManager.updateBatchMsgWithStageTime(batchMsgDoc, CommonKeys.LAYER_SOCKET_SERVER, CommonKeys.SUB_LAYER_SOCKET_SERVER);

        try {
            if (adapter.getRqstRspnsDstcd().equalsIgnoreCase("A")) {
                // 응답수신 셋팅
                batchMsgDoc.getBatchMsg().getHeader().setProcessType(CommonKeys.PROCESS_RESPONSE_RECEIVE);
            } else {
                // 응답송신 셋팅
                batchMsgDoc.getBatchMsg().getHeader().setProcessType(CommonKeys.PROCESS_RESPONSE_SEND);
            }
            logger.info("[InboundClient]  ProcessCode >> " + adapter.getBatchProcessCode());
            ArrayList<OutsideVO> osd_array = OutsideManager.getInstance().getOutsideInfo(adapter.getBatchProcessCode());
            int i=0;
            for (i=0; i<osd_array.size(); i++) {
                OutsideVO tmpVO = osd_array.get(i);
                if (tmpVO.getOsdCode().equalsIgnoreCase(adapter.getBatchInstitutionCode())) {
                    break;
                }
            }
            OutsideVO   outside = null;
            if (i==osd_array.size()) {
                String errMsg = "[InboundClient] 대외기관 정보를 구할 수  없습니다.  배치업무코드 [" + adapter.getBatchProcessCode()+"] 대외기관["+adapter.getBatchInstitutionCode()+"]";
                batchMsgDoc.getBatchMsg().getBody().setErrorCode(99);
                batchMsgDoc.getBatchMsg().getBody().setErrorMsg(errMsg);
                throw new Exception(errMsg);
            } else {
                logger.info("OUSIDE INFO index [" + i + "]");
                outside = (OutsideVO) osd_array.get(i);
            }

            // BatchMsg 에 기본값 지정..
            batchMsgDoc.getBatchMsg().getHeader().setProcessCode(adapter.getBatchProcessCode());
            batchMsgDoc.getBatchMsg().getHeader().setProcessName(adapter.getBatchProcessName());
            batchMsgDoc.getBatchMsg().getHeader().setInstitutionCode(adapter.getBatchInstitutionCode());
            batchMsgDoc.getBatchMsg().getHeader().setInstitutionName(adapter.getBatchInstitutionName());
//            batchMsgDoc.getBatchMsg().getHeader().setInstituaionType(adapter.getBatchInstitutionType());
            batchMsgDoc.getBatchMsg().getBody().setPortNo(adapter.getBatchServerPortNo());
            batchMsgDoc.getBatchMsg().getBody().setTimeoutInterval(Integer.toString(adapter.getBatchInterMsgTimeout()));
            batchMsgDoc.getBatchMsg().getHeader().setFlowCode(rcvFlowRuleCode);
            batchMsgDoc.getBatchMsg().getHeader().setSystemConnCode(adapter.getBatchSystemConnCode());
            batchMsgDoc.getBatchMsg().getHeader().setRuleCode(rcvFlowRuleCode);
            RuleInfoVO vo = RuleInfoManager.getInstance().getRuleInfo(rcvFlowRuleCode);
            batchMsgDoc.getBatchMsg().getHeader().setRuleDesc((vo!=null)? vo.getDesc() : "Rule Code not found in the TSEAIBR02");
			String recvdMsg = new String(incomingMessage.array());
			if ( adapter.getContext().isUsePollMsg() ){
				recvdMsg = new String(incomingMessage.array()) + new String(pollingMessage.array());
			}
			batchMsgDoc.getBatchMsg().getBody().setRecvedMsg(recvdMsg);


//            // 응답송신기능의 추가를 위해 일부 기능 수정 함.
//            if (adapter.getRqstRspnsDstcd().equalsIgnoreCase("C") ) {
//                // 응답송신 이면
//                String strPathName = BatchDirUtil.getResSendRealDir();
//                if ( strPathName.charAt(strPathName.length()-1) != '/' ) {
//                    strPathName = strPathName + '/';
//                }
//                BatchTargetVO btVO = FlowControllerManager.getInstance().getBatchTargetInfo(outside.getBatchCode(), outside.getOsdCode());
//                StringBuffer sb = new StringBuffer();
//
//                sb.append(btVO.getProcessCode());
//                sb.append("/");
//                sb.append(btVO.getOrganCode());
//                strPathName = strPathName + sb.toString();
//                logger.debug("[응답송신] PATH NAME = [" + strPathName + "]");
//
//                File ff = new File(strPathName);
//                if (!ff.exists()) {
//                    logger.warn("응답송신용 디렉토리 [" + strPathName + "] 이 없습니다.");
//                    throw new Exception(ExceptionUtil.getErrorCode("BWCEAIASI003"));
//                }
//                File[] fileList = ff.listFiles();
//                String strFileName = "";
//                long lnFileSize = -1;
//                if (fileList.length> 0) {
//                    for (int i1=0; i1<fileList.length; i1++) {
//                        if ( fileList[i1].isFile()) {
//                            strFileName = fileList[i1].getName();
//                            lnFileSize = fileList[i1].length();
//                            break;
//                        }
//                    }
//                }
//
//                batchMsgDoc.getBatchMsg().getHeader().setFlowCode(outside.getSendRuleCode());
//                batchMsgDoc.getBatchMsg().getHeader().setRuleCode(outside.getSendRuleCode());
//                batchMsgDoc.getBatchMsg().getHeader().setRuleDesc(RuleInfoManager.getInstance().getRuleInfo(outside.getSendRuleCode()).getDesc());
//
//                batchMsgDoc.getBatchMsg().getHeader().setFilePath(strPathName);
//                batchMsgDoc.getBatchMsg().getHeader().setFileName(strFileName);
//                batchMsgDoc.getBatchMsg().getHeader().setRenamedFileName(strFileName);
//                batchMsgDoc.getBatchMsg().getHeader().setFileSize(lnFileSize);
//            }
//            else {
//                //응답 수신 디렉토리 설정
//                String strPathName = BatchDirUtil.getResponseRealDir();
//                if ( strPathName.charAt(strPathName.length()-1) != '/' ) {
//                strPathName = strPathName + '/';
//                }
//                BatchTargetVO btVO = FlowControllerManager.getInstance().getBatchTargetInfo(outside.getBatchCode(), outside.getOsdCode());
//                StringBuffer sb = new StringBuffer();
//
//                sb.append(btVO.getProcessName()).append("_").append(btVO.getProcessCode());
//                sb.append("/");
//                sb.append(btVO.getOrganName()).append("_").append(btVO.getOrganCode());
//                strPathName = strPathName + sb.toString();
//                logger.debug("[응답수신] PATH NAME = [" + strPathName + "]");
//                batchMsgDoc.getBatchMsg().getHeader().setFilePath(strPathName);
//                // <<< 응답수신 디렉토리 설정
//            }

            //응답 수신 디렉토리 설정
            String strPathName = BatchDirUtil.getResponseRealDir();
            if ( strPathName.charAt(strPathName.length()-1) != '/' ) {
            strPathName = strPathName + '/';
            }
            BatchTargetVO btVO = FlowControllerManager.getInstance().getBatchTargetInfo(outside.getBatchCode(), outside.getOsdCode());
            StringBuffer sb = new StringBuffer();

            sb.append(btVO.getProcessCode());
            sb.append("/");
            sb.append(btVO.getOrganCode());
            strPathName = strPathName + sb.toString();
            logger.debug("[응답수신] PATH NAME = [" + strPathName + "]");
            batchMsgDoc.getBatchMsg().getHeader().setFilePath(strPathName);
            // <<< 응답수신 디렉토리 설정
            
//            batchMsgDoc.getBatchMsg().getHeader().setProtocol("TCP/IP");
            batchMsgDoc.getBatchMsg().getHeader().setUserID(adapter.getLnkID());
            batchMsgDoc.getBatchMsg().getHeader().setUserPassword(adapter.getLnkPwd());
            batchMsgDoc.getBatchMsg().getHeader().setBlockSize(outside.getBlkSize()); //TSEAIBJ06에 필드추가
            batchMsgDoc.getBatchMsg().getHeader().setSequenceSize(outside.getSequenceSize()); //TSEAIBJ06에 필드추가
            batchMsgDoc.getBatchMsg().getHeader().setPacketSize(outside.getPacketSize()); // 20060411 추가
            batchMsgDoc.getBatchMsg().getHeader().setRemoteIP(context.getHostName());
            batchMsgDoc.getBatchMsg().getHeader().setPort(context.getPortNumber()+"");
            //----------------------------------------------------------------
            logger.info("**** USER ID > [" + batchMsgDoc.getBatchMsg().getHeader().getUserID());
            logger.info("**** PASS WD > [" + batchMsgDoc.getBatchMsg().getHeader().getUserPassword());
            logger.info("**** BLOCK   > [" + batchMsgDoc.getBatchMsg().getHeader().getBlockSize());
            logger.info("**** SEQ SIZ > [" + batchMsgDoc.getBatchMsg().getHeader().getSequenceSize());
            logger.info("**** PACKET  > [" + batchMsgDoc.getBatchMsg().getHeader().getPacketSize());

            //단계 종료시간 설정
            batchMsgDoc.getBatchMsg().getBody().setPhaseEndTime(CalendarUtil.getCurrentTimeNoDash());

	        // 기관별 로그
	        Logger.addInstLog(batchMsgDoc);

            //응답수신에 대한 최초 DB로그. StartLog 호출.
            LogUtil.setStartLog(batchMsgDoc);

            strUUID = batchMsgDoc.getBatchMsg().getHeader().getUUID();

            // BatchRunningJobManager 에 Running Job 등록
            logger.info("InboundClient : BatchRunningJobManager 에 응답송수신 작업 등록......");

            BatchRunningJobManager.getInstance().addRunningJobInfo(strUUID, this, batchMsgDoc);
            logger.info("InboundClient : FlowController JPD 호출 시작......");
            ResponseHandler handler = new ResponseHandler();
            this.batchMsgDoc = handler.execute(this.batchMsgDoc);
            logger.info("InboundClient : FlowController JPD 호출 완료 !!");
            String endComp = this.batchMsgDoc.getBatchMsg().getPhaseinfo().getPhaseType();
            if ( !"END".equals( endComp ) ) {
                logger.info( "FlowController JPD 호출결과 정상 종료가 아님 --> 소켓 연결 종료" );
                waitRecoverConnection();
            }

        } catch (Exception ex) {
            try {
                // 파일로그 처리
                String errMsg = ExceptionUtil.getErrorCode(ex, "BECEAIASI010", new String[] {batchMsgDoc.getBatchMsg().getHeader().getUUID()});
                logger.error(errMsg, ex);   //응답수신 Socket Server 배치메시지 생성 및 정보 설정시 오류가 발생하였습니다. [UUID: {1}]

                // DB로그 처리
                batchMsgDoc.getBatchMsg().getBody().setPhaseEndTime(CalendarUtil.getCurrentTimeNoDash());
                LogUtil.setErrorLog(batchMsgDoc, errMsg);
                waitRecoverConnection();
            } catch (Exception e) {
                logger.error("[Socket Server] ★★★★★ 응답수신 Socket Server 예외 처리 중 에러 !! ★★★★★", e);
            }
        } finally {
            // BatchRunningJobManager 에서 Running Job 제거
            logger.info("InboundClient : BatchRunningJobManager 에서 응답송수신 작업 제거......");
            BatchRunningJobManager.getInstance().removeRunningJobInfo(strUUID);
            lastActivity = System.currentTimeMillis();
        }
    }

    private void checkRequest(){

		if ( context.isResponseOnly()) return;

		String processCode         = context.getAdapterVO().getBatchProcessCode();
        String institutionCode     = context.getAdapterVO().getBatchInstitutionCode();
        SchedulerMessageVO info;

        while ( true ){
            try{
                info = SchedulerMessageManager.getInstance().getExecutableJobFromQueue(processCode, institutionCode );
                if ( info == null ) {
                    return;
                }

                logger.info("[InboundClient]  ProcessCode >> " + adapter.getBatchProcessCode());
                ArrayList<OutsideVO> osd_array = OutsideManager.getInstance().getOutsideInfo(adapter.getBatchProcessCode());
                int i=0;
                for (i=0; i<osd_array.size(); i++) {
                    OutsideVO tmpVO = osd_array.get(i);
                    if (tmpVO.getOsdCode().equalsIgnoreCase(adapter.getBatchInstitutionCode())) {
                        break;
                    }
                }
                OutsideVO   outside = null;
                if (i==osd_array.size()) {
                    String errMsg = "[InboundClient] 대외기관 정보를 구할 수  없습니다.  배치업무코드 [" + adapter.getBatchProcessCode()+"] 대외기관["+adapter.getBatchInstitutionCode()+"]";
                    batchMsgDoc.getBatchMsg().getBody().setErrorCode(99);
                    batchMsgDoc.getBatchMsg().getBody().setErrorMsg(errMsg);
                    throw new Exception(errMsg);
                } else {
                    logger.info("OUSIDE INFO index [" + i + "]");
                    outside = (OutsideVO) osd_array.get(i);
                }

                //해당 작업을 Job_Queue 테이블에서 Job_Processing 테이블로 이동
                SchedulerMessageManager.getInstance().moveJobQueueToHandle(info);

                //BatchMsgDoc 생성
                batchMsgDoc = EAIBatchMsgManager.createBatchMsg(true);  //true:초기화함

                //BatchMsgDoc Layer, SubLayer, StartTime 설정
                EAIBatchMsgManager.updateBatchMsgWithStageTime(batchMsgDoc, CommonKeys.LAYER_SCHEDULER, CommonKeys.SUB_LAYER_SCHEDULER_PROCESSING);

                //BatchMsgDoc 값 설정
                batchMsgDoc.getBatchMsg().getHeader().setUUID(info.getUUID());
                batchMsgDoc.getBatchMsg().getHeader().setProcessCode(info.getProcessCode());
                batchMsgDoc.getBatchMsg().getHeader().setProcessName(info.getProcessName());
                batchMsgDoc.getBatchMsg().getHeader().setProcessType(info.getProcessType());
                batchMsgDoc.getBatchMsg().getHeader().setInstitutionCode(info.getInstitutionCode());
                batchMsgDoc.getBatchMsg().getHeader().setInstitutionName(info.getInstitutionName());
                batchMsgDoc.getBatchMsg().getHeader().setScheduleCode(info.getScheduleCode());
                batchMsgDoc.getBatchMsg().getHeader().setScheduleSTime(info.getStartTime());
                batchMsgDoc.getBatchMsg().getHeader().setScheduleETime(info.getEndTime());
                batchMsgDoc.getBatchMsg().getHeader().setFilePath(info.getFilePath());
                batchMsgDoc.getBatchMsg().getHeader().setFileName(info.getFileName());
                batchMsgDoc.getBatchMsg().getHeader().setRenamedFileName(info.getRenamedFileName());
                batchMsgDoc.getBatchMsg().getHeader().setJobCode(info.getJobCode());
                batchMsgDoc.getBatchMsg().getHeader().setBizCode(info.getBizCode());
                batchMsgDoc.getBatchMsg().getHeader().setFlowCode(info.getFlowRuleCode());
                batchMsgDoc.getBatchMsg().getHeader().setRuleCode(info.getFlowRuleCode());
                batchMsgDoc.getBatchMsg().getHeader().setInstitutionName(info.getInstitutionName());
//                batchMsgDoc.getBatchMsg().getHeader().setInstituaionType(info.getInstitutionType());
                batchMsgDoc.getBatchMsg().getHeader().setBlockSize(outside.getBlkSize());
                batchMsgDoc.getBatchMsg().getHeader().setSequenceSize(outside.getSequenceSize());
                batchMsgDoc.getBatchMsg().getHeader().setPacketSize(outside.getPacketSize());
                batchMsgDoc.getBatchMsg().getHeader().setUserID(adapter.getLnkID());
                batchMsgDoc.getBatchMsg().getHeader().setUserPassword(adapter.getLnkPwd());
                batchMsgDoc.getBatchMsg().getHeader().setRemoteIP(context.getHostName());
                batchMsgDoc.getBatchMsg().getHeader().setPort(context.getPortNumber()+"");
                batchMsgDoc.getBatchMsg().getHeader().setSystemConnCode(info.getSystemConnCode());
//                batchMsgDoc.getBatchMsg().getHeader().setProtocol("TCP/IP");
                batchMsgDoc.getBatchMsg().getHeader().setRequestResponse(info.getRqstRspnsDstcd());
                batchMsgDoc.getBatchMsg().getHeader().setHdrInfoName(info.getHdrInfoName());
                RuleInfoVO vo = RuleInfoManager.getInstance().getRuleInfo(info.getFlowRuleCode());
                batchMsgDoc.getBatchMsg().getHeader().setRuleDesc((vo != null)? vo.getDesc() : "Rule Code not found in the TSEAIBR02");

                // 레코드 크기 추가 2009.02.03
                String jobCode = batchMsgDoc.getBatchMsg().getHeader().getJobCode();
                BatchJobInfoVO bjvo = DirInfoManager.getInstance().getFileInfo( jobCode );
                batchMsgDoc.getBatchMsg().getHeader().setRecLen((int)bjvo.getFileRecSize());

                // 20120601 추가 -- 시작
                if ( info.getProcessType().equals(CommonKeys.PROCESS_REQUEST_SEND) ||
                		info.getProcessType().equals(CommonKeys.PROCESS_RESPONSE_SEND ) ){
                    String hdrInfoName = info.getHdrInfoName();
                    batchMsgDoc.getBatchMsg().getHeader().setHdrInfoName(hdrInfoName);
                    batchMsgDoc.getBatchMsg().getHeader().setTotRecCnt(StringUtil.getTotRecCountFromHeader(hdrInfoName));
                    batchMsgDoc.getBatchMsg().getHeader().setBaseDate(StringUtil.getBaseDateFromHeader(hdrInfoName));
                    batchMsgDoc.getBatchMsg().getHeader().setFileSize(batchMsgDoc.getBatchMsg().getHeader().getRecLen() * batchMsgDoc.getBatchMsg().getHeader().getTotRecCnt());
                }
                // 20120601 추가 -- 여기까지

                //재처리 플래그 설정..
                if (info.getRecvRetryFlag().equals("1")){
                    batchMsgDoc.getBatchMsg().getHeader().setReqSendRetryFlag(true);
                    logger.debug("[스케쥴 핸들러] ■ 요구송수신 JPD 호출: UUID ["+info.getUUID()+"]의 RetryFlag 를 true로 설정합니다." );

                }else {
                    batchMsgDoc.getBatchMsg().getHeader().setReqSendRetryFlag(false);
                    logger.debug("[스케쥴 핸들러] ■ 요구송수신 JPD 호출: UUID ["+info.getUUID()+"]의 RetryFlag 를 false로 설정합니다." );
                }

                batchMsgDoc.getBatchMsg().getBody().setSubUUID(info.getSubUUID());
                batchMsgDoc.getBatchMsg().getBody().setSubLayerCode(CommonKeys.SUB_LAYER_SCHEDULER_PROCESSING);

                try {
                    LogUtil.updateLogMaster(batchMsgDoc);
				} catch (Exception e) {
				}

                logger.info("[InboundClient] ■ BatchRunningJobManager 등록완료 ");
                String strUUID = batchMsgDoc.getBatchMsg().getHeader().getUUID();

                // BatchRunningJobManager 에 Running Job 등록
                logger.info("InboundClient : BatchRunningJobManager 에 응답송수신 작업 등록......");
                BatchRunningJobManager.getInstance().addRunningJobInfo(strUUID, this, batchMsgDoc);

                try {
                	// TimeoutException 기관로거 설정
                	Logger.addInstLog(batchMsgDoc);
                    //===================================================
                    logger.info("InboundClient : FlowController JPD 호출 시작......");
                    ByteBuffer tmpBuf = null;
                    try {
                    	tmpBuf = ByteBuffer.allocate(4096);
                    	processReadMessage( tmpBuf.array(), 500);
					} catch (Exception e) {
						if(tmpBuf != null)
							logger.warn( e.getMessage()+ new String(tmpBuf.array()), e );
						else // 20250910 npe
							logger.warn( e.getMessage(), e );
					}
                    ResponseHandler handler = new ResponseHandler();
                    this.batchMsgDoc = handler.execute(this.batchMsgDoc);
                    logger.info("InboundClient : FlowController JPD 호출 완료 !!");
                    String endComp = this.batchMsgDoc.getBatchMsg().getPhaseinfo().getPhaseType();

                    if ( !"END".equals( endComp) ) {
                        logger.info( "FlowController JPD 호출결과 EEND 소켓 연결 종료" );
                        waitRecoverConnection();
                    }
                    //===================================================

                } finally {
                    // BatchRunningJobManager 에서 Running Job 제거
                    logger.info("InboundClient : BatchRunningJobManager 에서 요구답송수신 작업 제거......");
                    BatchRunningJobManager.getInstance().removeRunningJobInfo(strUUID);
                    lastActivity = System.currentTimeMillis();
                }
            } catch ( Exception e){
            	logger.error(e.getMessage(), e);
                return;
            }
        }
    }
    
    private boolean byteCompare( byte[] arr1, byte[]arr2){
    	for ( int inx = 0; inx < Math.min(arr1.length,arr2.length); inx++)
    		if ( arr1[inx] != arr2[inx])
    			return false;
    	return true;
    }
}
