package org.apache.s4.comm;
import org.apache.s4.base.Emitter;
import org.apache.s4.base.Listener;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import com.google.inject.Inject;
import com.google.inject.name.Named;
/*
* Test class to test communication protocols. As comm-layer connections need to be
* made including acquiring locks, the test is declared abstract and needs to be
* extended with appropriate protocols.
*
* At a high-level, the test accomplishes the following:
* <ul>
* <li> Create Send and Receive Threads </li>
* <li> SendThread sends out a pre-defined number of messages to all the partitions </li>
* <li> ReceiveThread receives all/most of these messages </li>
* <li> To avoid the receiveThread waiting for ever, it spawns a TimerThread that would
* interrupt after a pre-defined but long enough interval </li>
* <li> The receive thread reports on number of messages received and dropped </li>
* </ul>
*
*/
public abstract class SimpleDeliveryTest {
protected CommWrapper sdt;
protected String lockdir;
static class CommWrapper {
private static final int MESSAGE_COUNT = 200;
private static final int TIMER_THREAD_COUNT = 100;
private final Emitter emitter;
private final Listener listener;
private final int interval;
public Thread sendThread, receiveThread;
private final int messagesExpected;
private int messagesReceived = 0;
@Inject
public CommWrapper(@Named("emitter.send.interval") int interval, Emitter emitter, Listener listener) {
this.emitter = emitter;
this.listener = listener;
this.interval = interval;
this.messagesExpected = MESSAGE_COUNT * this.emitter.getPartitionCount();
this.sendThread = new SendThread();
this.receiveThread = new ReceiveThread();
}
public boolean moreMessages() {
return ((messagesExpected - messagesReceived) > 0);
}
class SendThread extends Thread {
@Override
public void run() {
try {
for (int partition = 0; partition < emitter.getPartitionCount(); partition++) {
for (int i = 0; i < MESSAGE_COUNT; i++) {
byte[] message = (new String("message-" + i)).getBytes();
emitter.send(partition, message);
Thread.sleep(interval);
}
}
} catch (Exception e) {
e.printStackTrace();
return;
}
}
}
/*
* TimerThread - interrupts the passed thread, after specified time-interval.
*/
class TimerThread extends Thread {
private final Thread watchThread;
private Integer sleepCounter;
TimerThread(Thread watchThread) {
this.watchThread = watchThread;
this.sleepCounter = new Integer(TIMER_THREAD_COUNT);
}
public void resetSleepCounter() {
synchronized (this.sleepCounter) {
this.sleepCounter = TIMER_THREAD_COUNT;
}
}
public void clearSleepCounter() {
synchronized (this.sleepCounter) {
this.sleepCounter = 0;
}
}
private int getCounter() {
synchronized (this.sleepCounter) {
return this.sleepCounter--;
}
}
@Override
public void run() {
try {
while (getCounter() > 0) {
Thread.sleep(interval);
}
watchThread.interrupt();
} catch (Exception e) {
e.printStackTrace();
}
}
}
class ReceiveThread extends Thread {
@Override
public void run() {
// start the timer thread to interrupt if blocked for too long
TimerThread timer = new TimerThread(this);
timer.start();
while (messagesReceived < messagesExpected) {
byte[] message = listener.recv();
timer.resetSleepCounter();
if (message != null)
messagesReceived++;
else
break;
}
timer.clearSleepCounter();
}
}
}
/*
* All tests extending this class need to implement this method
*/
@Before
public abstract void setup();
/**
* Tests the protocol. If all components function without throwing exceptions, the test passes. The test also
* reports the loss of messages, if any.
*
* @throws InterruptedException
*/
@Test
public void testCommLayerProtocol() throws InterruptedException {
try {
// start send and receive threads
sdt.sendThread.start();
sdt.receiveThread.start();
// wait for them to finish
sdt.sendThread.join();
sdt.receiveThread.join();
Assert.assertTrue("Guaranteed message delivery", !sdt.moreMessages());
} catch (Exception e) {
e.printStackTrace();
Assert.fail("The comm protocol has failed basic functionality test");
}
Assert.assertTrue("The comm protocol seems to be working crash-free", true);
System.out.println("Done");
}
}