package org.intracetest.agent;
import java.util.List;
import java.util.ArrayList;
import java.util.concurrent.Delayed;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
import org.junit.Before;
import static org.junit.Assert.assertTrue;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.DelayQueue;
import org.intrace.output.trace.TraceHandler;
import static org.junit.Assert.assertEquals;
/** This is a 'proof of concept' test that demonstrates how to use a DelayQueue to emit
* data in batches, instead of individual queue items. Only jdk code and test code is exercised here.
*
* This test sticks 9 items into a DelayQueue. These items comes out in 3 batches.
* The delays (in milliseconds) between the batches are defined in the offsetA, offsetB, and offsetC variables.
*/
public class DelayQueueProofOfConceptTest
{
public static final int POISON_PILL = 3;
private static long offsetA = 53L;
private static long offsetB = 109L;
private static long offsetC = 150L;
private static long baseline = 0;
BlockingQueue<Pizza> pizzaQueue = new DelayQueue<Pizza>();
PizzaEaterRunnable pizzaEater = new PizzaEaterRunnable(pizzaQueue);
List<Long> expectedArrivalTimestamps = new ArrayList<Long>();
@Before
public void setup() throws java.lang.InterruptedException {
baseline = System.currentTimeMillis();
/**
* B A T C H # 1
*/
pizzaQueue.put( createTypeOne(baseline) ); expectedArrivalTimestamps.add( new Long(baseline+offsetA) );
pizzaQueue.put( createTypeOne(baseline) ); expectedArrivalTimestamps.add( new Long(baseline+offsetA) );
pizzaQueue.put( createTypeOne(baseline) ); expectedArrivalTimestamps.add( new Long(baseline+offsetA) );
pizzaQueue.put( createTypeOne(baseline) ); expectedArrivalTimestamps.add( new Long(baseline+offsetA) );
pizzaQueue.put( createTypeOne(baseline) ); expectedArrivalTimestamps.add( new Long(baseline+offsetA) );
/**
* B A T C H # 2
*/
pizzaQueue.put( createTypeTwo(baseline) ); expectedArrivalTimestamps.add( new Long(baseline+offsetB) );
pizzaQueue.put( createTypeTwo(baseline) ); expectedArrivalTimestamps.add( new Long(baseline+offsetB) );
pizzaQueue.put( createTypeTwo(baseline) ); expectedArrivalTimestamps.add( new Long(baseline+offsetB) );
/**
* B A T C H # 3
*/
pizzaQueue.put( createTypeThree(baseline) ); expectedArrivalTimestamps.add( new Long(baseline+offsetC) );
}
private Pizza createTypeOne(long baseline) {
Pizza newPizza = new Pizza( baseline + offsetA, 1 );
return newPizza;
}
private Pizza createTypeTwo(long baseline) {
Pizza newPizza = new Pizza( baseline + offsetB, 2 );
return newPizza;
}
private Pizza createTypeThree(long baseline) {
Pizza newPizza = new Pizza( baseline + offsetC, POISON_PILL );
return newPizza;
}
@Test
public void testDelayQueueBurst() {
try {
PizzaEaterRunnable eater = new PizzaEaterRunnable(pizzaQueue);//queue was filled in setup method
Thread t = new Thread(eater);
t.start();
//Time will elapse as DelayQueue emits
//a three bursts of pizzas from the queue, 1 second between each burst.
//The thread will finish when a 'poison pill' is detected by the thread.
t.join();
boolean compare = eater.arrivalTimestamps.equals(expectedArrivalTimestamps);
String actual = eater.arrivalTimestamps.toString();
String expected = expectedArrivalTimestamps.toString();
assertTrue("did not get expected arrival times. Expected [" + expected + "] Actual [" + actual + "]",compare);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
public static class Pizza implements Delayed {
private long departureTimestampMillis = 0L;
private int type = 0;
public Pizza(long val, int type) {
this.departureTimestampMillis = val;
this.type = type;
System.out.println("New Pizza [" + this.toString() + "]");
}
public String toString() {
return "Pizza {departureTimestampMillis=" + this.departureTimestampMillis + ",type=" + this.type + "}";
}
public int getType() {
return this.type;
}
@Override
public long getDelay(TimeUnit unit) {
long ts = System.currentTimeMillis();
//System.out.println("current [" + ts + "] " + this.toString()) ;
return this.departureTimestampMillis - ts;
}
@Override
public int compareTo(Delayed o) {
long rc = this.getDelay( TimeUnit.MILLISECONDS ) - o.getDelay( TimeUnit.MILLISECONDS );
return (int)rc;
}
}
class PizzaEaterRunnable implements Runnable {
private static final int batchSize = 1000000;//Only concerned with huge/horrible events, otherwise monitoring is required to tune batch size.
public List<Long> arrivalTimestamps = new ArrayList<Long>();
BlockingQueue<Pizza> pizzaQueue;
public volatile boolean stopRequested = false;
public PizzaEaterRunnable(BlockingQueue<Pizza> val) {
this.pizzaQueue = val;
}
private void eatMany(List<Pizza> manyPizzas) {
for(Pizza pizza : manyPizzas)
eat(pizza);
}
private void eat(Pizza pizza) {
arrivalTimestamps.add( new Long(System.currentTimeMillis()) );
if (pizza.getType() == DelayQueueProofOfConceptTest.POISON_PILL) {
this.stopRequested = true;
}
}
@Override
public void run() {
List<Pizza> manyPizzas = new ArrayList<Pizza>();
while (!stopRequested) {
try {
//System.out.println(Thread.currentThread().getName() + " eating up bread ");
Pizza pizza = pizzaQueue.poll(100, TimeUnit.MILLISECONDS);
if (pizza !=null) {
manyPizzas.add(pizza);
pizzaQueue.drainTo(manyPizzas,batchSize);
eatMany(manyPizzas);
manyPizzas.clear();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}