package com.roboclub.robobuggy.ros.internal;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import com.roboclub.robobuggy.ros.Message;
import com.roboclub.robobuggy.ros.Subscriber;
/**
* @author Matt Sebek
* @author Trevor Decker
*
* CHANGELOG: NONE
*
* DESCRIPTION: This class is the singleton that backs all publishers/subscribers.
* MessageServer is a global message queue that all publishers publish to.
* The MailmanThread pulls messages from this global message queue, and puts
* the individual messages into the queues that each Subscriber maintains.
*/
// This class is the backbone of the publisher/subscriber infrastructure.
public class MessageServer {
private static MessageServer _master;
private static Lock singleton_lock = new ReentrantLock();
private Map<String, List<Subscriber>> outbox_mapping = new HashMap<String, List<Subscriber>>();
private ReadWriteLock outbox_lock = new ReentrantReadWriteLock(true);
// Contains all messages that threads want sent.
private LinkedBlockingQueue<Map.Entry<String, Message>> inbox = new LinkedBlockingQueue<Map.Entry<String, Message>>();
private int messagesProcessed = 0;
// Picks up post, and delivers it.
private class MailmanThread implements Runnable {
@Override
public void run() {
while (true) {
Map.Entry<String, Message> request;
try {
request = inbox.take();
} catch (InterruptedException ie) {
System.out.println("I don't think you can get here...");
continue;
}
String topicName = request.getKey();
Message m = request.getValue();
m.setTopicName(topicName);
outbox_lock.readLock().lock();
List<Subscriber> subs = outbox_mapping.get(topicName);
// TODO: if the string ends in '/', then add logic that walks
// through the subscribers and hits all the relevant ones.
if (subs != null) {
for (Subscriber s : subs) {
// Note that this will block on s's inbox lock.
s.putMessage(m);
}
}
outbox_lock.readLock().unlock();
}
}
}
// Puts message in mailbox. Will be handled later.
// Note that this will block on inbox's lock.
public synchronized void sendMessage(String s, Message m) {
AbstractMap.SimpleEntry<String, Message> am = new AbstractMap.SimpleEntry<String, Message>(s, m);
if(!inbox.offer(am)) {
System.out.println("Offer failed; dropping message?");
}
}
/*
* The StatisticsThread
*/
private class StatisticsThread implements Runnable {
// Interesting things: how many messages are in the queue,
// how many messages processed since last time.
public StatisticsThread(int pollingPeriodInMs) {
}
@Override
public void run() {
}
}
private MessageServer() {
(new Thread(new MailmanThread(), "MessageServer-loop")).start();
}
public static MessageServer getMaster() {
singleton_lock.lock();
if (_master == null) {
_master = new MessageServer();
}
singleton_lock.unlock();
return _master;
}
// Will be called from multiple threads
public void addListener(String s, Subscriber ml) {
// Check if topic exists
outbox_lock.writeLock().lock();
List<Subscriber> listeners = outbox_mapping.get(s);
if (listeners == null) {
List<Subscriber> newTopic = new ArrayList<Subscriber>();
newTopic.add(ml);
outbox_mapping.put(s, newTopic);
} else {
listeners.add(ml);
}
outbox_lock.writeLock().unlock();
}
public void removeListener(String s, Subscriber ml) {
outbox_lock.writeLock().lock();
List<Subscriber> listeners = outbox_mapping.get(s);
listeners.remove(ml);
if(listeners.size() == 0) {
outbox_mapping.remove(s, listeners);
}
outbox_lock.writeLock().unlock();
}
}