package org.oobd.base;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.json.JSONException;
import org.oobd.base.support.Onion;
/**
*
* \brief Message-Port to supply sending, reading and waiting for messages
* \ingroup core
*/
public class MessagePort {
final Vector myMsgs;
int replyID = 0;
int waitingforID = 0;
String portName = "";
boolean extendWaitTime = false;
/**
* setup a new message port \param name Name of the port, just for debugging
*/
public MessagePort(String name) {
myMsgs = new Vector();
portName = name;
}
/**
* \brief gets a message from the core and sorts it in the waiting message
* quere
*
* This function is called by the core when a message for the messageport
* owner comes in.
*
* As it need to be sure, that an unexpected system message does not wake up
* a task who is waiting for an answer to a message he has sented, the
* messages with a reply ID are been sorted into the reveive msq quere as
* first and the task is waked up then accourdingly.
*
* @param thisMessage
*/
public void receive(Message thisMessage) {
int thisReplyID;
try {
thisReplyID = thisMessage.content.getInt("replyID");
} catch (JSONException ex) {
thisReplyID = -1;
}
//Logger.getLogger(MessagePort.class.getName()).log(Level.INFO, "Port " + portName + " Msg reply id:" + Integer.toString(thisReplyID) + " Waiting ID:" + Integer.toString(waitingforID));
if (thisReplyID > 0) { // this is a reply for something
//Logger.getLogger(MessagePort.class.getName()).log(Level.INFO, "A reply message " + thisReplyID);
if (thisReplyID == waitingforID) { //only if this is really the message we are waiting for, otherways just forget this obviously old reply
synchronized (myMsgs) {
myMsgs.insertElementAt(thisMessage, 0); //put the message as the first one in the message quere
waitingforID = 0; // reset the waitingFor Flag
myMsgs.notify();
//Logger.getLogger(MessagePort.class.getName()).log(Level.INFO, "saved and notified");
}
} else {
Logger.getLogger(MessagePort.class.getName()).log(Level.WARNING, "wrong reply id (received:" + thisReplyID + " waiting for:" + waitingforID + ") message deleted, extend waittime");
extendWaitTime = true;
}
} else { // this is a normal, non replied msg
//Logger.getLogger(MessagePort.class.getName()).log(Level.INFO, "A non reply message " + thisReplyID);
synchronized (myMsgs) {
myMsgs.add(thisMessage);
// Logger.getLogger(MessagePort.class.getName()).log(Level.INFO, "msg saved");
if (waitingforID == 0) { //if we not just waiting for a delicated reply message
myMsgs.notify();
} else {
Logger.getLogger(MessagePort.class.getName()).log(Level.INFO, "Msg NOT notified, as we wait for " + waitingforID);
}
}
}
}
/**
* \brief get the next message in the quere \ingroup core
*
* @param wait if true, waits forever for a message, otherways returns
* immediadly, even without a message
* @return message
*/
protected Message getMsg(boolean wait) {
if (wait == true) {
Message msg = null;
while (msg == null) {
msg = getMsg(-1);
}
return msg;
} else {
return getMsg(0);
}
}
/**
* \brief get the name of that message port \ingroup core
*
* @return message port name
*/
public String getMsgPortName() {
return portName;
}
/**
* \brief sends an answer to a message \ingroup core
*
* @param msg the original message
* @param onion the content of the message
* @return message
*/
public void replyMsg(Message msg, Onion content) {
String rec = msg.rec;
msg.rec = msg.sender;
msg.sender = rec;
msg.content = content;
Core.getSingleInstance().transferMsg(msg);
}
/**
* \brief send a message and waits for the answer \ingroup core
*
* @param msg the message
* @param timeout timeout in ms to wait for an answer. <0: wait forever, =0
* : Don't wait (which would be a little bit senseless ;-) @return
*/
public Message sendAndWait(Message msg, int timeout) {
replyID = (replyID > 10000) ? 1 : replyID + 1;
waitingforID = replyID;
try {
msg.content.put("msgID", replyID);
} catch (JSONException ex) {
Logger.getLogger(MessagePort.class.getName()).log(Level.SEVERE, null, ex);
}
Core.getSingleInstance().transferMsg(msg);
return getMsg(timeout);
}
/**
* \brief interupts the waiting thread
*
*/
public void interuptWait() {
synchronized (myMsgs) {
myMsgs.notify();
}
}
/**
* \brief get the next message in the quere \ingroup core
*
* @param timeout in ms to wait for an message . 0: wait forever, <0 don't
* wait @retu r n message
*/
public Message getMsg(int timeout) {
if (waitingforID != 0) {
if (myMsgs.isEmpty()) {
if (timeout < 0) { // wait forever
try {
synchronized (myMsgs) {
myMsgs.wait();
}
} catch (InterruptedException ex) {
return null;
}
waitingforID = 0; // reset the waitingFor Flag
return (Message) myMsgs.remove(0);
} else if (timeout == 0) {
waitingforID = 0; // reset the waitingFor Flag
return null;
} else {
try {
extendWaitTime = true;
while (extendWaitTime) {
extendWaitTime = false; // in case an older message comes back, the flag is set and the wait loop does another round
synchronized (myMsgs) {
myMsgs.wait(timeout);
}
}
} catch (InterruptedException ex) {
waitingforID = 0; // reset the waitingFor Flag
return null;
}
waitingforID = 0; // reset the waitingFor Flag
if (myMsgs.isEmpty()) {
return null;
} else {
return (Message) myMsgs.remove(0);
}
}
} else {
Message firstInQueue = (Message) myMsgs.remove(0);
try {
if (firstInQueue.content.getInt("replyID") == waitingforID) {
return firstInQueue;
} else {
return null;
}
} catch (JSONException ex) {
return null;
}
}
}
// this should only reached if waitingforID == 0, so no wait
if (myMsgs.isEmpty()) {
if (timeout < 0) { // wait forever
try {
synchronized (myMsgs) {
myMsgs.wait();
}
} catch (InterruptedException ex) {
return null;
}
return (Message) myMsgs.remove(0);
} else if (timeout == 0) {
return null;
} else {
try {
extendWaitTime = true;
while (extendWaitTime) {
extendWaitTime = false; // in case an older message comes back, the flag is set and the wait loop does another round
synchronized (myMsgs) {
myMsgs.wait(timeout);
}
}
} catch (InterruptedException ex) {
return null;
}
if (myMsgs.isEmpty()) {
return null;
} else {
return (Message) myMsgs.remove(0);
}
}
} else {
return (Message) myMsgs.remove(0);
}
}
}