All Downloads are FREE. Search and download functionalities are using the official Maven repository.

se.skltp.ei.intsvc.update.collect.JmsMessageCollectionController Maven / Gradle / Ivy

/**
 * Copyright (c) 2013 Sveriges Kommuner och Landsting (SKL). 
 *
 * This file is part of SKLTP.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package se.skltp.ei.intsvc.update.collect;

import java.util.List;
import java.util.UUID;

import javax.jms.DeliveryMode;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.soitoolkit.commons.mule.core.PropertyNames;

import se.skltp.ei.intsvc.EiConstants;

/**
 * Reads a number of messages (determined by a
 * MessageCollectionStrategy) from a JMS queue into a
 * MessageCollectionStrategy and then transmits the collected
 * messages to an JMS output queue.
 * 
 * @author hakan
 */
public class JmsMessageCollectionController implements Runnable {
	private static final Logger log = LoggerFactory
			.getLogger(JmsMessageCollectionController.class);

	private String jmsInputQueue = "skltp.ei.collect";
	private String jmsOutputQueue = "skltp.ei.process";
	private String jmsErrorQueue = "DLQ.skltp.ei.collect";
	private long jmsReceiveTimeoutMillis = 30000;
	private MessageCollectionStrategy messageCollectionStrategy;
	private QueueConnectionFactory qcf;
	private QueueConnection conn;

	public void setJmsInputQueue(String jmsInputQueue) {
		this.jmsInputQueue = jmsInputQueue;
	}

	public void setJmsOutputQueue(String jmsOutputQueue) {
		this.jmsOutputQueue = jmsOutputQueue;
	}

	public void setJmsErrorQueue(String jmsErrorQueue) {
		this.jmsErrorQueue = jmsErrorQueue;
	}

	public void setJmsReceiveTimeoutMillis(long jmsReceiveTimeoutMillis) {
		this.jmsReceiveTimeoutMillis = jmsReceiveTimeoutMillis;
	}

	public void setMessageCollectionStrategy(
			MessageCollectionStrategy messageCollectionStrategy) {
		this.messageCollectionStrategy = messageCollectionStrategy;
	}

	public void setQueueConnectionFactory(QueueConnectionFactory qcf) {
		this.qcf = qcf;
	}

	public void init() {
		startMessageCollectionThread();
	}

	private void startMessageCollectionThread() {
		try {
			conn = qcf.createQueueConnection();
			log.debug("got a JMS connection");
			conn.start();
			Thread t = new Thread(this, getClass().getName());
			t.setDaemon(true);
			t.start();
			log.info(
					"started JMS message listener on queue: {}, receiveTimeout: {}, output queue: {}, error queue: {}",
					new Object[] { jmsInputQueue, jmsReceiveTimeoutMillis,
							jmsOutputQueue, jmsErrorQueue });
		} catch (JMSException e) {
			String errMsg = "failed to get connection to JMS broker";
			log.error(errMsg, e);
			// signal error, app should not start without a QCF connection
			throw new RuntimeException(errMsg, e);
		}
	}

	@Override
	public void run() {
		runCollectMessagesLoop();
	}

	private void runCollectMessagesLoop() {
		while (true) {
			long minimumLoopTimeForFastErrorLoopProtection = jmsReceiveTimeoutMillis;
			long startLoopTime = System.currentTimeMillis();
			Session session = null;
			try {
				// start JMS transaction
				boolean isTransacted = true;
				session = conn.createSession(isTransacted,
						Session.SESSION_TRANSACTED);
				MessageConsumer msgConsumer = session.createConsumer(session
						.createQueue(jmsInputQueue));

				// begin reading JMS messages
				boolean commitTransactionDueToErrors = false;
				while (!commitTransactionDueToErrors
						&& !messageCollectionStrategy
								.isCollectedMessagesReadyToBeTransmitted()) {
					// Note: listen with a timeout since we need to periodically
					// ask the collection strategy if we should transmit the
					// buffer and start a new collection
					Message msg = msgConsumer.receive(jmsReceiveTimeoutMillis);

					if (msg == null) {
						log.debug("JMS recieve timed out after [ms]: {}",
								jmsReceiveTimeoutMillis);
					} else if (msg instanceof TextMessage) {
						log.debug("JMS receive returned a TextMessage");
						String text = ((TextMessage) msg).getText();
						try {
							// catch all exceptions since we are likely to have
							// a poison message if an exception is thrown
							messageCollectionStrategy.collectMessage(text);
						} catch (Exception e) {
							// commit current transaction directly/prematurely
							// to avoid complexity
							// with adding thresholds (count/time) for error
							// messages being held in the transaction
							commitTransactionDueToErrors = true;
							log.error(
									"could not collect message, will send message to error queue",
									e);
							sendMessageToErrorQueue(session, msg);
						}
					} else {
						log.error(
								"JMS receive returned unexpected message type: {}, will send message to error queue",
								msg.getClass().getName());
						commitTransactionDueToErrors = true;
						sendMessageToErrorQueue(session, msg);
					}
				}

				sendMessagesToOutQueue(session,
						messageCollectionStrategy
								.getCollectedMessagesAndClearBuffer());

				session.commit();
				log.debug("commited JMS transaction");

			} catch (JMSException e) {
				log.error("error occured in message receive loop", e);
				if (session != null) {
					try {
						session.rollback();
					} catch (JMSException e1) {
						log.warn("failed to rollback JMS transaction", e);
					}
				}
			} finally {
				if (session != null) {
					try {
						session.close();
					} catch (JMSException e) {

					}
				}
			}
			// protect against fast spinning in case of errors
			waitIfFastSpinning(minimumLoopTimeForFastErrorLoopProtection,
					startLoopTime);
		}
	}

	private void waitIfFastSpinning(
			long minimumLoopTimeForFastErrorLoopProtection, long startLoopTime) {
		if (System.currentTimeMillis() <= startLoopTime
				+ minimumLoopTimeForFastErrorLoopProtection) {
			try {
				log.debug("entered fast spinning loop deplay ...");
				Thread.sleep(minimumLoopTimeForFastErrorLoopProtection);
			} catch (InterruptedException e) {
				// do nothing
			}
		}
	}

	private void sendMessagesToOutQueue(Session session,
			List collMsgs) throws JMSException {
		if (collMsgs.isEmpty()) {
			log.debug("no collected msgs in buffer to transmit");
			return;
		}

		TextMessage outMsg = session.createTextMessage();
		MessageProducer producer = session.createProducer(session
				.createQueue(jmsOutputQueue));
		producer.setDeliveryMode(DeliveryMode.PERSISTENT);
		for (CollectedMessage collMsg : collMsgs) {
			log.debug("sending msg to output queue: {}", jmsOutputQueue);
			outMsg.setText(collMsg.getPayload());
			
			// add properties and statistics for logging
			// origConsumerId: set to "collect" since multiple service consumers can have contributed to the resulting message 
			outMsg.setStringProperty(EiConstants.EI_ORIGINAL_CONSUMER_ID, "collect");
			// create a new correlationId for collect, multiple service consumers can have contributed to the resulting message
			outMsg.setStringProperty(PropertyNames.SOITOOLKIT_CORRELATION_ID, UUID.randomUUID().toString());
			outMsg.setStringProperty(EiConstants.EI_LOG_MESSAGE_TYPE, EiConstants.EI_LOG_MESSAGE_TYPE_UPDATE);
			outMsg.setStringProperty(EiConstants.EI_LOG_NUMBER_OF_RECORDS_IN_MESSAGE, String.valueOf(collMsg.getStatisticsNrRecords()));
			
			outMsg.setStringProperty(EiConstants.EI_LOG_IS_UPDATE_ROUTED_VIA_COLLECT, String.valueOf(Boolean.TRUE));
			outMsg.setStringProperty(EiConstants.EI_LOG_UPDATE_COLLECT_NR_MESSAGES, String.valueOf(collMsg.getStatisticsCollectedNrMessages()));
			outMsg.setStringProperty(EiConstants.EI_LOG_UPDATE_COLLECT_NR_RECORDS, String.valueOf(collMsg.getStatisticsCollectedNrRecords()));
			outMsg.setStringProperty(EiConstants.EI_LOG_UPDATE_COLLECT_BUFFER_AGE_MS, String.valueOf(collMsg.getStatisticsBufferAgeMs()));
			
			producer.send(outMsg);
		}
	}

	private void sendMessageToErrorQueue(Session session, Message msg)
			throws JMSException {
		log.debug("sending message to error queue: {}", jmsErrorQueue);
		MessageProducer producer = session.createProducer(session
				.createQueue(jmsErrorQueue));
		producer.setDeliveryMode(DeliveryMode.PERSISTENT);
		producer.send(msg);
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy