package org.oddjob.beanbus.destinations;
import java.util.Iterator;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import javax.inject.Inject;
import org.apache.log4j.Logger;
import org.oddjob.Stoppable;
import org.oddjob.arooa.life.Configured;
import org.oddjob.arooa.life.Initialised;
import org.oddjob.beanbus.AbstractDestination;
import org.oddjob.beanbus.BusConductor;
import org.oddjob.beanbus.BusCrashException;
import org.oddjob.beanbus.BusEvent;
import org.oddjob.beanbus.TrackingBusListener;
/**
* @oddjob.description A Queue for beans. A work in progress.
*
* @oddjob.example
*
* A simple example.
*
* {@oddjob.xml.resource org/oddjob/beanbus/destinations/BeanQueueExample.xml}
*
* @oddjob.example
*
* An example in BeanBus.
*
* {@oddjob.xml.resource org/oddjob/beanbus/destinations/BeanQueueExample2.xml}
*
*
* @author rob
*
* @param <E> The type of element on the queue.
*/
public class BeanQueue<E> extends AbstractDestination<E>
implements Iterable<E>, Stoppable {
private static final Logger logger = Logger.getLogger(BeanQueue.class);
private int capacity;
private volatile BlockingQueue<Object> queue;
private final static Object STOP = new Object();
private String name;
private volatile int taken;
private volatile int waitingConusmers;
private final TrackingBusListener busListener =
new TrackingBusListener() {
@Override
public void busStarting(BusEvent event) throws BusCrashException {
logger.debug("Clearing Queue on Start.");
reset();
}
@Override
public void busStopping(BusEvent event) throws BusCrashException {
stop();
}
};
@Inject
public void setBeanBus(BusConductor busConductor) {
busListener.setBusConductor(busConductor);
}
@Initialised
public void init() {
if (capacity == 0) {
queue = new LinkedBlockingDeque<Object>();
}
else {
queue = new ArrayBlockingQueue<Object>(capacity);
}
}
@Configured
public void reset() {
queue.clear();
taken = 0;
}
public void stop() {
logger.debug("Stopping Queue.");
try {
queue.put(STOP);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
@Override
public boolean add(E bean) {
try {
queue.put(bean);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
return true;
}
@Override
public Iterator<E> iterator() {
return new BlockerIterator();
}
@Override
public boolean isEmpty() {
return getSize() == 0;
}
/**
* The implementation of the blocking iterator.
*/
class BlockerIterator implements Iterator<E> {
private E next;
private int taken;
@SuppressWarnings("unchecked")
@Override
public boolean hasNext() {
if (next != null) {
return true;
}
Object first = queue.poll();
if (first == null) {
// queue must be empty.
try {
++waitingConusmers;
first = queue.take();
} catch (InterruptedException e) {
logger.info("Inturrupted waiting for next value.");
Thread.currentThread().interrupt();
return false;
}
finally {
--waitingConusmers;
}
}
if (first == STOP) {
try {
queue.put(STOP);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return false;
}
else {
next = (E) first;
++this.taken;
++BeanQueue.this.taken;
return true;
}
}
@Override
public E next() {
try {
return next;
}
finally {
next = null;
}
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public String toString() {
return "Iterator for " + BeanQueue.this.toString() +
", taken=" + taken;
}
}
public int getSize() {
Queue<?> queue = this.queue;
return (queue == null ? 0: queue.size());
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getTaken() {
return taken;
}
public int getCapacity() {
return capacity;
}
public void setCapacity(int capacity) {
if (this.queue != null) {
throw new IllegalStateException(
"Capicity can't be dynamic because the queue has already been created.");
}
this.capacity = capacity;
}
public int getWaitingConusmers() {
return waitingConusmers;
}
@Override
public String toString() {
if (name == null) {
return getClass().getSimpleName();
}
else {
return name;
}
}
}