/**
*
* @author greg (at) myrobotlab.org
*
* This file is part of MyRobotLab (http://myrobotlab.org).
*
* MyRobotLab is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version (subject to the "Classpath" exception
* as provided in the LICENSE.txt file that accompanied this code).
*
* MyRobotLab is distributed in the hope that it will be useful or fun,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* All libraries in thirdParty bundle are subject to their own license
* requirements - please refer to http://myrobotlab.org/libraries for
* details.
*
* Enjoy !
*
* */
package org.myrobotlab.framework;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import org.myrobotlab.logging.LoggerFactory;
import org.slf4j.Logger;
public class Inbox implements Serializable {
private static final long serialVersionUID = 1L;
public final static Logger log = LoggerFactory.getLogger(Inbox.class.getCanonicalName());
String name;
LinkedList<Message> msgBox = new LinkedList<Message>();
boolean isRunning = false;
boolean bufferOverrun = false;
boolean blocking = false;
int maxQueue = 1024; // will need to adjust unit test if you change this
// value
HashMap<Long, Object[]> blockingList = new HashMap<Long, Object[]>();
public Inbox() {
this("Inbox");
}
public Inbox(String name) {
this.name = name;
}
public void add(Message msg) {
if ((msg.historyList.contains(name))) {
log.error(String.format("* %s dumping duplicate message %s.%s msgid - %d %s", name, msg.name, msg.method, msg.msgId, msg.historyList));
return;
}
msg.historyList.add(name);
synchronized (msgBox) {
while (blocking && msgBox.size() == maxQueue) // queue "full"
{
try {
msgBox.wait();
} catch (InterruptedException ex) {
log.debug("inbox enque msg INTERRUPTED " + name);
}
}
if (msgBox.size() > maxQueue) {
bufferOverrun = true;
log.warn(String.format("%s inbox BUFFER OVERRUN dumping msg size %d - %s", name, msgBox.size(), msg.method));
} else {
msgBox.addFirst(msg);
// Logging.logTime(String.format("inbox - %s size %d", name,
// msgBox.size()));
if (log.isDebugEnabled()) {
log.debug(String.format("%s.msgBox + 1 = %d", name, msgBox.size()));
}
msgBox.notifyAll(); // must own the lock
}
}
}
public void clear() {
msgBox.clear();
}
// FIXME - implement with HashSet or HashMap !!!!
// ******* TEST WITHOUT DUPE CHECKING *********
public boolean duplicateMsg(ArrayList<RoutingEntry> history) {
for (int i = 0; i < history.size(); ++i) {
if (history.get(i).name.equals(name)) {
log.error("dupe message {} {}", name, history);
return true;
}
}
return false;
}
/**
* Blocks and waits on a message put on the queue of the InBox. Service
* default behavior will wait on getMsg for a message, when they recieve a
* message they invoke it.
*
* @return the Message on the queue
* @see Message
*/
public Message getMsg() throws InterruptedException {
/*
* TODO - remove below - Inbox will call switchboards
* serializer/deserializer & communicator send/recieve interface switchboard
* has references to serializer and communicator - also all configuration
* needed At this level ALL details on where the Message / Message came from
* should be hidden and interfaces should be exposed only-
*/
Message msg = null;
synchronized (msgBox) {
while (msg == null) { // while no messages && no messages that are
// blocking
if (msgBox.size() == 0) {
// log.debug("Inbox WAITING " + name);
msgBox.wait(); // must own the lock
} else {
msg = msgBox.removeLast();
log.debug(String.format("%s.msgBox -1 %d", name, msgBox.size()));
// --- sendBlocking support begin --------------------
// TODO - possible safety check msg.status == Message.RETURN
// &&
if (blockingList.containsKey(msg.msgId)) {
Object[] returnContainer = blockingList.get(msg.msgId);
if (msg.data == null) // TODO - don't know if this is
// correct but this works for
// null data now
{
returnContainer[0] = null;
} else {
returnContainer[0] = msg.data[0]; // transferring
// return data !
}
synchronized (returnContainer) {
blockingList.remove(msg.msgId);
returnContainer.notify(); // addListener sender
}
msg = null; // do not invoke this msg - sendBlocking has
// been notified data returned
}
// --- sendBlocking support end --------------------
}
}
msgBox.notifyAll();
}
return msg;
}
public boolean isBufferOverrun() {
return bufferOverrun;
}
public void setBlocking(boolean toBlock) {
blocking = toBlock;
}
public int size() {
return msgBox.size();
}
}