/** * * @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.codec.CodecUtils; import org.myrobotlab.logging.LoggerFactory; import org.myrobotlab.service.interfaces.CommunicationInterface; import org.myrobotlab.service.interfaces.NameProvider; import org.slf4j.Logger; /* * Outbox is a message based thread which sends messages based on addListener lists and current * queue status. It is only aware of the Service directory, addListener lists, and operators. * It can (if possible) take a message and move it to the inbox of a local service, or * (if necessary) send it to a local operator. * * It knows nothing about protocols, serialization methods, or communication methods. */ public class Outbox implements Runnable, Serializable { private static final long serialVersionUID = 1L; public final static Logger log = LoggerFactory.getLogger(Outbox.class); static public final String RELAY = "RELAY"; static public final String IGNORE = "IGNORE"; static public final String BROADCAST = "BROADCAST"; static public final String PROCESSANDBROADCAST = "PROCESSANDBROADCAST"; NameProvider myService = null; LinkedList<Message> msgBox = new LinkedList<Message>(); private boolean isRunning = false; private boolean blocking = false; int maxQueue = 1024; int initialThreadCount = 1; transient ArrayList<Thread> outboxThreadPool = new ArrayList<Thread>(); public HashMap<String, ArrayList<MRLListener>> notifyList = new HashMap<String, ArrayList<MRLListener>>(); CommunicationInterface comm = null; public Outbox(NameProvider myService) { this.myService = myService; } // TODO - config to put message in block mode - with no buffer overrun // TODO - config to drop message without buffer overrun e.g. like UDP public void add(Message msg) { // chase network bugs // log.error(String.format("%s.outbox.add(msg) %s.%s --> %s.%s", // myService.getName(), msg.sender, msg.sendingMethod, msg.name, // msg.method)); synchronized (msgBox) { while (blocking && msgBox.size() == maxQueue) { // queue "full" try { // log.debug("outbox enque msg WAITING "); msgBox.wait(); // Limit the size } catch (InterruptedException ex) { log.debug("outbox add enque msg INTERRUPTED "); } } // we warn if over 10 messages are in the queue - but we will still // process them if (msgBox.size() > maxQueue) { log.warn(String.format("%s outbox BUFFER OVERRUN size %d", myService.getName(), msgBox.size())); } msgBox.addFirst(msg); // Logging.logTime(String.format("outbox %s size %d",myService.getName(), // msgBox.size())); if (log.isDebugEnabled()) { log.debug(String.format("msg [%s]", msg.toString())); } msgBox.notifyAll(); // must own the lock } } public CommunicationInterface getCommunicationManager() { return comm; } // FIXME - consider using a blocking queue now that we are using Java 5.0 @Override public void run() { isRunning = true; while (isRunning) { Message msg = null; synchronized (msgBox) { try { while (msgBox.size() == 0) { // log.debug("outbox run WAITING "); msgBox.wait(); // must own the lock } } catch (InterruptedException ex) { log.debug("outbox run INTERRUPTED "); // msgBox.notifyAll(); isRunning = false; return; } msg = msgBox.removeLast(); // chase network bugs // log.error(String.format("%s.outbox.run(msg) %s.%s -- %s.%s ", // myService.getName(), msg.sender, msg.sendingMethod, msg.name, // msg.method)); // log.debug(String.format("removed from msgBox size now %d", // msgBox.size())); msgBox.notifyAll(); } // RELAY OTHER SERVICE'S MSGS // if the msg name is not my name - then // relay it // WARNING - broadcast apparently means name == "" // why would a message with my name be in my outbox ??? - FIXME // deprecate that logic if (msg.name != null) { // commented out recently -> && // !myService.getName().equals(msg.name) log.debug("{} configured to RELAY ", msg.getName()); comm.send(msg); // recently added - // if I'm relaying I'm not broadcasting...(i think) continue; } // BROADCASTS name=="" WILL DROP DOWN and be processed here if (notifyList.size() != 0) { // get the value for the source method ArrayList<MRLListener> subList = notifyList.get(msg.sendingMethod); if (subList == null) { log.debug(String.format("no static route for %s.%s ", msg.sender, msg.sendingMethod)); // This will cause issues in broadcasts continue; } for (int i = 0; i < subList.size(); ++i) { MRLListener listener = subList.get(i); msg.name = listener.callbackName; msg.method = listener.callbackMethod; comm.send(msg); // must make new for internal queues // otherwise you'll change the name on // existing enqueued messages msg = new Message(msg); } } else { if (log.isDebugEnabled()) { log.debug(String.format("%s/%s(%s)", msg.getName(), msg.method, CodecUtils.getParameterSignature(msg.data) + " notifyList is empty")); } continue; } } // while (isRunning) } public void setCommunicationManager(CommunicationInterface c) { this.comm = c; } public int size() { return msgBox.size(); } public void start() { for (int i = outboxThreadPool.size(); i < initialThreadCount; ++i) { Thread t = new Thread(this, myService.getName() + "_outbox_" + i); outboxThreadPool.add(t); t.start(); } } public void stop() { isRunning = false; for (int i = 0; i < outboxThreadPool.size(); ++i) { Thread t = outboxThreadPool.get(i); t.interrupt(); outboxThreadPool.remove(i); t = null; } } public LinkedList<Message> getMsgBox() { return msgBox; } public int getMaxQueueSize() { return maxQueue; } public boolean isBlocking() { return blocking; } public void setBlocking(boolean blocking) { this.blocking = blocking; } public boolean isRunning() { return isRunning; } }