/*
* Copyright: Almende B.V. (2014), Rotterdam, The Netherlands
* License: The Apache Software License, Version 2.0
*/
package com.almende.eve.algorithms.simulation;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;
import com.almende.eve.capabilities.handler.Handler;
import com.almende.eve.protocol.InboxProtocol;
import com.almende.eve.protocol.Meta;
import com.almende.eve.protocol.jsonrpc.formats.JSONMessage;
import com.almende.eve.protocol.jsonrpc.formats.JSONRequest;
import com.almende.util.jackson.JOM;
import com.almende.util.uuid.UUID;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* The Class SimulationInboxProtocol.
*/
public class SimulationInboxProtocol extends InboxProtocol {
private final static Boolean[] ISWORK = new Boolean[] { false };
private final static Integer[] INBOXCNT = new Integer[] { 0 };
private final static Integer[] LATCH = new Integer[] { 0 };
private BlockingQueue<Meta> outbox = new PriorityBlockingQueue<Meta>();
private SimulationInboxProtocolConfig params = null;
private boolean isAtomicNetwork = false;
private final static Integer[] MSGINCNT = new Integer[] { 0 };
private final static Integer[] MSGOUTCNT = new Integer[] { 0 };
/**
* Instantiates a new simulation inbox protocol.
*
* @param params
* the params
* @param handle
* the handle
*/
public SimulationInboxProtocol(ObjectNode params, Handler<Object> handle) {
super(params, handle, false);
this.params = SimulationInboxProtocolConfig.decorate(params);
isAtomicNetwork = this.params.isAtomicNetwork();
synchronized (INBOXCNT) {
INBOXCNT[0]++;
}
initLooper();
}
@Override
public boolean outbound(Meta msg) {
inWork();
boolean res = false;
if (isAtomicNetwork) {
final JSONMessage message = JSONMessage.jsonConvert(msg.getMsg());
if (message != null) {
msg.setMsg(message);
if (message.isRequest()) {
JSONRequest request = (JSONRequest) message;
if ("scheduler.receiveTracerReport".equals(request
.getMethod())) {
return super.outbound(msg);
}
}
}
synchronized (MSGOUTCNT) {
res = super.outbound(msg);
if (res) {
MSGOUTCNT[0]++;
}
}
} else {
res = super.outbound(msg);
}
return res;
}
@Override
public boolean inbound(Meta msg) {
inWork();
boolean res = false;
final JSONMessage message = JSONMessage.jsonConvert(msg.getMsg());
UUID id = null;
if (message != null) {
msg.setMsg(message);
if (message.getId() == null || message.getId().isNull()) {
message.setId(JOM.getInstance().valueToTree(
new UUID().toString()));
}
id = new UUID(message.getId().textValue());
}
synchronized (getInbox()) {
if (id != null) {
res = super.inbound(new QueueEntry(msg, id));
} else {
res = super.inbound(new QueueEntry(msg));
}
}
if (isAtomicNetwork) {
// Make sure all outstanding messages have arrived.
if (message != null && message.isRequest()) {
JSONRequest request = (JSONRequest) message;
if ("scheduler.receiveTracerReport".equals(request.getMethod())) {
return res;
}
}
synchronized (MSGINCNT) {
MSGINCNT[0]++;
synchronized (MSGOUTCNT) {
if (MSGINCNT[0] == MSGOUTCNT[0]) {
MSGINCNT[0] = 0;
MSGOUTCNT[0] = 0;
MSGINCNT.notifyAll();
}
}
}
}
return res;
}
private static void inWork() {
if (!ISWORK[0]) {
synchronized (ISWORK) {
ISWORK[0] = true;
ISWORK.notifyAll();
}
}
}
private static void outOfWork() {
if (ISWORK[0]) {
synchronized (ISWORK) {
ISWORK[0] = false;
}
}
}
private void waitForWork() {
synchronized (ISWORK) {
while (!ISWORK[0]) {
try {
ISWORK.wait();
} catch (InterruptedException e) {}
}
}
}
private void waitForNetwork() {
synchronized (MSGINCNT) {
boolean wait = false;
synchronized (MSGOUTCNT) {
wait = MSGINCNT[0] != MSGOUTCNT[0];
}
if (wait) {
try {
MSGINCNT.wait();
} catch (InterruptedException e) {}
}
}
}
private void waitForHeartBeat() {
waitForWork();
if (isAtomicNetwork) {
// Make sure all outstanding messages have arrived.
waitForNetwork();
}
synchronized (LATCH) {
LATCH[0]++;
if (LATCH[0] == INBOXCNT[0]) {
LATCH[0] = 0;
outOfWork();
LATCH.notifyAll();
} else {
try {
LATCH.wait();
} catch (InterruptedException e) {}
}
}
// Copy inbox to -currentSendBox
synchronized (getInbox()) {
outbox.addAll(getInbox());
getInbox().clear();
}
}
@Override
public Meta getNext(BlockingQueue<Meta> inbox) throws InterruptedException {
if (!inbox.isEmpty()) {
return super.getNext(inbox);
}
return null;
}
private void initLooper() {
super.chgLooper(new Runnable() {
// Agent thread
@Override
public void run() {
stop[0] = false;
while (!stop[0]) {
waitForHeartBeat();
try {
Meta next = getNext(outbox);
while (next != null) {
next(next);
final Boolean[] sequencer = getSequencer();
synchronized (sequencer) {
while (!sequencer[0]) {
sequencer.wait();
}
}
next = getNext(outbox);
}
} catch (InterruptedException e) {
// Nothing todo.
}
}
}
});
}
}