package org.yajul.jms; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageConsumer; import javax.jms.MessageListener; import javax.naming.InitialContext; import java.util.LinkedList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.ThreadFactory; /** * Generic message receiver. Creates a thread. * <br>User: Joshua Davis * Date: Sep 18, 2007 * Time: 6:17:07 AM */ public class MessageReceiver extends Endpoint { private static Logger log = LoggerFactory.getLogger(MessageReceiver.class); private boolean shouldListen; private MessageListener listener; private Runnable idleAction; private static final int TIMEOUT = 1000; private Thread thread; private final int reconnectDelay; public MessageReceiver(InitialContext ic, String factoryJndiName, String destinationName, MessageListener listener, String messageSelector, int reconnectDelay) { super(ic, factoryJndiName, destinationName, messageSelector); this.listener = listener; this.reconnectDelay = reconnectDelay; } public MessageReceiver(ConnectionFactoryProvider factoryReference, DestinationProvider destinationReference, MessageListener listener, String messageSelector, int reconnectDelay) { super(factoryReference, destinationReference, messageSelector); this.listener = listener; this.reconnectDelay = reconnectDelay; } public void shutdown() { stopListening(); super.close(); if (thread != null) { try { if (log.isDebugEnabled()) log.debug("shutdown() : waiting for thread to join..."); thread.join(); if (log.isDebugEnabled()) log.debug("shutdown() : thread stopped."); } catch (InterruptedException e) { log.error("Unexpected: " + e, e); } } } private void stopListening() { synchronized (this) { shouldListen = false; } } public List<Message> receiveSync(long timeout) { if (thread != null) throw new IllegalStateException("Started in async mode!"); try { startConsumer(); MessageConsumer consumer = getConsumer(); List<Message> list = new LinkedList<Message>(); // Receive a message. Message m = consumer.receive(timeout); // If we got one, keep receiving until there are no more. while (m != null) { list.add(m); m = consumer.receiveNoWait(); } return list; } catch (JMSException e) { throw new RuntimeException(e); } } /** * Returns a list of message objects of a particular class. Messages that are not of class ObjectMessage * will be ignored. * * @param messages The list of JMS messages. * @param clazz the class to filter by * @return a list of all message objects of the specified class. */ public static <T> List<T> filterByMessageObjectClass(List<Message> messages, Class<T> clazz) { List<T> list = new LinkedList<T>(); for (Message message : messages) { Object object = JmsHelper.getObject(message); if (object == null) continue; Class objectClass = object.getClass(); if (clazz.isAssignableFrom(objectClass)) { list.add(clazz.cast(object)); } } return list; } public void start(Runnable idleAction, ThreadFactory factory) { try { this.idleAction = idleAction; if (listener == null) throw new IllegalStateException("No listener!"); super.startConsumer(); shouldListen = true; if (factory == null) factory = new DefaultThreadFactory(); thread = factory.newThread(new ListenerRunnable()); thread.setDaemon(true); thread.start(); } catch (JMSException e) { log.error(e.getMessage(), e); throw new RuntimeException(e); } } private void reconnect() { boolean notConnected = true; close(); int attempts = 0; while (notConnected) { try { try { Thread.sleep(reconnectDelay); } catch (InterruptedException e1) { log.warn("Can't sleep: " + e1); } attempts++; super.startConsumer(); notConnected = false; } catch (JMSException e) { log.error("Attempt # " + attempts + ", unable to connect due to " + e, e); close(); } } // while not connected } private boolean shouldRun() { synchronized (this) { return shouldListen && super.hasConsumer(); } } public Message peek() throws JMSException { return JmsHelper.peek(getSession(), getDestination()); } /** * For automatic naming. */ private static AtomicInteger serialNumber = new AtomicInteger(0); private class DefaultThreadFactory implements ThreadFactory { public Thread newThread(Runnable r) { String threadName = "Receiver-" + serialNumber.incrementAndGet(); return new Thread(r, threadName); } } private class ListenerRunnable implements Runnable { /** * Runs the JMS message consumer. */ public void run() { while (shouldRun()) { try { Message m = receive(TIMEOUT); if (m != null) { listener.onMessage(m); } else { if (idleAction != null) idleAction.run(); } } catch (javax.jms.JMSException jmse) { log.error("*** Reconnecting due to " + jmse, jmse); reconnect(); } catch (Throwable e) { log.error(e.getMessage(), e); log.info("Receiver loop continuing..."); } } log.info("Listener thread stopped."); } } }