package com.eactive.eai.inbound.processor;

import java.util.Calendar;
import java.util.Properties;
import java.util.Set;

import com.eactive.eai.common.TransactionContextKeys;
import com.eactive.eai.common.context.ElinkTransactionContext;
import com.eactive.eai.common.util.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;

import com.eactive.eai.authserver.service.OAuth2Manager;
import com.eactive.eai.authserver.vo.ClientVO;
import com.eactive.eai.adapter.AdapterGroupVO;
import com.eactive.eai.adapter.AdapterManager;
import com.eactive.eai.adapter.Keys;
import com.eactive.eai.adapter.http.HttpStatusException;
import com.eactive.eai.adapter.http.client.HttpClientAdapterServiceKey;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceSupport;
import com.eactive.eai.common.bizkey.BizKeyManager;
import com.eactive.eai.common.bizkey.BizKeyVO;
import com.eactive.eai.common.dao.DAOFactory;
import com.eactive.eai.common.exception.ExceptionHandler;
import com.eactive.eai.common.exception.ExceptionUtil;
import com.eactive.eai.common.inflow.Bucket;
import com.eactive.eai.common.inflow.InflowControlDAO;
import com.eactive.eai.common.inflow.InflowControlUtil;
import com.eactive.eai.common.inflow.InflowGroupVO;
import com.eactive.eai.common.inflow.InflowTargetVO;
import com.eactive.eai.common.logger.EAILogSender;
import com.eactive.eai.common.message.EAIMessage;
import com.eactive.eai.common.message.EAIMessageKeys;
import com.eactive.eai.common.message.EAIMessageManager;
import com.eactive.eai.common.message.MessageType;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.restrict.RestrictInfoManager;
import com.eactive.eai.common.restrict.RestrictInfoVO;
import com.eactive.eai.common.routing.FlowRouter;
import com.eactive.eai.common.routing.RouteKeys;
import com.eactive.eai.common.server.EAIServerManager;
//import com.eactive.eai.common.stdmessage.STDMessageManager;
import com.eactive.eai.common.stdmessage.STDMessageMapper;
import com.eactive.eai.inbound.action.ActionException;
import com.eactive.eai.inbound.action.ActionFactory;
import com.eactive.eai.inbound.action.RequestAction;
import com.eactive.eai.inbound.error.InboundErrorKeys;
import com.eactive.eai.message.StandardMessage;
import com.eactive.eai.message.StandardMessageUtil;
import com.eactive.eai.message.manager.StandardMessageManager;
import com.eactive.eai.message.mapper.MessageMapper;
import com.eactive.eai.message.parser.StandardReader;
import com.eactive.eai.message.service.InterfaceMapper;
import com.eactive.eai.util.SimHeaderUtil;
import com.ext.eai.common.stdmessage.STDMessageKeys;

//import org.apache.commons.lang3.StringUtils;
//import org.springframework.http.HttpStatus;

//import java.util.Calendar;
//import java.util.Properties;

/**
 * 1. 기능 : Inbound Adapter를 통해 전달 받은 메시지를 EAIMessage로 변환하고
 *           FlowControl로 전달하는 기능을 제공한다.
 * 2. 처리 개요 :
 *     - ExtMessage 생성
 *     - EAIMessage 생성
 *     - 요청 거래로깅
 *     - FlowControl 호출
 *     - 응답 거래로깅
 * 3. 주의사항
 *
 * @author  :
 * @version : v 1.0.0
 * @see :
 * @since :
 *  :
 */

public class RequestProcessor extends RequestProcessorSupport {

	protected static String instid = null;
	protected static String serverName = null;
	//private static boolean isPEAIServer = true;

	static {
		EAIServerManager eaiServerManager = EAIServerManager.getInstance();
		serverName = eaiServerManager.getLocalServerName();

		//isPEAIServer = eaiServerManager.isPEAIServer();
		instid = eaiServerManager.getGroupInstId();
	}
	
	protected static boolean httpHeaderLogMode = HttpAdapterExtraLogUtil.isHttpHeaderMode();
	
	protected ProcessVO setVO(Properties prop,long msgRcvTm) throws RequestProcessorException{
		String actionName        = prop.getProperty(Processor.REQUEST_ACTION);
		String adapterGroupName  = prop.getProperty(Processor.ADAPTER_GROUP_NAME);
		String adapterName       = prop.getProperty(Processor.ADAPTER_NAME);
		String adptrMsgPtrnCd = prop.getProperty(Processor.STD_MESSAGE);

		AdapterManager adapterManager = AdapterManager.getInstance();
		AdapterGroupVO adptGrpVO = adapterManager.getAdapterGroupVO(adapterGroupName);

		String adptrMsgType = "";
		String inAdapterOsidInstiNo = "";

		if(adptGrpVO == null) {
			logger.warn("Inbound Adapter is NULL - " +adapterGroupName);
			throw new RequestProcessorException("inbound adapter is null : "+ adapterGroupName);
		}

		adptrMsgType = adptGrpVO.getMessageType();

		// Adapter의 기관코드를 얻어온다.
		inAdapterOsidInstiNo = adptGrpVO.getOsidInstiNo();
        // Adapter의 텔러번호를 얻어온다. ※JBBEXT※ 추가필요 20210202
        String inAdapterUserEmpid = "";//adptGrpVO.getUserEmpid();
		String bidInterface = adptGrpVO.getBidInterface();
		String testMasterYn = adptGrpVO.getTestMasterYn();

		// UUID 생성 : UUID에서 - 없는 32자리
		String uuid = prop.getProperty(TransactionContextKeys.TRANSACTION_UUID);
		uuid = uuid == null ? UUIDGenerator.getUUID().toString().replaceAll("-", "") : uuid;
		// UUID 생성 : UUID = server구분4자리 + UUID
		/*
		String uuid = "";
		uuid = UUIDGenerator.getUUID().toString().replaceAll("-", "");

//		if(instid != null) {
//			uuid = instid + UUIDGenerator.getUUID();
//			if (logger.isDebug()) logger.debug("RequestProcessor] UUID ["+uuid+"]");
//		}
//		else {
//			String rspErrorCode = "RECEAIIRP030";
//			String rspErrorMsg = ErrorCodeHandler.getMessage(rspErrorCode);
//			if (logger.isError()) logger.error(rspErrorMsg);
//			throw new RequestProcessorException(rspErrorMsg);
//		}
		*/

		ProcessVO vo = new ProcessVO();
		vo.setAdapterGroupName(adapterGroupName);
		vo.setAdapterName(adapterName);
		vo.setActionName(actionName);
		vo.setStdMsgTypeCode(adptrMsgPtrnCd);
		vo.setTasStarted(false);
		vo.setAdptrMsgType(adptrMsgType);
		vo.setInAdapterOsidInstiNo(inAdapterOsidInstiNo);
		vo.setUuid(uuid);
		vo.setServerName(serverName);
		vo.setBidInterface(bidInterface);
		vo.setErrTm(msgRcvTm);
		vo.setTestMasterYn(testMasterYn);
		vo.setInAdapterUserEmpid(inAdapterUserEmpid);

		return vo;
	}


	/**
	 * 1. 기능 : Inbound Adapter를 통해 전달 맏은 메시지를 EAIMessage로 변환하고
	 *           FlowControl로 전달하는 기능을 제공한다.
	 * 2. 처리 개요 :
	 *     - STDMessage 생성
	 *     - EAIMessage 생성
	 *     - 요청 거래로깅
	 *     - FlowControl 호출
	 *     - 응답 거래로깅
	 * 3. 주의사항
	 *
	 * @param message Object 타입의 요청메시지
	 * @param prop 호출시 전달할 Property 값(추후 확장을 위해)
	 * @param msgRcvTm 메시지 수신시각
	 * @return Object
	 * @throws Exception
	 **/
	protected Object hookup(Object message, Properties prop, long msgRcvTm) throws Exception {
		EAIMessage retEaiMsg =  null;

		ProcessVO vo = setVO(prop, msgRcvTm);
		prop.setProperty(TransactionContextKeys.TRANSACTION_UUID, vo.getUuid());

		String inboundCharset = prop.getProperty(Keys.CHARSET_KEY_IN);

		// Unknown Message 로깅시 표준메시지일 경우 원본 자체를 남기기 위해 변수로 저장한다.
		// logUnknownMessage 호출시 사용하도록 함
		Object orgMessage = null;
		try {
			if (message instanceof String) {
				orgMessage = message;
			} else if (message instanceof byte[]) {
//				orgMessage = ObjectUtil.deepCopy(message);
				orgMessage = (byte[])message;
			}
		} catch (Exception e1) {
			logger.error("deep copy error : " + e1.getMessage(), e1);
		}

		// 2. StandardMessage 생성
		EAIServerManager eaiServerManager = EAIServerManager.getInstance();
		StandardMessage standardMessage = null;
		StandardMessageManager standardManager = StandardMessageManager.getInstance();
		StandardReader reader = null;
		InterfaceMapper mapper = standardManager.getMapper();
		StandardMessage subMessage = null;

		if(Keys.IF_STANDARD.equals(vo.getStdMsgTypeCode())) {
			if (logger.isDebug()) logger.debug("RequestProcessor] Get Message from INPUT MESSAGE");
			try {
		        standardMessage = standardManager.getStandardMessage();
		        reader = standardManager.getReader(vo.getAdptrMsgType());
				reader.parse(standardMessage, message);
				// 표준전문 파싱 후 어댑터의 charset을 설정한다.
				standardMessage.setBizDataCharset(inboundCharset);
				standardManager.getMessageCoordinator().coordinateAfterParsing(standardMessage, vo, prop);

			} catch(Exception e) {
				vo.setRspErrorCode("RECEAIIRP008");
				vo.setRspErrorMsg(ExceptionUtil.make(e, vo.getRspErrorCode(), new String[]{ e.getMessage()}));
				vo.setErrTypeCode(InboundErrorKeys.IN_UNKNOWN);
				if (logger.isError()) logger.error(vo.getRspErrorMsg(), e);
				logUnknownMessage(vo,orgMessage);
				throw new RequestProcessorException(vo.getRspErrorMsg());
			}
		}
		else {
			try {
				if(eaiServerManager.isTASEnabledEAIServer() && Keys.TESTMASTER_TAS.equals(vo.getTestMasterYn())) { //비표준 시뮬레이터
					if (logger.isDebug()) logger.debug("RequestProcessor] Get STDMessage from SIM Header data");
					if (message instanceof byte[]) {
						vo      = SimHeaderUtil.getSimHeader(vo, (byte[])message);
						message = SimHeaderUtil.getSimData((byte[])message);

						String adpterGroup = vo.getAdapterGroupName();//여기서는 SIM 어댑터
						//인터페이스ID로 어댑터명  추출
						EAIMessageManager eaiMsgManager = EAIMessageManager.getInstance();
						EAIMessage tmpMsg = eaiMsgManager.getEAIMessage(vo.getEaiSvcCd());
						if (tmpMsg != null){
							adpterGroup = tmpMsg.getSngSysItfTp(); //인터페이스에 설정된 어댑터로 변경
						}

						if (StringUtils.isNotBlank(adpterGroup)) {
							AdapterGroupVO gvo = AdapterManager.getInstance().getAdapterGroupVO(adpterGroup);
							String inAdapterUserEmpid = gvo.getOsidInstiNo(); // 사용자ID로 변경되야됨
							vo.setInAdapterUserEmpid(inAdapterUserEmpid);
						}
						// read from Rule
						standardMessage = StandardMessageUtil.getStandardMessageFromRule(message, vo, inboundCharset, prop);
					}
				}
				else {
					// 1. 전문 고유번호 추출 - RequestAction 실행
					String[] keys = null;

					if(vo.getActionName() != null ) {
						try {
							RequestAction action = ActionFactory.createAction(vo.getActionName());
							action.setAdapterInfo(vo.getAdapterGroupName(), vo.getAdapterName(), prop);
							keys = action.perform(message);
						}
						catch(Exception ex) {
							if(Keys.IF_SUBSTANDARD.equals(vo.getStdMsgTypeCode())) {
								if (logger.isWarn()) logger.warn("RequestProcessor] SUBSTANDARD Skip messageKey Error", ex);
							}
							else {
								throw ex;
							}
						}
					} else {
						if (logger.isInfo()) logger.info("RequestProcessor] The RequestAction is null in Adapter properties. - "+vo.getAdapterName());
					}

					if (keys != null) {
						if (logger.isDebug() ) {
							for(int i=0; i<keys.length; i++) {
								logger.debug("RequestProcessor] 전문고유번호 [" + i + "][" + keys[i] + "]");
							}
						}
						if (logger.isDebug()) logger.debug("RequestProcessor] Get STDMessage from Rule");
						for(int i=0; i<keys.length; i++) {
							vo.setSelectedKey(keys[i]);
							// read from Rule
							standardMessage = StandardMessageUtil.getStandardMessageFromRule(message, vo, inboundCharset, prop);
							if (standardMessage != null) {
								break;
							}
						}
					}

					if(Keys.IF_SUBSTANDARD.equals(vo.getStdMsgTypeCode())) {
						if (logger.isDebug()) logger.debug("RequestProcessor] Mapping StandardMessage from INPUT MESSAGE - SUBSTANDARD");
						try {
							if(logger.isDebug() && standardMessage !=null) {
								logger.debug("RequestProcessor] SUBSTANDARD ORG - " + standardMessage.toPrettyJson());
							}
							MessageMapper versionMapper = standardManager.getVersionMapper();
							subMessage = versionMapper.parseSub(vo.getAdptrMsgType(), message);
							subMessage.setBizDataCharset(inboundCharset);
							standardMessage = versionMapper.mapFromSub(subMessage, standardMessage);

							// 변경된 값이 있을 수 있으므로 재처리.
							standardManager.getMessageCoordinator().coordinateAfterParsing(standardMessage, vo, prop);
							if(logger.isDebug() && standardMessage !=null) {
								logger.debug("RequestProcessor] SUBSTANDARD MAPPED - " + standardMessage.toPrettyJson());
							}
						} catch(Exception e) {
							vo.setRspErrorCode("RECEAIIRP008");
							vo.setRspErrorMsg(ExceptionUtil.make(e, vo.getRspErrorCode(), new String[]{ e.getMessage()}));
							vo.setErrTypeCode(InboundErrorKeys.IN_UNKNOWN);
							if (logger.isError()) logger.error(vo.getRspErrorMsg(), e);
							logUnknownMessage(vo, orgMessage);
							throw new RequestProcessorException(vo.getRspErrorMsg());
						}
					}
				}
			} catch(ActionException e) {
				vo.setRspErrorCode("RECEAIIRP001");
				vo.setRspErrorMsg(ExceptionUtil.make(e, vo.getRspErrorCode(), new String[]{ vo.getActionName()}));
				vo.setErrTypeCode(InboundErrorKeys.IN_ERROR);
				if (logger.isError()) logger.error(vo.getRspErrorMsg(), e);
				logUnknownMessage(vo,orgMessage);
				throw e;
			} catch(Exception khe) {
				vo.setRspErrorCode("RECEAIIRP010");
				vo.setRspErrorMsg(ExceptionUtil.make(khe, vo.getRspErrorCode(), new String[]{vo.getSelectedKey()}));
				vo.setErrTypeCode(InboundErrorKeys.IN_UNKNOWN);
				if (logger.isError()) logger.error(vo.getRspErrorMsg(), khe);
				throw new RequestProcessorException(vo.getRspErrorMsg());
			}
		}

		if(standardMessage == null) {
			vo.setRspErrorCode("RECEAIIRP010");
			vo.setRspErrorMsg(ExceptionUtil.make(vo.getRspErrorCode(), vo.getSelectedKey()));
			vo.setErrTypeCode(InboundErrorKeys.IN_UNKNOWN);
			if (logger.isError()) logger.error(vo.getRspErrorMsg());
			logUnknownMessage(vo,orgMessage);
			throw new RequestProcessorException(vo.getRspErrorMsg());
		}

		// 3. EAIMessage Object 생성
		//String eaiSvcCd = mapper.getEaiSvcCode(standardMessage);   // commnet by jwhong
		String eaiSvcCd = prop.getProperty("API_SERVICE_CODE");
		logger.info("eaiSvcCd = "+eaiSvcCd);
		if (StringUtils.isEmpty(eaiSvcCd)) eaiSvcCd = mapper.getEaiSvcCode(standardMessage); // HTT 어댑터로 들어오는거래는 Prop에 서비스코드가 없음.
		String guid = mapper.getGuid(standardMessage);
		String guidSeq = mapper.getGuidSeq(standardMessage);
		String returnType = mapper.getSendRecvDivision(standardMessage); // S|R

		String receivedBizData = standardMessage.getBizData();

		vo.setEaiSvcCd(eaiSvcCd);
		if (StringUtils.isBlank(eaiSvcCd)) {
			vo.setRspErrorCode("RECEAIIRP009");
			vo.setRspErrorMsg(ExceptionUtil.make(vo.getRspErrorCode(), vo.getEaiSvcCd() ));
			vo.setErrTypeCode(InboundErrorKeys.IN_UNKNOWN);
			if (logger.isError()) logger.error(vo.getRspErrorMsg());
			logUnknownMessage(vo,orgMessage);
			throw new RequestProcessorException(vo.getRspErrorMsg());
		}else {
			// TODO : DJErp - 표준전문의 InterfaceID는 SEND_RECV_DIVISION, IN_EX_DIVISION 포함해야 할지에 따라 결정
			// mapper.setInterfaceId(standardMessage, eaiSvcCd);
			
			/**
			 * MCI Sync-Async 응답을 대표인터페이스로 구현하기 위해 추가
			 * 조건)MCI 거래 && 일반거래 && 응답 거래
			 */
			if (eaiServerManager.isMCI() && STDMessageKeys.SEND_RECV_CD_RECV.equals( mapper.getSendRecvDivision(standardMessage) )) {
				String bidInterface = vo.getBidInterface();
				if (StringUtils.isNotBlank(bidInterface)) {
					mapper.setInterfaceId(standardMessage, bidInterface);
					eaiSvcCd = mapper.getEaiSvcCode(standardMessage);
					vo.setEaiSvcCd(eaiSvcCd);
				}
			}
		}
		//validation 추가
		if(Keys.IF_STANDARD.equals(vo.getStdMsgTypeCode())) {//표준전문일 경우
			if (!standardManager.getMessageCoordinator().coordinateValidation(standardMessage, vo)){
				if (logger.isError()) logger.error(vo.getRspErrorMsg());
				logUnknownMessage(vo,orgMessage);
				return getInboundErrorResponse(prop, standardMessage, mapper, vo.getRspErrorCode(), vo.getRspErrorMsg(), inboundCharset);
			}
		}

		// 채널분류코드별로 GUID 중복체크
		if (eaiServerManager.isMCI() && STDMessageKeys.SEND_RECV_CD_SEND.equals(mapper.getSendRecvDivision(standardMessage))
				&& PropManager.getInstance().isContainProperties("DUPLICATION_GUID") ) {
			Properties duplicationProp = PropManager.getInstance().getProperties("DUPLICATION_GUID");
			if (duplicationProp != null && !duplicationProp.isEmpty()) {
				String channelTypeCd = mapper.getChannelDetailCd(standardMessage);// 채널세부분류코드 3자리
	            String duplicationKey = duplicationProp.getProperty("CHNL_CLCD", "");
	            if (StringUtils.isNotBlank(channelTypeCd) && StringUtils.isNotBlank(duplicationKey) ) {
	               Set<String> duplicationKeys = org.springframework.util.StringUtils.commaDelimitedListToSet(duplicationKey);
	               if (duplicationKeys.contains(channelTypeCd)){
						// 중복체크
						boolean isDup = false;
						try {
							DuplicationGuidLogUtil.insert(guid + guidSeq, channelTypeCd, vo, orgMessage);
						} catch (Exception e) {
							isDup = true;
						}
						if (isDup) {
							vo.setRspErrorCode("RECEAIIRP130");
							vo.setRspErrorMsg(ExceptionUtil.make(vo.getRspErrorCode(), new String[] { channelTypeCd, guid + guidSeq}));
							vo.setErrTypeCode(InboundErrorKeys.IN_UNKNOWN);
							if (logger.isError()) logger.error(vo.getRspErrorMsg());
							logUnknownMessage(vo, orgMessage);
							return getInboundErrorResponse(prop, standardMessage, mapper, vo.getRspErrorCode(), vo.getRspErrorMsg(), inboundCharset);
						}
					}
				}
			}
		}
		prop.setProperty(TransactionContextKeys.GUID, guid);

		// INBOUND MESSAGE 스레드 로컬에 저장
		String bizData = standardMessage.getBizData();
		ElinkTransactionContext.setRequestBizData(bizData);

		// INBOUND_HEADER 가 NULL인 경우는 HTTP가 아닌것으로 간주함.
		if(prop.get(HttpClientAdapterServiceKey.INBOUND_HEADER) != null) {
			if (!httpHeaderLogMode) {
	    		logger.info("httpHeader logging off - use.http.header.log");
	    	} else {
	    		HttpAdapterExtraLogUtil.insertHttpAdapterExtraLog(vo.getUuid(), 100,
						prop.getProperty(HttpClientAdapterServiceKey.ADAPTER_GROUP_NAME),
						prop.getProperty(HttpClientAdapterServiceKey.ADAPTER_NAME),
						(Properties) prop.get(HttpClientAdapterServiceKey.INBOUND_HEADER),
						prop.getProperty(HttpClientAdapterServiceKey.INBOUND_EXTURI)+"?"+prop.getProperty(HttpClientAdapterServiceKey.INBOUND_EXTPARAMS),
						prop.getProperty(HttpClientAdapterServiceKey.INBOUND_METHOD));
	    	}
		}
		
		EAIMessage eaiMsg = null;
		try {
			EAIMessageManager eaiMsgManager = EAIMessageManager.getInstance();
			eaiMsg = eaiMsgManager.getEAIMessage(vo.getEaiSvcCd());
		}
		catch(Exception e) {
			vo.setRspErrorCode("RECEAIIRP020");
			vo.setRspErrorMsg(ExceptionUtil.make(e, vo.getRspErrorCode(), new String[]{vo.getEaiSvcCd()}));
			vo.setErrTypeCode(InboundErrorKeys.IN_ERROR);
			if (logger.isError()) logger.error(vo.getRspErrorMsg());
			logUnknownMessage(vo,orgMessage);
			return getInboundErrorResponse(prop, standardMessage, mapper, vo.getRspErrorCode(), vo.getRspErrorMsg(), inboundCharset);
		}

		if(eaiMsg == null) {
			vo.setRspErrorCode("RECEAIIRP020");
			vo.setRspErrorMsg(ExceptionUtil.make(vo.getRspErrorCode(), vo.getEaiSvcCd() ));
			vo.setErrTypeCode(InboundErrorKeys.IN_ERROR);
			if (logger.isError()) logger.error(vo.getRspErrorMsg());
			logUnknownMessage(vo,orgMessage);
			return getInboundErrorResponse(prop, standardMessage, mapper, vo.getRspErrorCode(), vo.getRspErrorMsg(), inboundCharset);
		}
		
		eaiMsg.setCallProp(prop);
		
		// set standardMessage & mapper to EAIMessage
		eaiMsg.setStandardMessage(standardMessage);
		eaiMsg.setMapper(mapper);
		eaiMsg.setClientId(prop.getProperty(HttpAdapterServiceSupport.PROPERTIES_NAME_CLIENT_ID));
		eaiMsg.setClientIp(prop.getProperty(HttpAdapterServiceKey.INBOUND_CLIENT_IP));
		String psvItfTp = eaiMsg.getCurrentSvcMsg().getPsvItfTp();
		svrLogLevel = eaiMsg.getSvrLogLvl();
		if(svrLogLevel >= 1) {
			if (esbLogger.isInfo()) esbLogger.info("RequestProcessor]  started... - EAI Server Log Level is INFO");
		}
		
		afterFoundEaiMessage(eaiMsg, vo, prop);
		
		// 3-1. 수신메시지와 초기값을 EAIMessage에 설정한다.
		// 메시지 수신시각 설정
		eaiMsg.setMsgRcvTm(vo.getErrTm());
		String svcTsmtUsgTp = eaiMsg.getSvcTsmtUsgTp();
		prop.setProperty(HttpAdapterServiceKey.INBOUND_SYNC_ASYNC_TYPE, svcTsmtUsgTp);

		//-------------------------------------------------
		// 시스템운영환경구분코드 매핑
		//       차세대 표준      EAI
		// PROD     O        P
		// DR       R        P
		// STAGING  S        S
		// DEVELOP  D        D
		//-------------------------------------------------

		String sysOperEvirnDstcd = mapper.getOperationEnv(standardMessage);//운영환경 구분코드
		if( !(Keys.IF_STANDARD.equals(vo.getStdMsgTypeCode())) ) {
			if (EAIMessageKeys.ASYNC_SVC.equals(svcTsmtUsgTp)) {
				mapper.setSyncAsyncType(standardMessage, STDMessageKeys.SYNC_ASYNC_CD_ASYNC);
				if (STDMessageKeys.SEND_RECV_CD_RECV.equals(mapper.getSendRecvDivision(standardMessage))) {
					// 정상셋팅
					String procsRsltDvcd = mapper.getResponseType(standardMessage);
					if (StringUtils.isBlank(procsRsltDvcd)) {
						mapper.setResponseType(standardMessage, STDMessageKeys.RESPONSE_TYPE_CODE_N);
					}
				}
			} else if (EAIMessageKeys.SYNC_SVC.equals(svcTsmtUsgTp)) {
				mapper.setSyncAsyncType(standardMessage, STDMessageKeys.SYNC_ASYNC_CD_SYNC);
			}
		}


		// NON STANDARD guid 설정
		// 표준 Prod 기동 시스템의 UUID가 setting 안되어 있으면 Error
		// 표준 개발,테스트 기동시스템의 UUID가 Setting 안되어 있으면 EAI에서 Setting
		String intgraTelgmUniqno = mapper.getGuid(standardMessage);
		if( Keys.IF_STANDARD.equals(vo.getStdMsgTypeCode()) ) {
			if(StringUtils.isEmpty(intgraTelgmUniqno)) {
				// 표준 Prod 기동 시스템에서 UUID 셋팅 안한 경우 예외 처리
				vo.setRspErrorCode("RECEAIIRP011");
				vo.setRspErrorMsg(ExceptionUtil.make(vo.getRspErrorCode(), vo.getEaiSvcCd() ));
				if (logger.isError()) logger.error(vo.getRspErrorMsg());
				return getInboundErrorResponse(prop, standardMessage, mapper, vo.getRspErrorCode(), vo.getRspErrorMsg(), inboundCharset);
			}
		}
		else {
			if (logger.isDebug()) logger.debug("RequestProcessor] IF_NONSTANDARD MESSAGE 통합전문일련번호 - " 
					+ guid + guidSeq);
		}

		String guidLogPrefix = "RequestProcessor] GUID[" + guid + "][" + guidSeq + "] UUID["+vo.getUuid()+"] ";
		if(svrLogLevel >= 1) {
			if (esbLogger.isInfo()) esbLogger.info(guidLogPrefix + " START");
		}

		if(logger.isInfo()) logger.info(guidLogPrefix);

		// Inbound AdapterGroup Name 설정
		eaiMsg.setSngSysItfTp(vo.getAdapterGroupName());
		eaiMsg.setSvcOgNo(vo.getUuid());
		// Current EAIServerInstanceName setting
		eaiMsg.setEAISvrInstNm(serverName);
		// StringServiceClassification setting (ESBFW | TIMER | EXCEPTION)
		eaiMsg.setSngSvcCls(STARTING_SERVICE_KEY);
		eaiMsg.setStdMsgUsgCls(vo.getStdMsgTypeCode());

		//----------------------------------------------
		// LogSequence 설정
		//     요청거래의 경우 100
		//     응답거래의 경우 300으로 설정한다.
		//----------------------------------------------
		if(EAIMessageKeys.ASYNC_SVC.equals(svcTsmtUsgTp) ) {
			if( STDMessageKeys.SEND_RECV_CD_RECV.equals(returnType)) {
				eaiMsg.setLogPssSno(300);
			}
			else {
				eaiMsg.setLogPssSno(100);
			}
		}
		else {
			eaiMsg.setLogPssSno(100);
		}
		
		if(Keys.IF_SUBSTANDARD.equals(vo.getStdMsgTypeCode())) {
			// 응답시 복원을 위해 저장 
			eaiMsg.setSubMessage(subMessage);
			if(subMessage !=null && logger.isDebug()) {
				logger.debug( String.format("RequestProcessorSupport]  SUBSTANDARD-LOGGING SAVE UUID: %s, LOGSEQ: %s, HEADER: %s", 
						eaiMsg.getSvcOgNo(), eaiMsg.getLogPssSno(), subMessage.toFixedString(false)) );
			}
		}
		
		try {			
			eaiMsg.setRspErrCd(vo.getRspErrorCode());
		} catch(Exception e) {
			vo.setRspErrorCode("RECEAIIRP040");
			vo.setRspErrorMsg(ExceptionUtil.make(e, vo.getRspErrorCode(), new String[]{vo.getEaiSvcCd()}));
//			eaiMsg.setRspErrCd(vo.getRspErrorCode());
//			eaiMsg.setRspErrMsg(vo.getRspErrorMsg());
			eaiMsg.setRspErr(vo.getRspErrorCode(),vo.getRspErrorMsg());

			try {
				EAILogSender.send(eaiMsg, null);
				if (logger.isError()) logger.error(guidLogPrefix + " : Log 성공 - " + vo.getRspErrorMsg());
			} catch(Exception ex) {
				if (logger.isError()) logger.error(guidLogPrefix + " : Log 실패 - " + vo.getRspErrorMsg());
			}
			prop.setProperty(EAIMessageKeys.ELINK_RET_CODE, eaiMsg.getRspErrCd());
			return getInboundErrorResponse(prop, standardMessage, mapper, vo.getRspErrorCode(), vo.getRspErrorMsg(), inboundCharset);
		}

		if( EAIMessageKeys.TRANTYPE_STOP.equals(eaiMsg.getTranType()) ) {
			try {
				EAILogSender.send(eaiMsg, null);
				if (logger.isError()) logger.error(guidLogPrefix + " : TRANTYPE_STOP : "+vo.getEaiSvcCd()+", DB Log 성공");
			} catch(Exception ex) {
				if (logger.isError()) logger.error(guidLogPrefix + " : TRANTYPE_STOP: "+vo.getEaiSvcCd()+",  DB Log 실패");
			}
			return null;
		}

		// 5. 추적관리필드 추출
		// 동기호출이 아닌 경우에 추적관리필드를 추출하여 EAIMessage에 설정한다.
		String traceKey = "";
		try {
			/*
			 * 20220321 AS CASE 분리
			 * 요청일 경우에는 OutboundAdapter의 표준/비표준을 기준으로 추출
			 * 응답일 경우에는 InboundAdapter의 표준/비표준을 기준으로 추출
			 * 표준일 경우 GUID를 추출키로 사용
			 * 비표준일 경우 전문추적키를 사용
			 */
			if( EAIMessageKeys.SYNC_SVC.equals(svcTsmtUsgTp) && EAIMessageKeys.ASYNC_SVC.equals(psvItfTp) ) {
				String outboundAdapterGroupName = eaiMsg.getCurrentSvcMsg().getPsvSysItfTp();
				boolean isRecv = STDMessageKeys.SEND_RECV_CD_RECV.equals(returnType);
				String messageTypeCd ;
				if(isRecv) {
					messageTypeCd = vo.getStdMsgTypeCode();
				}else {
					AdapterGroupVO outboundAdapterGvo = AdapterManager.getInstance().getAdapterGroupVO(outboundAdapterGroupName);
					String outboundMessageType = outboundAdapterGvo.getAdptrMsgPtrnCd();
					messageTypeCd = outboundMessageType;
				}

				if(Keys.IF_STANDARD.equals(messageTypeCd)) {
					if (logger.isDebug()) logger.debug(guidLogPrefix + " : STANDARD Interface - STDMessage HEADER TraceKey [" + traceKey + "] 에서 추출");
					traceKey = guid;
				} else {
					traceKey = BizKeyExtractor.getBizKeyValue(eaiMsg.getEAISvcCd(), null, receivedBizData);
					if (logger.isDebug()) logger.debug(guidLogPrefix + " : NONSTANDARD Interface - Message IntgraTelgmUniqNo set to traceKey ["+traceKey+"]");
				}
			} else {
				// SA, AA 거래인경우에만 TraceKey를 추출한다
				if( EAIMessageKeys.ASYNC_SVC.equals(psvItfTp) ) {
					if(Keys.IF_STANDARD.equals(vo.getStdMsgTypeCode())) {
			            BizKeyManager manager = BizKeyManager.getInstance();
			            BizKeyVO bizKey = manager.getBizKey(vo.getEaiSvcCd()+"TR");

			            if(bizKey == null) {
							traceKey = guid;							
							if (logger.isDebug()) logger.debug(guidLogPrefix + " : 표준 - STDMessage HEADER에서 추출 TraceKey [" + traceKey + "]");
						} else {
							traceKey = BizKeyExtractor.getBizKeyValue(eaiMsg.getEAISvcCd(), null, receivedBizData);
							if (logger.isDebug()) logger.debug(guidLogPrefix + " : 표준 - Rule을 통해 추출 TraceKey [" + traceKey + "]");
						}
						// messageID 가 있으나 TR 룰이 등록되지 않은 경우 guid 를 추출한다.
						if(StringUtils.isEmpty(traceKey)) {
							traceKey = guid;							
						}
					} else {
						if(FAIL_MSG_RECEIVER.equals(vo.getAdapterGroupName())) {
							if (logger.isDebug()) {
								logger.debug(guidLogPrefix + " : IF_NONSTANDARD Message SKIP TRACEKEY Extract - "+vo.getAdapterGroupName());
							}
						}
						else {
							traceKey = BizKeyExtractor.getBizKeyValue(eaiMsg.getEAISvcCd(), null, receivedBizData);
						}						
						if (logger.isDebug()) logger.debug(guidLogPrefix + " : IF_NONSTANDARD Message IntgraTelgmUniqno set to traceKey ["+traceKey+"]");
					}

					if (!BizKeyExtractor.isHex(eaiMsg.getEAISvcCd()) && traceKey != null) {
						traceKey = traceKey.trim();
	            	}
					// AA의 응답메시지가 timer관리 대상인 경우 Timer처리 Table의 상태를 변경한다.
					if(EAIMessageKeys.ASYNC_SVC.equals(svcTsmtUsgTp)) {
						// AA의 응답메시지일 경우 정상처리Flag를 설정한다.
						eaiMsg.setRspErrCd(EAIMessageKeys.EAI_SUCCESS_CODE);
					}
					// SA 거래의 타이아웃시간이 0일 경우 기본값으로 59초(59*1000 ms)로 설정하도록 한다.
					if(EAIMessageKeys.SYNC_SVC.equals(svcTsmtUsgTp) 
						&& (eaiMsg.getCurrentSvcMsg().getTmoVl() <= 0) ) {
						if (logger.isWarn()) logger.warn(guidLogPrefix + " : Current SA TimeOut["+eaiMsg.getCurrentSvcMsg().getTmoVl()+"] - " + vo.getUuid());
						eaiMsg.getCurrentSvcMsg().setTmoVl( 59 ); // seconds
						if (logger.isWarn()) logger.warn(guidLogPrefix + " : RESET Default SA TimeOut["+eaiMsg.getCurrentSvcMsg().getTmoVl()+"] - " + vo.getUuid());
					}
				}
				else { 
					// SS, AS의 경우 GUID를 TraceKey로 사용한다.
					traceKey = guid;
					if (!BizKeyExtractor.isHex(eaiMsg.getEAISvcCd()) && traceKey != null) {
						traceKey = traceKey.trim();
					}
				}
			}
			if(StringUtils.isBlank(traceKey)){
				traceKey = guid;
			}
			if (logger.isInfo()) logger.info(guidLogPrefix + " : TraceKey [" + traceKey + "]");
			eaiMsg.getCurrentSvcMsg().setKeyMgtMsgVl(traceKey);
		} catch(Exception e) {
			vo.setRspErrorCode("RECEAIIRP050");
			vo.setRspErrorMsg(ExceptionUtil.make(e, vo.getRspErrorCode(), new String[]{vo.getEaiSvcCd()}));
//			eaiMsg.setRspErrCd(vo.getRspErrorCode());
//			eaiMsg.setRspErrMsg(vo.getRspErrorMsg());
			eaiMsg.setRspErr(vo.getRspErrorCode(),vo.getRspErrorMsg());

			try {
				EAILogSender.send(eaiMsg, null);
				if (logger.isError()) logger.error(guidLogPrefix + " : DB Log 성공 - " + vo.getRspErrorMsg());
			} catch(Exception ex) {
				if (logger.isError()) logger.error(guidLogPrefix + " : DB Log 실패 - " + vo.getRspErrorMsg());
			}
			try {
				retEaiMsg = ExceptionHandler.handle(eaiMsg);
				Object retMsgObject = receivedBizData;
				prop.setProperty(EAIMessageKeys.ELINK_RET_CODE, retEaiMsg.getRspErrCd());
				return getReturnObject(retEaiMsg, retMsgObject, retMsgObject, vo.getStdMsgTypeCode()
						, vo.getAdptrMsgType(), vo.getUuid(), inboundCharset);
			} catch(Exception ehe) {
				if (logger.isError()) logger.error(guidLogPrefix + " : ExceptionHandler Call Error : " + e.getMessage());
				throw new RequestProcessorException(vo.getRspErrorMsg());
			}
		}

		//----------------------------------------------
		// 비동기-비동기 거래 && 기동이 표준이 아닌 경우
		// 원거래복원 여부가 '1' 일 경우에
		// STDMessage를 복원한다.
		//----------------------------------------------
		String recoverYn = mapper.getRecoverYn(standardMessage);
		if(logger.isDebug()) {
			logger.debug(guidLogPrefix + " : STDMessage 복원 Flag ["+recoverYn+"]");
		}

		if(EAIMessageKeys.ASYNC_SVC.equals(svcTsmtUsgTp) &&
				!Keys.IF_STANDARD.equals(vo.getStdMsgTypeCode()) &&
				STDMessageKeys.RECOVER_YN_YES.equals(recoverYn) &&
				STDMessageKeys.SEND_RECV_CD_RECV.equals(returnType)
		) {
			try {
				if(logger.isDebug()) {
					logger.debug(guidLogPrefix + " : STDMessage 복원 실행 KEY["+traceKey+"]");
				}
				if(StringUtils.isEmpty(traceKey)) {
					throw new Exception(guidLogPrefix + " : Invalid traceKey Error["+traceKey+"]");
				}
				String extBizCode = eaiMsg.getExtBizCd();

				String restoredString = STDMessageMapper.getStoredMessageFixedHeader(traceKey, extBizCode);
				if(restoredString != null) {
					//복원시 outbound Adapter 메시지 타입 기준으로 reader를 셋팅한다.
					String outboundAdapterGroupName = eaiMsg.getCurrentSvcMsg().getPsvSysItfTp();
					AdapterManager adapterManager = AdapterManager.getInstance();
					AdapterGroupVO outAdptGrpVO = adapterManager.getAdapterGroupVO(outboundAdapterGroupName);
					
					StandardMessage restoredStandardMessage = standardManager.getStandardMessage();
					StandardReader restoredReader = standardManager.getReader(outAdptGrpVO.getMessageType());
					restoredReader.parse(restoredStandardMessage, restoredString);
					standardManager.getMessageCoordinator().coordinateAfterRestored(restoredStandardMessage, standardMessage);
					restoredStandardMessage.setBizData(standardMessage.getBizDataBytes(), standardMessage.getBizDataCharset());
					
					eaiMsg.setStandardMessage(restoredStandardMessage);
				}
				else {
					throw new Exception("요청 내부표준전문의 원거래 복원필드 항목이 ‘1’로 세팅되지 않았거나, 응답전문의 복원 KEY["+traceKey+"] 값이 일치하지 않음");
				}
			}
			catch(Exception e) {
				if(logger.isError()) {
					logger.error(e.toString(), e);
				}
				vo.setRspErrorCode("RECEAIIRP055");
				vo.setRspErrorMsg(ExceptionUtil.make(e, vo.getRspErrorCode(), new String[]{vo.getEaiSvcCd()}));
//				eaiMsg.setRspErrCd(vo.getRspErrorCode());
//				eaiMsg.setRspErrMsg(vo.getRspErrorMsg());
				eaiMsg.setRspErr(vo.getRspErrorCode(),vo.getRspErrorMsg());

				try {
					EAILogSender.send(eaiMsg, null);
					if (logger.isError()) logger.error(guidLogPrefix + " : DB Log 성공 - " + vo.getRspErrorMsg());
				} catch(Exception ex) {
					if (logger.isError()) logger.error(guidLogPrefix + " : DB Log 실패 - " + vo.getRspErrorMsg());
				}
				try {
					retEaiMsg = ExceptionHandler.handle(eaiMsg);
					Object retMsgObject = receivedBizData;
					prop.setProperty(EAIMessageKeys.ELINK_RET_CODE, retEaiMsg.getRspErrCd());
					return getReturnObject(retEaiMsg, retMsgObject, retMsgObject, vo.getStdMsgTypeCode()
							, vo.getAdptrMsgType(), vo.getUuid(), inboundCharset);

				} catch(Exception ehe) {
					if (logger.isError()) logger.error(guidLogPrefix + " : ExceptionHandler Call Error : " + e.getMessage());
					throw new RequestProcessorException(vo.getRspErrorMsg());
				}
			}
		}

		if(logger.isInfo()) logger.info(guidLogPrefix + " :  TRAN_TYPE - " + eaiMsg.getTranType());
		// 6. ALT-KEY 테이블의 검색키필드를 추출하여 로그에 넘겨준다
		//    Logger의 Property로 설정한다
		String[] altKeys = new String[4];
		try {
			// 메시지 ID가 정의되지 않은 경우 오류메시지를 리턴한다.
			if(FAIL_MSG_RECEIVER.equals(vo.getAdapterGroupName())) {
				if (logger.isDebug()) logger.debug(guidLogPrefix + " : SKIP ALTKEY Extract - " + vo.getAdapterGroupName());
			}
			else {
				altKeys = BizKeyExtractor.getAltKeyValues(eaiMsg.getEAISvcCd(), null, receivedBizData);
			}
		} catch(Exception e) {
			vo.setRspErrorCode("RECEAIIRP060");
			vo.setRspErrorMsg(ExceptionUtil.make(e, vo.getRspErrorCode(), new String[]{vo.getEaiSvcCd()}));

//			eaiMsg.setRspErrCd(vo.getRspErrorCode());
//			eaiMsg.setRspErrMsg(vo.getRspErrorMsg());
			eaiMsg.setRspErr(vo.getRspErrorCode(),vo.getRspErrorMsg());

			try {
				EAILogSender.send(eaiMsg, null);
				if (logger.isError()) logger.error(guidLogPrefix + " : DB Log 성공 - " + vo.getRspErrorMsg(), e);
			} catch(Exception ex) {
				if (logger.isError()) logger.error(guidLogPrefix + " : DB Log 실패 - " + vo.getRspErrorMsg(), e);
			}
			try {
				retEaiMsg = ExceptionHandler.handle(eaiMsg);
				Object retMsgObject = retEaiMsg.getStandardMessage().getBizData();
				prop.setProperty(EAIMessageKeys.ELINK_RET_CODE, retEaiMsg.getRspErrCd());
				return getReturnObject(retEaiMsg, receivedBizData, retMsgObject, vo.getStdMsgTypeCode(), vo.getAdptrMsgType(), vo.getUuid(), inboundCharset);
			} catch(Exception ehe) {
				if (logger.isError()) logger.error(guidLogPrefix + " : ExceptionHandler Call Error : " + e.getMessage());
				throw new RequestProcessorException(vo.getRspErrorMsg());
			}
		}

		if (logger.isDebug()) {
			logger.debug(guidLogPrefix + " : LOG_ALT_KEY1 [" + altKeys[0] + "]");
			logger.debug(guidLogPrefix + " : LOG_ALT_KEY2 [" + altKeys[1] + "]");
			logger.debug(guidLogPrefix + " : LOG_ALT_KEY3 [" + altKeys[2] + "]");
			logger.debug(guidLogPrefix + " : LOG_ALT_KEY4 [" + altKeys[3] + "]");
		}
		Properties altKeyProp = new Properties();

		if(altKeys != null && altKeys.length > 0) {
			if(altKeys[0] == null) altKeys[0] = "";
			if(altKeys[1] == null) altKeys[1] = "";
			if(altKeys[2] == null) altKeys[2] = "";
			if(altKeys[3] == null) altKeys[3] = "";
			altKeyProp.setProperty(com.eactive.eai.common.logger.Keys.LOG_ALT_KEY1, altKeys[0]);
			altKeyProp.setProperty(com.eactive.eai.common.logger.Keys.LOG_ALT_KEY2, altKeys[1]);
			altKeyProp.setProperty(com.eactive.eai.common.logger.Keys.LOG_ALT_KEY3, altKeys[2]);
			altKeyProp.setProperty(com.eactive.eai.common.logger.Keys.LOG_ALT_KEY4, altKeys[3]);
		}
		//----------------------------------------------
		// 6.1 업체코드 추출하여 로그에 넘겨준다  ( 요건추가 2019-03-26 )
		//    Logger의 Property로 설정한다
		//    거래 방향이 내부이면 SourceAdapter 에서 추출
		//    거래 방향이 외부이면 TargetAdapter 에서 추출
		//    내부는 1 , 외부는 2,3,4 의 값이 나올수 있음
		//----------------------------------------------
		String companyCode = "";
		String adptrInstiCode = "";
		try{
			String companyCodeData = null;
			boolean isEbcdic = false;
			if (eaiServerManager.isFEP()) {
				AdapterManager adapterManager = AdapterManager.getInstance();
				AdapterGroupVO adptGrpVO = adapterManager.getAdapterGroupVO(vo.getAdapterGroupName());
				//내부방향
				if(STDMessageKeys.DIRECTION_IN.equals(MessageUtil.getInExDivision(mapper.getInExDivision(standardMessage), eaiMsg.getInternalExternalDvcd()))){ //내부방향
					companyCodeData = adptGrpVO.getCompanyCodeData();
					adptrInstiCode = adptGrpVO.getAdptrInstiCode();
				}else{//외부방향
					AdapterGroupVO targetAdapterGroupVO = adapterManager.getAdapterGroupVO(eaiMsg.getCurrentSvcMsg().getPsvSysItfTp());
					//Sync-Async 응답에서 Adapter가 없는 경우
					if (targetAdapterGroupVO != null){
						companyCodeData = targetAdapterGroupVO.getCompanyCodeData();
						adptrInstiCode = targetAdapterGroupVO.getAdptrInstiCode();
					}
				}
			}

			if (companyCodeData != null){
				if(MessageType.EBC.equals(vo.getAdptrMsgType())) {
					isEbcdic = true;
				}else{
					isEbcdic = false;
				}
				companyCode = BizKeyExtractor.getBizKeyCode(companyCodeData, receivedBizData, isEbcdic);
			}
		}catch(Exception e){
			vo.setRspErrorCode("RECEAIIRP060"); //신규 에러 코드 추출해야됨
			vo.setRspErrorMsg(ExceptionUtil.make(e, vo.getRspErrorCode() ));
//			eaiMsg.setRspErrCd(vo.getRspErrorCode());
//			eaiMsg.setRspErrMsg(vo.getRspErrorMsg());
			eaiMsg.setRspErr(vo.getRspErrorCode(),vo.getRspErrorMsg());
		}
		if(companyCode != null && !"".equals(companyCode)) {
			eaiMsg.setCompanyCode(companyCode);
		}else{
			eaiMsg.setCompanyCode("");
		}
		if(adptrInstiCode != null && !"".equals(adptrInstiCode)) {
			eaiMsg.setAdptrInstiCode(adptrInstiCode);
		}else{
			eaiMsg.setAdptrInstiCode("");
		}
		// 6.2 업무데이터 길이를  추출하여 로그에 넘겨준다
		//    Logger의 Property로 설정한다
		String data = receivedBizData;
		if( data != null ) {
			eaiMsg.setSplizLength(data.getBytes().length);
		}else{
			eaiMsg.setSplizLength(0);
		}

		// 7. Service Flag, ACKON 처리
		//----------------------------------------------------------
		// 거래통제로직
		// 2009.08.19
		// 거래통제 정보를 조회하여 동기거래인 경우 응답메시지를 조립하여 전달하고
		// 비동기 거래인 경우 에러 응답처리 한다.
		// 동기 표준인 경우         : 거래통제에러코드 설정
		// 동기 표준이 아닌 경우  : EAI에서 정의한 동일한 에러응답메시지 전달
		//----------------------------------------------------------
		RestrictInfoManager rManager = RestrictInfoManager.getInstance();
		RestrictInfoVO rVo = rManager.getRestrictInfoVO(eaiMsg.getEAISvcCd());

		// 기관코드로 한번더 Check 해야 함 : 2009.10.30

		String instCode = mapper.getInstCode(standardMessage);
		String ctrlNameDstcd = "1"; // EAI 서비스코드에 의한 통제

		boolean isCheckRestrictTime = false;
		
		if(rVo == null) {
			// 2011.01.04 기관코드 비정상 처리 수정
			if(instCode != null && instCode.trim().length() > 0) {
				rVo = rManager.getRestrictInfoVO(instCode);
			}
			if(rVo != null) {
				isCheckRestrictTime = checkRestrictTime(rVo);
				if(isCheckRestrictTime) {
					if (logger.isInfo()) {
						logger.info(guidLogPrefix + " : 거래통제  기관코드["+instCode+"] EAI서비스코드 ["+eaiMsg.getEAISvcCd()+"] GUID["+ guid +"]");
					}
					ctrlNameDstcd = "2";
				}
			}
		} else {
			isCheckRestrictTime = checkRestrictTime(rVo);
			if (logger.isInfo() && isCheckRestrictTime) {
				logger.info(guidLogPrefix + " : 거래통제  EAI 서비스코드 ["+eaiMsg.getEAISvcCd()+"] GUID["+ guid +"]");
			}
		}

		// APP 별 거래 제한 처리
		boolean isBlockedApp = false;
		String clientId = prop.getProperty(HttpAdapterServiceSupport.PROPERTIES_NAME_CLIENT_ID);
		if(StringUtils.isNotBlank(clientId)) {
			ClientVO clientVO = OAuth2Manager.getInstance().getClientVO(clientId);
			if("0".equals(clientVO.getAppStatus())){
				isBlockedApp = true;
			}
		}

		if(isBlockedApp || (rVo != null && isCheckRestrictTime)) {
			// 거래통제 대상 거래일 경우 '1'로 설정한다.
			eaiMsg.setSvcTmEn(EAIMessageKeys.YES_FLAG);
			vo.setRspErrorCode(EAIMessageKeys.EAI_BLOCKED_CODE);
			vo.setRspErrorMsg(buildRestrictErrorMsg(eaiMsg, prop));
			eaiMsg.setRspErr(vo.getRspErrorCode(),vo.getRspErrorMsg());

			// 거래통제 로그에 로그생성
			try {
				RestrictInfoManager.getInstance().addResrtictLog(eaiMsg.getBwkCls(), DatetimeUtil.getCurrentTime(eaiMsg.getMsgRcvTm()),
						eaiMsg.getSvcOgNo(), eaiServerManager.getLocalServerName(),
						eaiMsg.getEAISvcCd(), instCode, ctrlNameDstcd);
			} catch(Exception ex) {
				if (logger.isError()) {
					StringBuilder sb = new StringBuilder();
					sb.append("EAIBzwkDstcd      [" +eaiMsg.getBwkCls()                                  +"]\n");
					sb.append("MsgDpstYMS        [" +DatetimeUtil.getCurrentTime(eaiMsg.getMsgRcvTm())   +"]\n");
					sb.append("EAISvcSerno       [" +eaiMsg.getSvcOgNo()                                 +"]\n");
					sb.append("EAISevrInstncName [" +eaiServerManager.getLocalServerName()               +"]\n");
					sb.append("EAISvcName        [" +eaiMsg.getEAISvcCd()                                +"]\n");
					sb.append("EAIOsidInstiCd    [" +instCode                                            +"]\n");
					sb.append("EAICtrlNameDstcd  [" +ctrlNameDstcd                                       +"]\n");
					logger.error(guidLogPrefix + " 거래통제건수 Log 실패 - "+ ex.toString() + "\n" + sb.toString());
				}
			}

			try {
				EAILogSender.send(eaiMsg, altKeyProp);
				if (logger.isError()) logger.error(guidLogPrefix + " : 거래통제 Log 성공 - " + vo.getRspErrorMsg());
			} catch(Exception ex) {
				if (logger.isError()) logger.error(guidLogPrefix + " : 거래통제 Log 실패 - " + vo.getRspErrorMsg());
			}

			if(EAIMessageKeys.SYNC_SVC.equals(svcTsmtUsgTp))  {
				// 거래통제 응답 로그
				eaiMsg.setLogPssSno(400);
				mapper.setSendRecvDivision(standardMessage, STDMessageKeys.SEND_RECV_CD_RECV);
				
				// Add pathSeq
		        if (logger.isInfo()) logger.info( guidLogPrefix + "RES Current GUID ["+ guid + "]["+ guidSeq +"]");
		        guidSeq = mapper.nextGuidSeq(standardMessage);
		        if (logger.isInfo()) logger.info( guidLogPrefix + "RES ADD PATHSEQ GUID ["+ guid + "]["+ guidSeq +"]");
		        
		        //거래통제에 따른 응답
				Object restrictResponse = getRestrictedResponse(eaiMsg, receivedBizData
						, vo.getStdMsgTypeCode(), vo.getAdptrMsgType(), vo.getUuid(), inboundCharset);
				if (Keys.IF_STANDARD.equals(vo.getStdMsgTypeCode())) {
					eaiMsg.getStandardMessage().setBizData(receivedBizData, inboundCharset);
				} else {
					eaiMsg.getStandardMessage().setBizData(restrictResponse, inboundCharset);
				}
				try {
					EAILogSender.send(eaiMsg, null);
					if (logger.isError()) logger.error(guidLogPrefix + " : 거래통제 Log 성공 - " + vo.getRspErrorMsg());
				} catch(Exception ex) {
					if (logger.isError()) logger.error(guidLogPrefix + " : 거래통제 Log 실패 - " + vo.getRspErrorMsg());
				}

				AdapterGroupVO adptGrpVO = AdapterManager.getInstance().getAdapterGroupVO(vo.getAdapterGroupName());
				if (!Keys.IF_STANDARD.equals(vo.getStdMsgTypeCode()) && StringUtils.equalsAny(adptGrpVO.getType(),
						Keys.TYPE_REST, Keys.TYPE_HTTP, Keys.TYPE_HTTP_CUSTOM)) {
					throw new HttpStatusException(eaiMsg.getRspErrMsg(), eaiMsg.getRspErrCd(),
							HttpStatus.SERVICE_UNAVAILABLE.value());
				} else {
					return restrictResponse;
				}
			}
			else {
				// ExceptionHandler에 의해 에러 응답송신
				try  {
					ExceptionHandler.handle(eaiMsg);
				}
				catch(Exception ee) {
					if (logger.isError()) logger.error(guidLogPrefix + " : 거래통제 비동기 에러응답송신 실패  - " + ee.toString());
				}
				return null;
			}
		}
		
		// 7.1 유량제어 처리
		// 유량제어 정보를 조회 하여 비동기 거래인 경우만 처리한다.
		if (InflowControlUtil.isTargetOfInflowControl(eaiServerManager, eaiMsg)) {
			Bucket bucket = InflowControlUtil.getBucket();

			String blockedType = "";
			String customBlockedType = checkCustomInflow(bucket, eaiMsg, prop);
			if (customBlockedType != null) {
				blockedType += "C";
			}
			// Adapter 유량제어 체크
			String adapterBlockedType = checkAdapterInflow(bucket, vo.getAdapterGroupName());
			if (adapterBlockedType != null) {
				blockedType += "A";
			}
			// 그룹 우선, 없으면 인터페이스 유량제어 체크
			String groupId = bucket.getGroupIdByInterface(vo.getEaiSvcCd());
			String groupBlockedType = null;
			String interfaceBlockedType = null;
			if (groupId != null) {
				// 그룹에 속한 인터페이스 → 그룹 유량제어 체크
				groupBlockedType = bucket.isGroupPass(groupId);
				if (groupBlockedType != null) {
					blockedType += "G";
				}
			} else {
				// 그룹에 속하지 않은 인터페이스 → 인터페이스 유량제어 체크
				interfaceBlockedType = checkInterfaceInflow(bucket, vo.getEaiSvcCd());
				if (interfaceBlockedType != null) {
					blockedType += "I";
				}
			}

			String inflowErrorMsg;
			if (blockedType.length() > 0) {
				InflowTargetVO inflowTargetVO = null;
				InflowGroupVO inflowGroupVO = null;
				if (blockedType.startsWith("C")) {
					inflowTargetVO = resolveCustomInflowThreshold(bucket, eaiMsg, prop);
				} else if (blockedType.startsWith("A")) {
					inflowTargetVO = bucket.getAdapterInflowThreashold(vo.getAdapterGroupName());
				} else if (blockedType.contains("G")) {
					inflowGroupVO = bucket.getGroupInflowThreshold(groupId);
				} else {
					inflowTargetVO = bucket.getInterfaceInflowThreashold(vo.getEaiSvcCd());
				}
				// 에러처리
				vo.setRspErrorCode(EAIMessageKeys.EAI_INFLOW_BLOCKED_CODE);
				if (inflowGroupVO != null) {
					if ("PER_SECOND".equals(groupBlockedType)) {
						inflowErrorMsg = String.format("API 호출 한도 초과 [%s: %dreq/sec]",
								inflowGroupVO.getGroupName(),
								inflowGroupVO.getThresholdPerSecond());
					} else {
						inflowErrorMsg = String.format("API 호출 한도 초과 [%s: %dreq/%s]",
								inflowGroupVO.getGroupName(),
								inflowGroupVO.getThreshold(),
								inflowGroupVO.getThresholdTimeUnit());
					}
				} else if (inflowTargetVO != null) {
					String targetBlockedType;
					if (blockedType.startsWith("C"))      targetBlockedType = customBlockedType;
					else if (blockedType.startsWith("A")) targetBlockedType = adapterBlockedType;
					else                                  targetBlockedType = interfaceBlockedType;
					inflowErrorMsg = buildInflowTargetErrorMsg(inflowTargetVO, targetBlockedType);
				} else {
					inflowErrorMsg = "API 호출 한도 초과";
				}
				vo.setRspErrorMsg(inflowErrorMsg);

//				eaiMsg.setRspErrCd(vo.getRspErrorCode());
//				eaiMsg.setRspErrMsg(vo.getRspErrorMsg());
				eaiMsg.setRspErr(vo.getRspErrorCode(),vo.getRspErrorMsg());

				try {
					Properties inFlowProp = PropManager.getInstance().getProperties("INFLOW");
					String controlInflowHistory = inFlowProp.getProperty("inflow.control.history.yn", "N");
					if (StringUtils.equalsIgnoreCase(controlInflowHistory, "Y") && (inflowTargetVO != null || inflowGroupVO != null)) {
						DAOFactory factory = DAOFactory.newInstance();
//						InflowControlDAO restrictDao = (InflowControlDAO) factory.create(InflowControlDAO.class);
//						InflowControlDAO를 서비스로 만들어서 factory.create로 하면 안됨.
						InflowControlDAO restrictDao = (InflowControlDAO) ApplicationContextProvider.getContext().getBean(InflowControlDAO.class);
						// 그룹 유량제어 정보가 있으면 그룹 정보로, 없으면 기존 inflowTargetVO 정보로 로그
						long thresholdPerSecond = (inflowGroupVO != null) ? inflowGroupVO.getThresholdPerSecond() : inflowTargetVO.getThresholdPerSecond();
						long threshold = (inflowGroupVO != null) ? inflowGroupVO.getThreshold() : inflowTargetVO.getThreshold();
						String thresholdTimeUnit = (inflowGroupVO != null) ? inflowGroupVO.getThresholdTimeUnit() : inflowTargetVO.getThresholdTimeUnit();
						String logGroupName = (inflowGroupVO != null) ? inflowGroupVO.getGroupId() : vo.getAdapterGroupName();

						restrictDao.addInflowServicetLog(eaiMsg.getBwkCls(),
								DatetimeUtil.getCurrentTime(eaiMsg.getMsgRcvTm()), eaiMsg.getSvcOgNo(),
								eaiServerManager.getLocalServerName(), eaiMsg.getEAISvcCd(), logGroupName,
								thresholdPerSecond, threshold, thresholdTimeUnit);
					}
				} catch (Exception ex) {
					if (logger.isError()) {
						StringBuilder sb = new StringBuilder();
						sb.append("EAIBzwkDstcd      [" + eaiMsg.getBwkCls() + "]\n");
						sb.append("MsgDpstYMS        [" + DatetimeUtil.getCurrentTime(eaiMsg.getMsgRcvTm()) + "]\n");
						sb.append("EAISvcSerno       [" + eaiMsg.getSvcOgNo() + "]\n");
						sb.append("EAISevrInstncName [" + eaiServerManager.getLocalServerName() + "]\n");
						sb.append("EAISvcName        [" + eaiMsg.getEAISvcCd() + "]\n");
						sb.append("ADPTRBZWKGROUPNAME[" + vo.getAdapterGroupName() + "]\n");
						sb.append("InflowInfo        [" + inflowErrorMsg + "]\n");
						if (groupId != null) {
							sb.append("InflowGroupId     [" + groupId + "]\n");
						}

						logger.error(guidLogPrefix + " 유량제어 건수 Log 실패 - " + ex.toString() + "\n" + sb.toString());
					}
				}
		
				try {
					EAILogSender.send(eaiMsg, altKeyProp);
					if (logger.isError())
						logger.error(guidLogPrefix + " : 유량제어 Log 성공 - " + vo.getRspErrorMsg());
				} catch (Exception ex) {
					if (logger.isError())
						logger.error(guidLogPrefix + " : 유량제어 Log 실패 - " + vo.getRspErrorMsg());
				}

				if (EAIMessageKeys.SYNC_SVC.equals(svcTsmtUsgTp)) {
					// 유량제어 응답 로그
					eaiMsg.setLogPssSno(400);
					mapper.setSendRecvDivision(standardMessage, STDMessageKeys.SEND_RECV_CD_RECV);

					// Add pathSeq
					if (logger.isInfo())
						logger.info(guidLogPrefix + "RES Current GUID [" + guid + "][" + guidSeq + "]");
					guidSeq = mapper.nextGuidSeq(standardMessage);
					if (logger.isInfo())
						logger.info(guidLogPrefix + "RES ADD PATHSEQ GUID [" + guid + "][" + guidSeq + "]");

					Object restrictResponse = getRestrictedResponse(eaiMsg, receivedBizData, vo.getStdMsgTypeCode(),
							vo.getAdptrMsgType(), vo.getUuid(), inboundCharset);
					if (Keys.IF_STANDARD.equals(vo.getStdMsgTypeCode())) {
						eaiMsg.getStandardMessage().setBizData(receivedBizData, inboundCharset);
					} else {
						eaiMsg.getStandardMessage().setBizData(restrictResponse, inboundCharset);
					}
					try {
						EAILogSender.send(eaiMsg, null);
						if (logger.isError())
							logger.error(guidLogPrefix + " : 유량제어 Log 성공 - " + vo.getRspErrorMsg());
					} catch (Exception ex) {
						if (logger.isError())
							logger.error(guidLogPrefix + " : 유량제어 Log 실패 - " + vo.getRspErrorMsg());
					}

					AdapterGroupVO adptGrpVO = AdapterManager.getInstance().getAdapterGroupVO(vo.getAdapterGroupName());
					if (!Keys.IF_STANDARD.equals(vo.getStdMsgTypeCode()) && StringUtils.equalsAny(adptGrpVO.getType(),
							Keys.TYPE_REST, Keys.TYPE_HTTP, Keys.TYPE_HTTP_CUSTOM)) {
						throw new HttpStatusException(eaiMsg.getRspErrMsg(), eaiMsg.getRspErrCd(),
								HttpStatus.TOO_MANY_REQUESTS.value());
					} else {
						return restrictResponse;
					}
				} else {
					// ExceptionHandler에 의해 에러 응답송신
					try {
						ExceptionHandler.handle(eaiMsg);
					} catch (Exception ee) {
						if (logger.isError())
							logger.error(guidLogPrefix + " : 유량제어 비동기 에러응답송신 실패  - " + ee.toString());
					}
					throw new HttpStatusException(eaiMsg.getRspErrMsg(), eaiMsg.getRspErrCd(),
							HttpStatus.TOO_MANY_REQUESTS.value());
				}
			}
		}

		// 장애전문 전송 - 모든 변환을 안함으로 설정
		if(FAIL_MSG_RECEIVER.equals(vo.getAdapterGroupName())) {
			if (logger.isDebug()) {
				logger.debug(guidLogPrefix + " : 장애전문전송 EAI서비스코드 ["+eaiMsg.getEAISvcCd()+"] GUID["+ guid +"]");
			}
			eaiMsg.getCurrentSvcMsg().setCnvEn("0");
			eaiMsg.getCurrentSvcMsg().setBsRspCnvEn("0");
			eaiMsg.getCurrentSvcMsg().setErrRspCnvEn("0");

			// 전문의 앞 30자리를 짤라서 업무데이터로 인식
			// 전문은 항상 ASCII 전문이어야 한다.
			String failData = null;
			if(receivedBizData != null && receivedBizData.length() > 30) {
				failData = receivedBizData.substring( 0,30 );
			}
			else {
				failData = receivedBizData;
			}
			standardMessage.setBizData(failData, inboundCharset);
		}

		// 8. 수신 거래로그 Logging
		if (logger.isInfo()) logger.info(guidLogPrefix + " : LOG(" + eaiMsg.getLogPssSno() + ") EAIMessage : "+eaiMsg.getEAISvcCd());
		try {
			EAILogSender.send(eaiMsg, altKeyProp);
		} catch(Exception e) {
			vo.setRspErrorCode("RECEAIIRP080");
			vo.setRspErrorMsg(ExceptionUtil.make(e, vo.getRspErrorCode(), new String[]{vo.getEaiSvcCd()}));
			if (logger.isError()) logger.error(vo.getRspErrorMsg());
		}

		// 9. FlowRouter 호출 : SS, SA - Method Call,  SA의 응답거래 - TOPIC, AA,AS - QUEUE
		try {
			if (logger.isInfo()) {
				logger.info(guidLogPrefix + eaiMsg.getEAISvcCd()
					+ " : 기동 ["+svcTsmtUsgTp +"],수동 ["+psvItfTp +"], 전문요청구분코드 [" + returnType + "]");
			}
			if( EAIMessageKeys.SYNC_SVC.equals(svcTsmtUsgTp) ) {
				if( EAIMessageKeys.ASYNC_SVC.equals(psvItfTp) &&
						STDMessageKeys.SEND_RECV_CD_RECV.equals(returnType)
				) {
										
					standardMessage.setBizData(orgMessage, inboundCharset);
					prop.setProperty(RouteKeys.ROUTING_TYPE, RouteKeys.ROUT_TOPIC);
					prop.setProperty(RouteKeys.TRACE_KEY, eaiMsg.getCurrentSvcMsg().getKeyMgtMsgVl());
					
				}
				else {
					prop.setProperty(RouteKeys.ROUTING_TYPE, RouteKeys.ROUT_CALL);					
				}
			}
			// AA, AS
			else {
				prop.setProperty(RouteKeys.ROUTING_TYPE, RouteKeys.ROUT_QUEUE);
			}

			if(svrLogLevel >= 1 && esbLogger.isInfo()) {
				esbLogger.info(guidLogPrefix + " : FlowControl  ROUTING_TYPE [" + prop.getProperty(RouteKeys.ROUTING_TYPE) + "]");
			}

			beforeRoute(eaiMsg, prop);
			retEaiMsg =  FlowRouter.process(eaiMsg, prop);
			retEaiMsg.setCallProp(prop);
			// 응답메시지에 Error Code기 설정되어 있을 경우
			// ExceptionHandler를 통해 응답메시지를 생성한다.
			if(retEaiMsg != null && !MessageUtil.checkRspErrCd(retEaiMsg.getRspErrCd())) {
				//Timeout이 발생했을 경우 처리 추가됨 short11
				//Timeout이 발생했을 경우 요청거래의 bizmsg를 응담거래의 bizmsg에 넣어준다.
//				if (STDMessageKeys.SEND_RECV_CD_SEND.equals(returnType)){
//					retEaiMsg.getStandardMessage().setBizData(receivedBizData, inboundCharset);						
//				}
				retEaiMsg = ExceptionHandler.handle(retEaiMsg);
			}
		} catch(Exception e) {
			vo.setRspErrorCode("RECEAIIRP090");
			vo.setRspErrorMsg(ExceptionUtil.make(e, vo.getRspErrorCode(), new String[]{vo.getEaiSvcCd()}));

			if (logger.isError()) logger.error(guidLogPrefix + " : " + vo.getRspErrorMsg());
//			eaiMsg.setRspErrCd(vo.getRspErrorCode());
//			eaiMsg.setRspErrMsg(vo.getRspErrorMsg());
			eaiMsg.setRspErr(vo.getRspErrorCode(),vo.getRspErrorMsg());
			try {
				retEaiMsg = ExceptionHandler.handle(eaiMsg);
			} catch(Exception ehe) {
				if (logger.isError()) logger.error(guidLogPrefix + " : ExceptionHandler Call Error : " + e.getMessage());
				throw new RequestProcessorException(vo.getRspErrorMsg());
			}
		}
		afterRoute(eaiMsg, retEaiMsg, prop);

		// TODO : StandardMessage -> SubMessage
		if(Keys.IF_SUBSTANDARD.equals(vo.getStdMsgTypeCode())) {
	        StandardMessage stdMessage = retEaiMsg.getStandardMessage();	        
	        try {
	        	MessageMapper versionMapper = standardManager.getVersionMapper();
	        	subMessage = versionMapper.convertToSub(stdMessage, subMessage);
	        	subMessage.setBizDataCharset(inboundCharset);
	        	retEaiMsg.setSubMessage(subMessage);
	        	
				if(subMessage !=null && logger.isDebug()) {
					logger.debug( String.format("RequestProcessorSupport] SUBSTANDARD-LOGGING SAVE UUID: %s, LOGSEQ: %s, HEADER: %s", 
							retEaiMsg.getSvcOgNo(), retEaiMsg.getLogPssSno(), subMessage.toFixedString(false)) );
				}
			} catch (Exception e) {
				logger.error("convertToSub failed.", e);
			}
		}

		//응답 전 표준 전문의 로직 처리 필요 한 경우를 위해 호출
		try{
			if(EAIMessageKeys.SYNC_SVC.equals(svcTsmtUsgTp) && retEaiMsg != null){
				standardManager.getMessageCoordinator().coordinateBeforeResponse(retEaiMsg.getStandardMessage(), vo, prop, inboundCharset);
			}
		}catch(Exception e){
			logger.error("coordinateBeforeResponse failed.", e);
		}

		// 10. 응답 거래로그 Logging
		if(EAIMessageKeys.SYNC_SVC.equals(svcTsmtUsgTp) &&
				STDMessageKeys.SEND_RECV_CD_SEND.equals(returnType) ) {
			try {
				retEaiMsg.setLogPssSno(400);
//				String retGuid = mapper.getGuid(retEaiMsg.getStandardMessage());
//				String retGuidSeq = mapper.getGuidSeq(retEaiMsg.getStandardMessage());
//				String newGuid = retGuid + StringUtils.defaultString(StringUtils.leftPad(retGuidSeq, UUIDGenerator.GUID_SEQ_LENGTH, '0'));
//				mapper.setGuid(retEaiMsg.getStandardMessage(), newGuid);
				mapper.setSendTime(retEaiMsg.getStandardMessage(), DatetimeUtil.getCurrentTimeMillis());
				
				EAILogSender.send(retEaiMsg, null);
			} catch(Exception e) {
				vo.setRspErrorCode("RECEAIIRP100");
				vo.setRspErrorMsg(ExceptionUtil.make(e, vo.getRspErrorCode(), new String[]{vo.getEaiSvcCd()} ));
				if (logger.isError()) logger.error(guidLogPrefix + " : " + vo.getRspErrorMsg(), e);
			}
			if (logger.isInfo()) logger.info(guidLogPrefix + " : LOG(" + eaiMsg.getLogPssSno() + ") EAIMessage : "+eaiMsg.getEAISvcCd());
		}

		// 11. 비동기 거래의 경우 Return NULL
		if(svrLogLevel >= 1 && esbLogger.isInfo()) {
			esbLogger.info(guidLogPrefix + " : RETURN svcTsmtUsgTp ["+svcTsmtUsgTp+"]");
		}
		
		// 기동시스템과 Sync 이고 요청거래인 경우
		if(EAIMessageKeys.SYNC_SVC.equals(svcTsmtUsgTp) &&
				STDMessageKeys.SEND_RECV_CD_SEND.equals(returnType) )  {
			prop.put(HttpAdapterServiceKey.STANDARD_MESSAGE_OBJECT, retEaiMsg.getStandardMessage());
			Object retMsgObject = retEaiMsg.getStandardMessage().getBizData();
			//sync 시뮬레이터 때문에 추가됨 juns 20211029
			if (eaiServerManager.isTASEnabledEAIServer() && Keys.TESTMASTER_TAS.equals(vo.getTestMasterYn())) { //비표준 시뮬레이터
				if (retMsgObject instanceof byte[]){
					byte[] retMsg = (byte[]) retMsgObject;
					byte[] addSimMsg = null;
					try {
						addSimMsg = SimHeaderUtil.addSimHeader(retEaiMsg.getEAISvcCd(),instCode, retMsg);
					} catch (Exception e) {
						e.printStackTrace();
						addSimMsg = retMsg;
					}
					retMsgObject = addSimMsg;
				}
			}
			prop.setProperty(EAIMessageKeys.ELINK_RET_CODE, retEaiMsg.getRspErrCd());
			
			if (!MessageUtil.checkRspErrCd(retEaiMsg.getRspErrCd())) {
				if (!Keys.IF_STANDARD.equals(vo.getStdMsgTypeCode())
						&& StringUtils.equalsAny(vo.getAdptrMsgType(), MessageType.JSON, MessageType.XML)) {
					throw new HttpStatusException(retEaiMsg.getRspErrMsg(), retEaiMsg.getRspErrCd(),
							HttpStatus.INTERNAL_SERVER_ERROR.value());
				}
			}
			
			return getReturnObject(retEaiMsg, receivedBizData, retMsgObject, vo.getStdMsgTypeCode(), vo.getAdptrMsgType(), vo.getUuid(), inboundCharset);
		}
		// ACKON 처리 : 현재 수신메시지를 응답메시지로 전달함
		else if(EAIMessageKeys.ACKONLY_SVC.equals(svcTsmtUsgTp)) {
			try {
				return receivedBizData;
			} catch(Exception e) {
				vo.setRspErrorCode("RECEAIIRP071");
				vo.setRspErrorMsg(ExceptionUtil.make(e, vo.getRspErrorCode(), new String[]{vo.getEaiSvcCd()}));

//				eaiMsg.setRspErrCd(vo.getRspErrorCode());
//				eaiMsg.setRspErrMsg(vo.getRspErrorMsg());
				eaiMsg.setRspErr(vo.getRspErrorCode(),vo.getRspErrorMsg());
				try {
					EAILogSender.send(eaiMsg, altKeyProp);
					if (logger.isError()) logger.error(guidLogPrefix + " : DB Log 성공 - " + vo.getRspErrorMsg());
				} catch(Exception ex) {
					if (logger.isError()) logger.error(guidLogPrefix + " : DB Log 실패 - " + vo.getRspErrorMsg());
				}
				throw new RequestProcessorException(vo.getRspErrorMsg());

			}
		}
		else {
			// 비동기 거래, S-A의 응답거래시에는 NULL을 리턴
			return null;
			// S-A 에서 Sync 100에 대한 ack 응답시 http header 에 traceId 에 uuid를 넣어달라고 해서..
			//return vo.getUuid();
		}
	}

	protected void afterFoundEaiMessage(EAIMessage eaiMsg, ProcessVO vo, Properties prop) {
	}


	protected String checkCustomInflow(Bucket bucket, EAIMessage eaiMsg, Properties prop) {
		return null;
	}

	protected InflowTargetVO resolveCustomInflowThreshold(Bucket bucket, EAIMessage eaiMsg, Properties prop) {
		return null;
	}

	// 어댑터/인터페이스 유량제어 체크 훅 — 서브클래스에서 오버라이드하여 차단 원인(PER_SECOND/THRESHOLD)을 반환 가능
	protected String checkAdapterInflow(Bucket bucket, String adapterGroupName) {
		return bucket.isAdapterPass(adapterGroupName) ? null : "BLOCKED";
	}

	protected String checkInterfaceInflow(Bucket bucket, String eaiSvcCd) {
		return bucket.isInterfacePass(eaiSvcCd) ? null : "BLOCKED";
	}

	protected String buildInflowTargetErrorMsg(InflowTargetVO inflowTargetVO, String blockedType) {
		return String.format("API 호출 한도 초과 [%s]", inflowTargetVO.getName());
	}

	protected String buildRestrictErrorMsg(EAIMessage eaiMsg, Properties prop) {
		return ExceptionUtil.make(EAIMessageKeys.EAI_BLOCKED_CODE, new String[]{eaiMsg.getEAISvcCd()});
	}

	protected void beforeRoute(EAIMessage eaiMsg, Properties prop) {}

	protected void afterRoute(EAIMessage reqMsg, EAIMessage retMsg, Properties prop) {}

	protected String getInboundErrorResponse(Properties prop, StandardMessage standardMessage, InterfaceMapper mapper
			, String errCode, String errMsg, String charset)  {
		String errorResponse = null;

		String adptrMsgPtrnCd = prop.getProperty(Processor.STD_MESSAGE);
		String messageType = prop.getProperty(Processor.MESSAGE_TYPE);

		if(standardMessage == null) {
			errorResponse = ExceptionHandler.handle(errMsg);
			logger.error("getInboundErrorResponse STDMessage is NULL  adptrMsgPtrnCd["+adptrMsgPtrnCd+"] messageType["+messageType
					+"] response["+errorResponse+"]");
		}
		else {
			String guid = mapper.getGuid(standardMessage);
			logger.error(guid +" - getInboundErrorResponse adptrMsgPtrnCd["+adptrMsgPtrnCd+"] messageType["+messageType+"]");

			if(Keys.IF_STANDARD.equals(adptrMsgPtrnCd)) {
				StandardMessageManager standardManager = StandardMessageManager.getInstance();
				standardManager.getMessageCoordinator().coordinateBeforeInboundErrorResponse(standardMessage);
				errorResponse = getInboundErrorResponseForStandard(standardMessage, mapper, errCode, errMsg, charset,messageType);
			}
			else {
				errorResponse = ExceptionHandler.handle(errMsg);
				logger.error(guid +" - getInboundErrorResponse [" + errorResponse +"]");
			}
		}
		return errorResponse;
	}
	
    protected String getInboundErrorResponseForStandard(StandardMessage standardMessage, InterfaceMapper mapper,
    		String errCode, String errMsg, String charset, String messageType) {
		mapper.nextGuidSeq(standardMessage);
		mapper.setSendRecvDivision(standardMessage, STDMessageKeys.SEND_RECV_CD_RECV);
		StandardMessageManager standardManager = StandardMessageManager.getInstance();
		standardManager.getMessageCoordinator().coordinateSetStandardMessageError(standardMessage, mapper, errCode, errMsg);
		//mapper.setErrorCode(standardMessage, errCode);
		//mapper.setErrorMsg(standardMessage, errMsg);
		String result = null;
		try {
			result = standardMessage.getDataString(messageType, charset);
		}catch(Exception e) {
			
		}
		return result;
    }
    
	public boolean checkRestrictTime(RestrictInfoVO rVo) {
		String[] params = rVo.getEAICtrlDsticCtnt().split("\\|");
		if(params == null)
			return true;
		Calendar calendar = Calendar.getInstance();
		int hour = calendar.get(Calendar.HOUR_OF_DAY);
		int minute = calendar.get(Calendar.MINUTE);
		
		String end = params.length > 6 ? params[6] : null;
		String start = params.length > 5 ? params[5] : null;
		
		if(StringUtils.isNotBlank(start)) {
			int targetHour = Integer.parseInt(start.substring(0, 2));
			int targetMinute = Integer.parseInt(start.substring(2, 4));
			if(hour < targetHour || (hour == targetHour && minute < targetMinute)) {
				return false;
			}
		}
		if(StringUtils.isNotBlank(end) && !"0000".equals(end)) {
			int targetHour = Integer.parseInt(end.substring(0, 2));
			int targetMinute = Integer.parseInt(end.substring(2, 4));
			if(hour > targetHour || (hour == targetHour && minute >= targetMinute)) {
				return false;
			}
		}
		return true;
	}
}