/*
* Galaxy
* Copyright (c) 2012-2014, Parallel Universe Software Co. All rights reserved.
*
* This program and the accompanying materials are dual-licensed under
* either the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation
*
* or (per the licensee's choosing)
*
* under the terms of the GNU Lesser General Public License version 3.0
* as published by the Free Software Foundation.
*/
package co.paralleluniverse.galaxy.core;
import co.paralleluniverse.galaxy.Cluster;
import co.paralleluniverse.galaxy.cluster.NodeAddressResolver;
import co.paralleluniverse.galaxy.cluster.NodeChangeListener;
import co.paralleluniverse.galaxy.core.Message.LineMessage;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicLong;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author pron
*/
public abstract class AbstractComm<Address> extends ClusterService implements Comm, NodeChangeListener {
private static final Logger LOG = LoggerFactory.getLogger(AbstractComm.class);
final protected NodeAddressResolver<Address> addressResolver;
private final AtomicLong nextMessageId = new AtomicLong(1L);
private final Cluster cluster;
private MessageReceiver receiver;
private long timeout = 200;
protected boolean sendToServerInsteadOfMulticast;
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
public AbstractComm(String name, Cluster cluster, NodeAddressResolver<Address> addressResolver) {
super(name, cluster);
this.addressResolver = addressResolver;
this.cluster = cluster;
cluster.addNodeChangeListener(this);
}
public void setSendToServerInsteadOfMulticast(boolean value) {
assertDuringInitialization();
this.sendToServerInsteadOfMulticast = value;
}
public boolean isSendToServerInsteadOfMulticast() {
return sendToServerInsteadOfMulticast;
}
public void setTimeout(long milliseconds) {
assertDuringInitialization();
this.timeout = milliseconds;
}
protected long getTimeout() {
return timeout;
}
@Override
public void setReceiver(MessageReceiver receiver) {
assertDuringInitialization();
this.receiver = receiver;
}
protected short getNode(Address address) {
return addressResolver.getNodeId(address);
}
protected Address getNodeAddress(short node) {
return addressResolver.getNodeAddress(node);
}
protected long nextMessageId() {
return nextMessageId.getAndIncrement();
}
protected ScheduledExecutorService getScheduler() {
return scheduler;
}
/**
* Blocks
*/
@Override
public void send(final Message message) throws NodeNotFoundException {
assert message.getMessageId() <= 0 ^ message.isResponse();
message.setTimestamp(System.nanoTime());
assert message.isBroadcast() ^ message.getNode() >= 0;
if (message.getNode() == cluster.getMyNodeId()) {
scheduler.execute(new Runnable() {
@Override
public void run() {
receive(message);
}
});
return;
}
if (getCluster().hasServer()) {
if (message.isBroadcast()
&& (sendToServerInsteadOfMulticast || (message instanceof LineMessage && Cache.isReserved(((LineMessage) message).getLine()))))
message.setNode(SERVER);
}
if (message.getNode() == SERVER)
sendToServer(message);
else if (message.getNode() >= 0)
sendToNode(message, message.getNode());
else
broadcast(message);
}
protected void assignMessageId(Message message) {
if (message.getMessageId() < 0)
message.setMessageId(nextMessageId()); // TODO: possible pitfall: b/c this method is not synchronized, two threads may run it concurrently, one would get a smaller id bu the other would put the message in a queue first - broken invariant!
}
/**
* Doesn't add to pending. Assumes a different comm for server, but if not, the derived can add to pending.
*
* @param message
*/
protected void sendToServer(Message message) {
assignMessageId(message);
LOG.debug("Sending to server: {}", message);
}
protected void sendToNode(Message message, short node) throws NodeNotFoundException {
if (LOG.isDebugEnabled())
LOG.debug("Sending to node {}: {}", node, message);
final Address address = getNodeAddress(node);
if (address == null) {
LOG.warn("Address not found for node {} while sending {}!", node, message);
throw new NodeNotFoundException(node);
} else
sendToNode(message, node, address);
}
protected abstract void sendToNode(Message message, short node, Address address);
protected abstract void broadcast(Message message);
protected final void receive(Message message) {
if (getCluster().isMaster())
receiver.receive(message);
}
@Override
public void nodeAdded(short id) {
}
@Override
public void nodeSwitched(short id) {
}
@Override
public void nodeRemoved(short id) {
}
@Override
protected void shutdown() {
super.shutdown();
scheduler.shutdownNow();
}
}