Files
elink-online-common/src/main/java/com/eactive/eai/flowcontrol/jms/FCRabbitMqReceiver.java
T
Rinjae aacc1a389e init
2025-09-05 18:57:45 +09:00

606 lines
18 KiB
Java

package com.eactive.eai.flowcontrol.jms;
import java.util.HashMap;
import java.util.Properties;
import org.apache.commons.lang.SerializationUtils;
import com.eactive.eai.common.exception.ExceptionUtil;
import com.eactive.eai.common.logger.EAILogSender;
import com.eactive.eai.common.message.EAIMessage;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.routing.ElinkESBProcessProxy;
import com.eactive.eai.common.routing.RouteKeys;
import com.eactive.eai.common.routing.RoutingManager;
import com.eactive.eai.common.routing.RoutingVO;
import com.eactive.eai.common.util.JMSSender;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.common.util.MessageUtil;
import com.ext.eai.common.stdmessage.STDMessageKeys;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.GetResponse;
public class FCRabbitMqReceiver {
private static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
private String receiverName;
private String queueName;
private String errorQueueName;
private int receiverCount;
private int errorDelayTimeMs;
private int retryCount;
private String messageSelector = "";
private String description;
// ------------------------------------
// Used for Test
// ------------------------------------
public static final boolean isSingleApplication = false;
// ------------------------------------
// Objects needed for RabbitMQ clients
// ------------------------------------
private ConnectionFactory factory;
private Connection[] connection;
private Channel[] channel;
private ReceiverObject[] receiverObj = null;
// private boolean isTx = false;
// private static final DecimalFormat dFormat = new DecimalFormat("#0.0");
// ------------------------------------
// Thread Runtime Info
// ------------------------------------
private Thread[] receiverThread = null;
private boolean[] threadDowned = null;
public boolean allThreadDowned = true;
// private static String instid = "11";
static {
// String serverName = System.getProperty(Keys.WLI_SERVER_KEY);
// if(serverName != null && serverName.length() > 2) {
// instid = serverName.substring(serverName.length()-2, serverName.length());
// }
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[ FCRabbitMqReceiver INFO ]");
sb.append("\nReveciver Name = " + this.receiverName);
sb.append(", Queue Name = " + this.queueName);
sb.append(", Error Queue Name = " + this.errorQueueName);
sb.append(", Recever Count = " + this.receiverCount);
sb.append(", Delay Time(ms) = " + this.errorDelayTimeMs);
sb.append(", Retry Count = " + this.retryCount);
sb.append(", Selector = " + this.messageSelector);
sb.append(", Description = " + this.description);
return sb.toString();
}
public FCRabbitMqReceiver(String rcvName, String qname, String errorqname, int rcvCount, int delayMs, int retryCnt,
boolean isTx, int ackMode, String msgSelector, String descr) throws Exception {
queueName = qname;
errorQueueName = errorqname;
receiverName = rcvName;
receiverCount = rcvCount;
errorDelayTimeMs = delayMs;
retryCount = retryCnt;
messageSelector = msgSelector;
description = descr;
// trim space
if (queueName != null)
queueName = queueName.trim();
if (errorQueueName != null)
errorQueueName = errorQueueName.trim();
if (receiverName != null)
receiverName = receiverName.trim();
if (messageSelector != null)
messageSelector = messageSelector.trim();
threadDowned = new boolean[receiverCount];
connection = new Connection[receiverCount];
channel = new Channel[receiverCount];
receiverObj = new ReceiverObject[receiverCount];
receiverThread = new Thread[receiverCount];
String clientId = receiverName;
String connectionUrl = "";
try {
if (isSingleApplication) {
if (logger.isDebug())
logger.debug("Local Application Test");
connectionUrl = "amqp://guest:guest@localhost:5672";
} else {
PropManager propManager = PropManager.getInstance();
connectionUrl = propManager.getProperty(RouteKeys.GROUP_NAME,
RouteKeys.ROUTING_QUEUE_CONNECTION_FACTORY);
}
} catch (Exception e) {
if (logger.isError())
logger.error("FCQueueReceiver: get InitialContext Error : ", e);
throw e;
}
if (logger.isInfo())
logger.info("RabbitMQ Connection : " + connectionUrl);
try {
// Create the connections
for (int i = 0; i < receiverCount; i++) {
factory = new ConnectionFactory();
factory.setUri(connectionUrl);
// create each connection
connection[i] = factory.newConnection();
// clientId = receiverName + instid + "-" + i;
// clientId = receiverName + "-" + i;
channel[i] = connection[i].createChannel();
channel[i].queueDeclare(queueName, false, false, false, null);
receiverObj[i] = new ReceiverObject(rcvName, i, channel[i], queueName, this.messageSelector,
this.errorQueueName, this.errorDelayTimeMs, this.retryCount, connectionUrl);
receiverObj[i].setName(clientId);
if (logger.isDebug())
logger.debug("[" + new java.util.Date() + "] Receiver Thread : [" + clientId + "] " + queueName);
receiverThread[i] = new Thread(receiverObj[i]);
receiverThread[i].setDaemon(true);// avoids thread leaks if
}
} catch (Exception e) {
if (logger.isError())
logger.error(" Exception while in constructor : ", e);
close();
throw e;
}
}
public synchronized void startReceiving() throws Exception {
if (!allThreadDowned) {
throw new Exception("Thread Started Already !");
}
try {
allThreadDowned = false;
} catch (Exception jmse) {
if (logger.isError()) {
logger.error("FCQueueReceiver: JMSException while in startReceiving() method : ", jmse);
}
close();
throw jmse;
}
}
public synchronized void clearReceived() {
// Clear Counter
for (int i = 0; i < receiverObj.length; i++) {
receiverObj[i].clearCount();
}
}
public synchronized void stopReceiving() {
// Cleanup each ReceiverObj (stops sending
for (int i = 0; i < receiverObj.length; i++) {
// stops each ReceiverObject
if (receiverObj[i] != null)
receiverObj[i].stopReceiver();
receiverObj[i].stopThread();
}
}
public String[][] getStatus() {
String[][] receiveStatus = new String[receiverObj.length][4];
for (int i = 0; i < receiverObj.length; i++) {
if (receiverObj[i] != null) {
receiveStatus[i][0] = receiverObj[i].getName();
receiveStatus[i][1] = "" + receiverObj[i].msgCounter;
receiveStatus[i][2] = "" + receiverObj[i].msgSuccessCounter;
receiveStatus[i][3] = "" + receiverObj[i].receiverStatus;
}
}
return receiveStatus;
}
public String[] getMessageCount() {
String[] receiveStatus = new String[2];
int receivedTotal = 0;
int successTotal = 0;
for (int i = 0; i < receiverObj.length; i++) {
if (receiverObj[i] != null) {
receivedTotal += receiverObj[i].msgCounter;
successTotal += receiverObj[i].msgSuccessCounter;
}
}
receiveStatus[0] = "" + receivedTotal;
receiveStatus[1] = "" + successTotal;
return receiveStatus;
}
/**
* Closes all connections which in turn stops the receivers and exits JVM
*/
public void close() {
for (int k = 0; k < channel.length; k++) {
try {
if (channel[k] != null) {
channel[k].close();
}
} catch (Exception ex) {
if (logger.isError()) {
logger.error("Exceptionchannel " + k + " message is " + ex.getMessage());
}
}
}
// Close all the connections before exiting the application
for (int i = 0; i < connection.length; i++) {
try {
if (connection[i] != null)
connection[i].close();
} catch (Exception ex) {
if (logger.isError()) {
logger.error("In exit(): Caught Exception trying to close connection(s)", ex);
}
}
}
}
// ////////////////////////////////////////////////////////////////////////
/**
* Inner class: ReceiverObject is an ASYNCHRONOUS receiver.
*/
// ////////////////////////////////////////////////////////////////////////
private class ReceiverObject extends Thread {
private String recvName = null;
private String queueName = null;
// Channel
private Channel channel = null;
private int id; // identifies the ReceiverObject
private int msgCounter = 0; // keeps track of messages
private int msgSuccessCounter = 0;
private int receiverStatus = 0; // 0 - wait, 1 - running, 2 - retry
private Object lockCounter = new Object();// used to synchronize
// updates of the message
// counter
private boolean keepRunning = false; // used to exit the sending loop
// in the run method
private String messageSelector = "";
private String errorQueueName = "";
private int errorDelayTimeMs = 0;
private int retryCount = 0;
private String queueConFactory = "";
/**
* Construct a ReceiverObject param id - the id number should be unique for each
* ReceiverObject param sess - the session object under which the receiver is
* created param q - the Queue object to receiver from
*/
public ReceiverObject(String recvName, int id, Channel channel, String queueName, String messageSelector,
String errorQueueName, int errorDelayMs, int retryCnt, String connectionUri) throws Exception {
init(recvName, id, channel, queueName, messageSelector, errorQueueName, errorDelayMs, retryCnt,
connectionUri);
}
public void init(String recvName, int sid, Channel channel, String queueName, String selector,
String errQueueName, int deplayMs, int retryCnt, String qConFactory) throws Exception {
this.recvName = recvName;
this.id = sid;
this.channel = channel;
this.queueName = queueName;
this.messageSelector = selector;
this.errorQueueName = errQueueName;
this.errorDelayTimeMs = deplayMs;
this.retryCount = retryCnt;
this.queueConFactory = qConFactory;
try {
logger.info("recvName=" + this.recvName + ",messageSelector=" + this.messageSelector);
} catch (Exception jmse) {
if (logger.isError())
logger.error(this.getName()
+ "] In ReceiverObject constructor: JMSException while setting up receiverObj[" + id + "]",
jmse);
close();
throw jmse;
}
}
/**
* Get number of message received since last get
*
* return totaSinceLastGet total number of messages received since the last get
*/
public int getMsgCount() {
synchronized (lockCounter) {
return msgCounter;
}
}
public void setMsgCount(int msgCounter) {
synchronized (lockCounter) {
this.msgCounter = msgCounter;
}
}
public void clearCount() {
synchronized (lockCounter) {
if (logger.isError()) {
logger.error(this.getName() + "] Clearing id: [" + id + "] clearing: " + msgCounter);
}
// reset counter
msgCounter = 0;
msgSuccessCounter = 0;
}
}
public void stopReceiver() {
// stop Receiver from sending which causes thread to exit run() method
// and thus finish.
keepRunning = false;
}
public void stopThread() {
// super.stop();
}
@Override
public void run() {
if (Thread.currentThread().getName().startsWith("Thread-")) {
Thread.currentThread().setName(queueName + "-" + Thread.currentThread().getName());
}
keepRunning = true;
threadDowned[id] = false;
EAIMessage eaiMessage = null;
while (keepRunning) {
try {
receiverStatus = 0;
GetResponse response = channel.basicGet(queueName, false);
if (response == null) {
Thread.sleep(500);
continue;
}
synchronized (lockCounter) {
receiverStatus = 1;
msgCounter++;
}
byte[] body = response.getBody();
try {
eaiMessage = (EAIMessage) SerializationUtils.deserialize(body);
} catch (Exception e) {
throw new RuntimeException(this.getName() + "] EAI메시지가 아닙니다. - " + e.getMessage());
}
// String guidLogPrefix = "RabbitMQ Receiver] GUID["+eaiMessage.getKbMsg().getKBHeader().getGuIdNo()+"] UUID["+eaiMessage.getSvcOgNo()+"] ";
String guidLogPrefix = "RabbitMQ Receiver] GUID["
+ eaiMessage.getMapper().getGuid(eaiMessage.getStandardMessage()) + "] UUID["
+ eaiMessage.getSvcOgNo() + "] ";
if (logger.isDebug()) {
logger.debug(this.getName() + "] receiverObj[" + id + "] Executed - " + eaiMessage.getEAISvcCd()
+ ":" + eaiMessage.getSvcOgNo());
}
RoutingVO routingVO = RoutingManager.getInstance().getRoutingVO(eaiMessage.getFlwCntlRtnNm());
String serviceURI = routingVO.getSyncRoutingPath();
boolean isLocal = true;
HashMap<String, Object> headers = (HashMap<String, Object>) response.getProps().getHeaders();
Properties prop = new Properties();
try {
if (headers != null) {
for (String key : headers.keySet()) {
String propName = key;
String propValue = (String) headers.get(propName);
prop.setProperty(propName, propValue);
}
}
} catch (Exception e) {
throw new RuntimeException(this.getName() + "] 프라퍼티 설정에러 - " + e.getMessage());
}
EAIMessage resEAIMessage = null;
channel.basicAck(response.getEnvelope().getDeliveryTag(), false);
try {
// EAI 거래처리 Error 이고 retry count가 있으면 회수만큼 재시도
// errorDelayTimeMs > 0 면 sleep
int retry = 1;
// retry count 만큼 반복한다.
// -1 : 무한대 retry
// 0 : 처리되지 않는다.
if (this.retryCount == 0) {
if (logger.isWarn()) {
logger.warn(this.getName() + "] SKIP Execute EAISvcCd (" + eaiMessage.getEAISvcCd()
+ ") Retry Count - " + this.retryCount);
}
continue; // 처리를 중단한다.
} else if (this.retryCount > 1) {
receiverStatus = 2;
}
while (((retry <= this.retryCount) || (this.retryCount == -1)) && keepRunning) {
resEAIMessage = ElinkESBProcessProxy.callElinkESBProcess(serviceURI, eaiMessage, isLocal,
prop);
if (resEAIMessage == null) {
if (logger.isWarn()) {
logger.warn(guidLogPrefix + this.getName()
+ "] response EAIMessage is NULL. SKIP Retry Count - " + this.retryCount);
}
break;
}
if (MessageUtil.checkRspErrCd(resEAIMessage.getRspErrCd())) {
break;
} else {
if ((this.retryCount > 0) && (retry <= this.retryCount)) {
if (logger.isWarn()) {
logger.warn(guidLogPrefix + this.getName() + "] Call Error("
+ resEAIMessage.getRspErrCd() + ") Retry - " + retry + "/"
+ this.retryCount);
}
// 다음 처리전에 delay를 준다.
if (this.errorDelayTimeMs > 0) {
if (logger.isWarn()) {
logger.warn(guidLogPrefix + this.getName() + "] Delay Before Retry - "
+ this.errorDelayTimeMs);
}
try {
Thread.sleep(this.errorDelayTimeMs);
} catch (Exception e) {
}
}
} else {
if (logger.isWarn()) {
logger.warn(guidLogPrefix + this.getName() + "] FlowControl Call Error("
+ resEAIMessage.getRspErrCd() + ") No Retry");
}
}
}
retry++;
} // while(retry <= this.retryCount)
} catch (Exception e) {
if (resEAIMessage != null && logger.isError()) {
logger.error(guidLogPrefix + this.getName() + "] FlowControl Call Error("
+ resEAIMessage.getRspErrCd() + ") " + e.getMessage(), e);
}
}
if (response != null) {
// increment message counter
synchronized (lockCounter) {
msgSuccessCounter++;
}
response = null;
}
if (resEAIMessage != null) {
// EAI 거래처리 Error 면
// errorQueueName 이 있으면 redirect
// errorDelayTimeMs > 0 면 sleep
if (!MessageUtil.checkRspErrCd(resEAIMessage.getRspErrCd())) {
if (this.errorQueueName != null && this.errorQueueName.length() > 0) {
if (logger.isWarn()) {
logger.warn(guidLogPrefix + this.getName() + "] Redirect Messsage to - "
+ this.errorQueueName);
}
JMSSender.sendToQueue(eaiMessage, queueConFactory, this.errorQueueName, prop);
}
}
}
// 다음 처리 전에 delay를 준다.
if (this.errorDelayTimeMs > 0) {
if (logger.isWarn()) {
logger.warn(guidLogPrefix + this.getName() + "] Delay Before Receive Next Message - "
+ this.errorDelayTimeMs);
}
try {
Thread.sleep(this.errorDelayTimeMs);
} catch (Exception e) {
}
}
} catch (Exception ex) {
if (logger.isError()) {
logger.error(this.getName() + "] JMSException for receiverObj[" + id + "] - " + ex.getMessage(),
ex);
}
if (eaiMessage != null) {
// --------------------------------------------
// Set Error
String[] msgArgs = new String[1];
msgArgs[0] = eaiMessage.getEAISvcCd();
String rspErrorCode = "RECEAIFQR001"; // [EAI 서비스코드:{1}] 큐수신자가 플로우컨트롤 호출 중 오류 발생
String rspErrorMsg = ExceptionUtil.make(ex, rspErrorCode, msgArgs);
// eaiMessage.setRspErrCd(rspErrorCode);
// eaiMessage.setRspErrMsg(rspErrorMsg);
eaiMessage.setRspErr(rspErrorCode,rspErrorMsg);
// --------------------------------------------
// String mesgDmanDvcd = eaiMessage.getKbMsg().getKBHeader().getTelgmDmndDstcd();
// String mesgDmanDvcd = eaiMessage.getExtMsg().getSendRecv();
String mesgDmanDvcd = eaiMessage.getMapper()
.getSendRecvDivision(eaiMessage.getStandardMessage()); // S|R
if (STDMessageKeys.SEND_RECV_CD_RECV.equals(mesgDmanDvcd)) {
// 응답인 경우
eaiMessage.setLogPssSno(400);
} else {
// 요청 인경우
eaiMessage.setLogPssSno(200);
}
try {
EAILogSender.send(eaiMessage, null);
} catch (Exception e) {
if (logger.isError())
logger.error(this.getName() + "] ErrorLogging :" + e.getMessage());
}
}
// auto reconnect so wait 10 seconds
try {
Thread.sleep(10000);
} catch (Exception e) {
// Do nothing
}
}
} // end of while
// if Stop Event Occurred
if (!keepRunning) {
threadDowned[id] = true;
synchronized (this) {
boolean tmpDowned = false;
for (int j = 0; j < threadDowned.length; j++) {
if (threadDowned[j]) {
tmpDowned = true;
} else {
tmpDowned = false;
break;
}
}
if (tmpDowned) {
allThreadDowned = true;
}
}
}
} // end of run
}// end of inner class ReceiverObject
}