package siebog.interaction.bsp;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.ejb.LocalBean;
import javax.ejb.Lock;
import javax.ejb.LockType;
import javax.ejb.Remote;
import javax.ejb.Stateful;
import javax.inject.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import siebog.agents.AID;
import siebog.agents.Agent;
import siebog.agents.AgentManagerBean;
import siebog.agents.XjafAgent;
import siebog.interaction.ACLMessage;
import siebog.interaction.Performative;
import siebog.utils.ObjectFactory;
@Stateful
@Remote(Agent.class)
@LocalBean
@Lock(LockType.WRITE)
public class BarrierBean extends XjafAgent {
private static final long serialVersionUID = 1L;
private static final Logger LOG = LoggerFactory.getLogger(BarrierBean.class);
public static final String PROTOCOL = "siebog-bsp";
private static final long TIMEOUT = 10000;
private int superstep;
private Set<AID> registered;
// agents that are processing messages in the current superste
private Set<AID> processing;
@Inject
private AgentManagerBean localAgm;
public BarrierBean() {
registered = new HashSet<>();
processing = new HashSet<>();
}
@Override
protected void onMessage(ACLMessage msg) {
switch (msg.performative) {
case SUBSCRIBE:
registerAgent((AID) msg.contentObj);
break;
case CANCEL:
deregisterAgent((AID) msg.contentObj);
break;
case INFORM:
agentCompletedSuperstep((AID) msg.contentObj);
break;
case REQUEST: // timeout
onTimeout((Superstep) msg.contentObj);
break;
default:
break;
}
}
private void registerAgent(AID aid) {
registered.add(aid);
nextSuperstepIfPossible();
LOG.info("Registered agent {}.", aid);
}
private void deregisterAgent(AID aid) {
registered.remove(aid);
if (processing.remove(aid)) {
nextSuperstepIfPossible();
}
LOG.info("Deregistered agent {}.", aid);
}
private void agentCompletedSuperstep(AID aid) {
processing.remove(aid);
nextSuperstepIfPossible();
}
private void onTimeout(Superstep superstep) {
if (superstep.getCounter() == this.superstep) {
LOG.info("Barrier timeout in superstep #{}, pending agents: {}",
superstep.getCounter(), processing);
filterUnavailableAgents();
if (!nextSuperstepIfPossible()) {
scheduleTimeout();
}
}
}
private boolean nextSuperstepIfPossible() {
if (processing.isEmpty() && !registered.isEmpty()) {
processing.addAll(registered);
signalSuperstep(superstep + 1, processing);
scheduleTimeout();
return true;
}
return false;
}
private void signalSuperstep(int superstep, Set<AID> receivers) {
this.superstep = superstep;
ACLMessage msg = buildSuperstepMsg(receivers);
ObjectFactory.getMessageManager().post(msg);
}
private ACLMessage buildSuperstepMsg(Set<AID> receivers) {
ACLMessage msg = new ACLMessage(Performative.INFORM);
msg.sender = myAid;
msg.protocol = PROTOCOL;
msg.receivers.addAll(processing);
msg.contentObj = buildSuperstep();
return msg;
}
private void scheduleTimeout() {
ACLMessage msg = new ACLMessage(Performative.REQUEST);
msg.receivers.add(myAid);
msg.contentObj = buildSuperstep();
msm().post(msg, TIMEOUT);
}
private void filterUnavailableAgents() {
Iterator<AID> i = processing.iterator();
while (i.hasNext()) {
AID aid = i.next();
if (agentAlive(aid)) {
signalSuperstep(superstep, Collections.singleton(aid));
} else {
i.remove();
}
}
}
private boolean agentAlive(AID aid) {
try {
Agent agent = localAgm.getAgentReference(aid);
agent.ping();
return true;
} catch (Exception ex) {
return false;
}
}
private Superstep buildSuperstep() {
return new Superstep(myAid.getName(), superstep);
}
}