/** * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.transport; import java.util.HashSet; import java.util.List; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.Lifecycle; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.TerminatableJob; /** * A message dispatcher that can send a batch of messages. * <p> * An abstract implementation of a system that can consume batches of * messages and dispatch them to a {@link BatchByteArrayMessageReceiver}. * It will create a single <em>non-daemon</em> thread to pull messages * off the underlying message source. * <p> * This class is mutable, but thread-safe */ public abstract class AbstractBatchMessageDispatcher implements Lifecycle { /** Logger. */ private static final Logger s_logger = LoggerFactory.getLogger(AbstractBatchMessageDispatcher.class); /** * The source of data. */ private final ByteArraySource _source; /** * The message receivers. */ private final Set<BatchByteArrayMessageReceiver> _receivers = new HashSet<BatchByteArrayMessageReceiver>(); /** * The job for collecting messages. */ private final MessageCollectionJob _collectionJob = new MessageCollectionJob(); /** * The name. */ private volatile String _name = "AbstractBatchMessageDispatcher"; /** * The thread. */ private volatile Thread _dispatchThread; /** * Creates a dispatcher. * * @param source the data source, not null */ protected AbstractBatchMessageDispatcher(ByteArraySource source) { ArgumentChecker.notNull(source, "source"); _source = source; } //------------------------------------------------------------------------- /** * Adds a receiver to the dispatcher. * * @param receiver the message receiver, not null */ public void addReceiver(BatchByteArrayMessageReceiver receiver) { ArgumentChecker.notNull(receiver, "receiver"); synchronized (_receivers) { _receivers.add(receiver); } } /** * Gets a copy of the set of receivers. * * @return the receivers, modifiable copy, not null */ public Set<BatchByteArrayMessageReceiver> getReceivers() { final Set<BatchByteArrayMessageReceiver> receivers; synchronized (_receivers) { receivers = new HashSet<BatchByteArrayMessageReceiver>(_receivers); } return receivers; } //------------------------------------------------------------------------- /** * Gets the source of data. * * @return the array source, not null */ public ByteArraySource getSource() { return _source; } /** * Gets the job being used. * * @return the collection job, not null */ public MessageCollectionJob getCollectionJob() { return _collectionJob; } /** * Gets the name. * * @return the name, not null */ public String getName() { return _name; } /** * Sets the name. * * @param name the name to set, not null */ public void setName(String name) { ArgumentChecker.notNull(name, "name"); _name = name; } /** * Gets the thread in use. * * @return the dispatch thread, null if not running */ public Thread getDispatchThread() { return _dispatchThread; } //------------------------------------------------------------------------- @Override public boolean isRunning() { return (getDispatchThread() != null && getDispatchThread().isAlive()); } @Override public synchronized void start() { if (isRunning()) { throw new IllegalStateException("Cannot start a running dispatcher"); } final Thread dispatchThread = new Thread(getCollectionJob(), getName()); dispatchThread.setDaemon(false); dispatchThread.start(); _dispatchThread = dispatchThread; } @Override public synchronized void stop() { if (!isRunning()) { throw new IllegalStateException("Cannot stop a dispatcher which isn't running"); } getCollectionJob().terminate(); try { getDispatchThread().join(30000L); } catch (InterruptedException e) { Thread.interrupted(); s_logger.info("Interrupted while waiting for dispatch thread to finish"); } if (getDispatchThread().isAlive()) { s_logger.warn("Waited 30 seconds for dispatch thread to finish, but it didn't terminate normally"); } _dispatchThread = null; } /** * Dispatch messages to the receivers. * * @param messages the messages, not null */ protected void dispatchMessages(final List<byte[]> messages) { final BatchByteArrayMessageReceiver[] receivers; synchronized (_receivers) { receivers = (BatchByteArrayMessageReceiver[]) _receivers.toArray(new BatchByteArrayMessageReceiver[_receivers.size()]); } for (BatchByteArrayMessageReceiver receiver : receivers) { receiver.messagesReceived(messages); } } //------------------------------------------------------------------------- /** * A job that collects messages and can be terminated. */ protected class MessageCollectionJob extends TerminatableJob { @Override protected void runOneCycle() { final List<byte[]> batchMessages = getSource().batchReceive(1000L); if (batchMessages == null || batchMessages.isEmpty()) { return; } dispatchMessages(batchMessages); } } }