package cgl.iotcloud.core.transport;
import cgl.iotcloud.core.msg.MessageContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Contains a set of channels belonging to a particular group. The channels are
* distributed across the available brokers. A Channel group consumes or produces messages from or
* to a single logical queue.
*/
public class ChannelGroup {
private static Logger LOG = LoggerFactory.getLogger(ChannelGroup.class);
/**
* Name of the group, this name is a unique combination of sensor group and channel name
*/
private String name;
/**
* Keep track of the channels for a broker host
*/
protected Map<BrokerHost, List<Channel>> brokerHostToProducerChannelMap = new HashMap<BrokerHost, List<Channel>>();
protected Map<BrokerHost, List<Channel>> brokerHostToConsumerChannelMap = new HashMap<BrokerHost, List<Channel>>();
/**
* The available brokers
*/
private List<BrokerHost> brokerHosts;
/**
* The index is used to pick the next broker available
*/
private int consumerIndex = 0;
private int producerIndex = 0;
private Lock lock = new ReentrantLock();
/**
* These are the queues we put the messages coming from the channels. The actual message consumers or
* senders use these queues
*/
protected Map<BrokerHost, BlockingQueue<MessageContext>> consumerQueues = new HashMap<BrokerHost, BlockingQueue<MessageContext>>();
protected Map<BrokerHost, BlockingQueue<MessageContext>> producerQueues = new HashMap<BrokerHost, BlockingQueue<MessageContext>>();
protected AbstractTransport transport;
protected Map<BrokerHost, Manageable> consumers = new HashMap<BrokerHost, Manageable>();
protected Map<BrokerHost, Manageable> producers = new HashMap<BrokerHost, Manageable>();
protected Map<BrokerHost, ConsumingWorker> consumingWorkers = new HashMap<BrokerHost, ConsumingWorker>();
protected boolean run;
protected String prefix;
protected int brokerIndex;
public ChannelGroup(String name, String prefix, List<BrokerHost> brokerHosts, AbstractTransport transport, int initialBrokerIndex) {
this.name = name;
this.brokerHosts = brokerHosts;
this.transport = transport;
this.prefix = prefix;
this.brokerIndex = initialBrokerIndex;
for (BrokerHost brokerHost : brokerHosts) {
brokerHostToConsumerChannelMap.put(brokerHost, new ArrayList<Channel>());
brokerHostToProducerChannelMap.put(brokerHost, new ArrayList<Channel>());
consumerQueues.put(brokerHost, new ArrayBlockingQueue<MessageContext>(1024));
producerQueues.put(brokerHost, new ArrayBlockingQueue<MessageContext>(1024));
}
this.run = true;
}
public BrokerHost addChannel(Channel channel) {
lock.lock();
try {
// add the channel and return the broker host
Manageable manageable;
if (channel.getDirection() == Direction.OUT) {
BrokerHost host = brokerHosts.get(brokerIndex);
List<Channel> producerChannels = brokerHostToProducerChannelMap.get(host);
BlockingQueue<MessageContext> channelOutQueue = producerQueues.get(host);
if (!producers.containsKey(host)) {
manageable = transport.registerProducer(host, prefix, channel.getProperties(), producerQueues.get(host));
producers.put(host, manageable);
manageable.start();
}
// now register the channel with the brokers map
// check weather you have a sender consumer for this host
channel.setOutQueue(channelOutQueue);
producerChannels.add(channel);
producerChannels.add(new Channel("", Direction.OUT));
LOG.info("Registering channel {} with group {} and host {}", channel.getName(), name, host.toString());
incrementProducerIndex();
return host;
} else if (channel.getDirection() == Direction.IN) {
BrokerHost host = brokerHosts.get(brokerIndex);
List<Channel> consumerChannels = brokerHostToConsumerChannelMap.get(host);
if (!consumers.containsKey(host)) {
BlockingQueue<MessageContext> channelInQueue = consumerQueues.get(host);
manageable = transport.registerConsumer(host, prefix, channel.getProperties(), channelInQueue);
consumers.put(host, manageable);
ConsumingWorker worker;
if (channel.isGrouped()) {
worker = new ConsumingWorker(consumerChannels, channelInQueue);
} else {
worker = new ConsumingWorker(consumerChannels, channelInQueue, true);
}
Thread thread = new Thread(worker);
thread.start();
manageable.start();
consumingWorkers.put(host, worker);
}
// now register the channel with the brokers map
// check weather you have a sender consumer for this host
consumerChannels.add(channel);
LOG.info("Registering channel {} with group {} and host {}", channel.getName(), name, host.toString());
incrementConsumerIndex();
return host;
}
} finally {
lock.unlock();
}
return null;
}
public void removeChannel(Channel channel) {
lock.lock();
try {
if (channel.getDirection() == Direction.OUT) {
BrokerHost registeredHost = null;
for (Map.Entry<BrokerHost, List<Channel>> e : brokerHostToProducerChannelMap.entrySet()) {
List<Channel> channels = e.getValue();
Iterator<Channel> channelIterator = channels.iterator();
while (channelIterator.hasNext()) {
Channel c = channelIterator.next();
if (c.equals(channel)) {
registeredHost = e.getKey();
channelIterator.remove();
// if there are no more channels remove the producer
if (channels.size() == 0) {
Manageable producer = producers.remove(registeredHost);
producer.stop();
}
break;
}
}
if (registeredHost != null) {
break;
}
}
} else if (channel.getDirection() == Direction.IN) {
BrokerHost registeredHost = null;
for (Map.Entry<BrokerHost, List<Channel>> e : brokerHostToConsumerChannelMap.entrySet()) {
List<Channel> channels = e.getValue();
Iterator<Channel> channelIterator = channels.iterator();
while (channelIterator.hasNext()) {
Channel c = channelIterator.next();
if (c.equals(channel)) {
registeredHost = e.getKey();
channelIterator.remove();
// if there are no more channels remove the producer
if (channels.size() == 0) {
ConsumingWorker worker = consumingWorkers.remove(registeredHost);
worker.stop();
Manageable consumer = consumers.remove(registeredHost);
consumer.stop();
}
break;
}
}
if (registeredHost != null) {
break;
}
}
}
} finally {
lock.unlock();
}
}
private void incrementConsumerIndex() {
if (consumerIndex == brokerHosts.size() - 1) {
consumerIndex = 0;
} else {
consumerIndex++;
}
}
private void incrementProducerIndex() {
if (producerIndex == brokerHosts.size() - 1) {
producerIndex = 0;
} else {
producerIndex++;
}
}
public BrokerHost getHostForChannel(Channel channel) {
BrokerHost registeredHost = null;
lock.lock();
try {
if (channel.getDirection() == Direction.OUT) {
for (Map.Entry<BrokerHost, List<Channel>> e : brokerHostToProducerChannelMap.entrySet()) {
List<Channel> channels = e.getValue();
Iterator<Channel> channelIterator = channels.iterator();
while (channelIterator.hasNext()) {
Channel c = channelIterator.next();
if (c.equals(channel)) {
registeredHost = e.getKey();
break;
}
}
if (registeredHost != null) {
break;
}
}
} else if (channel.getDirection() == Direction.IN) {
for (Map.Entry<BrokerHost, List<Channel>> e : brokerHostToConsumerChannelMap.entrySet()) {
List<Channel> channels = e.getValue();
Iterator<Channel> channelIterator = channels.iterator();
while (channelIterator.hasNext()) {
Channel c = channelIterator.next();
if (c.equals(channel)) {
registeredHost = e.getKey();
break;
}
}
if (registeredHost != null) {
break;
}
}
}
} finally {
lock.unlock();
}
return registeredHost;
}
public void start() {
for (Manageable manageable : consumers.values()) {
manageable.start();
}
for (Manageable manageable : producers.values()) {
manageable.start();
}
}
public void stop() {
for (Manageable manageable : consumers.values()) {
manageable.stop();
}
for (Manageable manageable : producers.values()) {
manageable.stop();
}
}
}