/** * * @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.service; import java.io.IOException; import java.util.ArrayList; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import org.myrobotlab.framework.Message; import org.myrobotlab.framework.Service; import org.myrobotlab.framework.ServiceType; import org.myrobotlab.framework.Status; import org.myrobotlab.logging.Level; import org.myrobotlab.logging.LoggerFactory; import org.myrobotlab.logging.Logging; import org.myrobotlab.logging.LoggingFactory; import org.myrobotlab.service.interfaces.SerialDataListener; import org.slf4j.Logger; /** * test catcher is a class to be used to exercise and verify publish, subscribe * and other forms of message sending * * @author GroG * */ public class TestCatcher extends Service implements SerialDataListener { private static final long serialVersionUID = 1L; public final static Logger log = LoggerFactory.getLogger(TestCatcher.class); /** * data to hold the incoming messages */ transient public BlockingQueue<Message> msgs = new LinkedBlockingQueue<Message>(); transient public BlockingQueue<Object> data = new LinkedBlockingQueue<Object>(); ArrayList<Status> errorList = new ArrayList<Status>(); boolean isLocal = true; /** * awesome override to simulate remote services - e.g. in * Serial.addByteListener */ public boolean isLocal() { return isLocal; } public Status onError(Status error) { errorList.add(error); return error; } public TestCatcher(String n) { super(n); } /** * some pub/sub interfaces do not use the Message queue to post their data - * but use a callback thread from the other service as an optimization onByte * is one of those methods */ @Override public Integer onByte(Integer b) { addData("onByte", b); return b; } /** * preProcessHook is used to intercept messages and process or route them * before being processed/invoked in the Service. * * @throws * * @see * org.myrobotlab.framework.Service#preProcessHook(org.myrobotlab. * framework.Message) */ @Override public boolean preProcessHook(Message msg) { try { msgs.put(msg); if (log.isDebugEnabled()) { log.debug(String.format("%d msg %s ", msgs.size(), msg)); } } catch (Exception e) { Logging.logError(e); } return false; } public void clear() { data.clear(); msgs.clear(); } public BlockingQueue<Message> getMsgs() { return msgs; } public Message getMsg(long timeout) throws InterruptedException { Message msg = msgs.poll(timeout, TimeUnit.MILLISECONDS); return msg; } public Object getData(long timeout) throws InterruptedException { Object obj = data.poll(timeout, TimeUnit.MILLISECONDS); return obj; } public BlockingQueue<Message> waitForMsgs(int count) throws InterruptedException, IOException { return waitForMsgs(count, 1000); } public BlockingQueue<Message> waitForMsgs(int count, int timeout) throws InterruptedException, IOException { long start = System.currentTimeMillis(); int interCount = 0; while ((interCount = msgs.size()) < count) { if (timeout < System.currentTimeMillis() - start) { throw new IOException(String.format("timeout - %d msgs under %d ms expected - got %d in %d ms", interCount, timeout, msgs.size(), System.currentTimeMillis() - start)); } sleep(10); } log.warn(String.format("returned %d msgs in %s ms", interCount, System.currentTimeMillis() - start)); return msgs; } public ArrayList<?> waitForData(int count) throws InterruptedException, IOException { return waitForData(count, 1000, 100); } public ArrayList<?> waitForData(int count, int timeout) throws InterruptedException, IOException { return waitForData(count, timeout, 100); } public ArrayList<?> waitForData(int count, int timeout, int pollInterval) throws InterruptedException, IOException { int msgCount = 0; ArrayList<Object> ret = new ArrayList<Object>(); long start = System.currentTimeMillis(); long now = start; while (msgCount < count) { Object msg = data.poll(pollInterval, TimeUnit.MILLISECONDS); if (msg != null) { ret.add(msg); } now = System.currentTimeMillis(); if (now - start > timeout) { throw new IOException(String.format("waited %d ms received %d messages expecting %d in less than %d ms", now - start, ret.size(), count, timeout)); } } log.info("returned %d data in %s ms", ret.size(), now - start); return ret; } public int getMsgCount() { return msgs.size(); } @Override public String onConnect(String portName) { info("connected to %s", portName); addData("onConnect", portName); return portName; } @Override public String onDisconnect(String portName) { info("disconnect to %s", portName); addData("onDisconnect", portName); return portName; } public void checkMsg(String method) throws InterruptedException, IOException { checkMsg(1000, method, (Object[]) null); } public void checkMsg(String method, Object... checkParms) throws InterruptedException, IOException { checkMsg(1000, method, checkParms); } public void checkMsg(long timeout, String method, Object... checkParms) throws InterruptedException, IOException { Message msg = getMsg(timeout); if (msg == null) { log.error(String.format("%s", msg)); throw new IOException(String.format("reached timeout of %d waiting for message", timeout)); } if (checkParms != null && checkParms.length != msg.data.length) { log.error(String.format("%s", msg)); throw new IOException(String.format("incorrect number of expected parameters - expected %d got %d", checkParms.length, msg.data.length)); } if (checkParms == null && msg.data != null) { log.error(String.format("%s", msg)); throw new IOException(String.format("expected null parameters - got non-null")); } if (checkParms != null && msg.data == null) { log.error(String.format("%s", msg)); throw new IOException(String.format("expected non null parameters - got null")); } if (!method.equals(msg.method)) { log.error(String.format("%s", msg)); throw new IOException(String.format("unlike methods - expected %s got %s", method, msg.method)); } for (int i = 0; i < checkParms.length; ++i) { Object expected = checkParms[i]; Object got = msg.data[i]; if (!expected.equals(got)) { throw new IOException(String.format("unlike methods - expected %s got %s", method, msg.method)); } } } /** * "unified"? way of testing direct callbacks. reconstruct the message that * "would have" been created to make this direct callback * * @param method * @param parms * @throws InterruptedException */ public void addData(String method, Object... parms) { try { Message msg = new Message(); msg.method = method; msg.data = parms; msgs.put(msg); } catch (Exception e) { Logging.logError(e); } } static public ServiceType meta = null; /** * This static method returns all the details of the class without it having * to be constructed. It has description, categories, dependencies, and peer * definitions. * * @return ServiceType - returns all the data * */ public void startService() { super.startService(); startPeer("t01"); startPeer("t02"); startPeer("opencv"); } public static void main(String[] args) { LoggingFactory.init(Level.DEBUG); try { Runtime.start("c01", "TestCatcher"); Runtime.start("gui", "GUIService"); /* * TestThrower thrower = new TestThrower("thrower"); * thrower.startService(); * * catcher01.subscribe(thrower.getName(), "throwInteger", * catcher01.getName(), "catchInteger"); * * for (int i = 0; i < 1000; ++i) { thrower.invoke("throwInteger", i); if * (i % 100 == 0) { thrower.sendBlocking(catcher01.getName(), * "catchInteger"); } } */ // thrower.throwInteger(count); } catch (Exception e) { Logging.logError(e); } } static public ServiceType getMetaData() { ServiceType meta = new ServiceType(TestCatcher.class.getCanonicalName()); meta.addDescription("This service is used to test messaging"); meta.setAvailable(false); meta.addCategory("testing", "framework"); return meta; } }