package cz.cuni.mff.d3s.been.socketworks;
import cz.cuni.mff.d3s.been.cluster.Service;
import cz.cuni.mff.d3s.been.cluster.ServiceException;
import cz.cuni.mff.d3s.been.mq.MessagingException;
import cz.cuni.mff.d3s.been.socketworks.oneway.OneWayMessaging;
import cz.cuni.mff.d3s.been.socketworks.oneway.ReadOnlyHandler;
import cz.cuni.mff.d3s.been.socketworks.twoway.ReadReplyHandlerFactory;
import cz.cuni.mff.d3s.been.socketworks.twoway.TwoWayMessaging;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.TreeMap;
/**
* A message dispatcher intended as high-level abstraction over inter-process
* socket works.
*
* @author radek.macha@gmail.com
*/
public class MessageDispatcher implements Service {
private static final Logger log = LoggerFactory.getLogger(MessageDispatcher.class);
private final String hostname;
private final Map<String, QueueGuard> guards;
private MessageDispatcher(String hostname) {
this.hostname = hostname;
this.guards = new TreeMap<String, QueueGuard>();
}
/**
* Create a message dispatcher (handles communication channels with task processes)
*
* @param hostname Hostname of this node under which message queues should be accessible from tasks
*
* @return A dispatcher server
*/
public static MessageDispatcher create(String hostname) {
return new MessageDispatcher(hostname);
}
/**
* Get the port a named queue listens on
*
* @param queueName Name of the queue
*
* @return The port the queue listens on
*/
public Integer getPortForQueue(String queueName) {
return guards.get(queueName).getPort();
}
/**
* Create a new one-way named queue and associate it with a provided handler.
*
* Use this method <b>BEFORE</b> {@link #start()}. If you reverse the order,
* the queue will be correctly created (and disposed of upon termination), but
* will remain inactive.
*
* @param queueName
* Name of the queue to create. Must be unique
* @param handler
* Handler that specifies what should be done with incoming messages
*
* @throws ServiceException
* If sockets associated with any of the requested message queues
* cannot be bound
*/
public void addReceiveHandler(String queueName, ReadOnlyHandler handler) throws ServiceException {
try {
guards.put(queueName, OneWayMessaging.createServer(hostname, queueName, handler));
} catch (MessagingException e) {
throw new ServiceException(String.format("Unable to initialize receiver for queue \"%s\".", queueName), e);
}
}
/**
* Create a new two-way named queue and associated it with a provided handler
* factory. This means that messages arriving to this queue will be handled by
* handlers supplied by that factory.
*
* Use this method <b>BEFORE</b> {@link #start()}. If you reverse the order,
* the queue will be correctly created (and disposed of upon termination), but
* will remain inactive.
*
* @param queueName
* Name of the created queue. Must be unique
*
* @param handlerFactory
* Factory that provides handlers servicing the requests (messages)
*
* @throws ServiceException
* If sockets associated with any of the requested message queues
* cannot be found
*/
public void addRespondingHandler(String queueName, ReadReplyHandlerFactory handlerFactory) throws ServiceException {
try {
guards.put(queueName, TwoWayMessaging.createServer(hostname, queueName, handlerFactory));
} catch (MessagingException e) {
throw new ServiceException(String.format("Unable to initialize receiver for queue \"%s\"", queueName), e);
}
}
/**
* Create a map of current bindings provided by this {@link MessageDispatcher}
*
* @return Guard bindings
*/
public Map<String, String> getBindings() {
final Map<String,String> bindings = new TreeMap<String,String>();
for(Map.Entry<String, QueueGuard> binding: guards.entrySet()) {
bindings.put(binding.getKey(), binding.getValue().getConnection());
}
return bindings;
}
@Override
public void start() throws ServiceException {
for (QueueGuard guard : guards.values()) {
guard.listen();
}
}
@Override
public final void stop() {
for (QueueGuard guard : guards.values()) {
try {
log.debug("Terminating {}", guard);
guard.terminate();
log.debug("{} terminated", guard);
} catch (MessagingException e) {
log.error("Failed to purge queue {}");
}
}
}
}