package com.google.code.joto.eventrecorder.writer;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import com.google.code.joto.eventrecorder.RecordEventSummary;
/**
* Asynchronous implementation for RecordEventWriter
*/
public class AsyncQueueRecordEventWriter extends AbstractRecordEventWriter {
private static class QueueEventData {
RecordEventSummary event;
Serializable objData;
RecordEventWriterCallback callback;
public QueueEventData(RecordEventSummary event, Serializable objData,
RecordEventWriterCallback callback) {
super();
this.event = event;
this.objData = objData;
this.callback = callback;
}
}
private static enum ThreadStatus {
stopped,
running,
running_interrupting
}
/** underlying proxy target object */
private RecordEventWriter target;
private Object lock = new Object();
private Queue<QueueEventData> queue = new LinkedList<QueueEventData>();
private ThreadStatus currThreadStatus = ThreadStatus.stopped;
//-------------------------------------------------------------------------
public AsyncQueueRecordEventWriter(RecordEventWriter target) {
this.target = target;
}
//-------------------------------------------------------------------------
public void startQueue() {
synchronized(lock) {
switch(currThreadStatus) {
case stopped:
new Thread(new Runnable() {
public void run() {
doRunThreadLoop();
}
}).start();
break;
case running:
// do nothing
break;
case running_interrupting:
currThreadStatus = ThreadStatus.running; // reset
break;
}
}
}
public void stopQueue() {
synchronized(lock) {
switch(currThreadStatus) {
case stopped:
// do nothing
break;
case running:
currThreadStatus = ThreadStatus.running_interrupting;
break;
case running_interrupting:
// do nothing
break;
}
}
}
public void waitEmptyQueue() {
for(;;) {
synchronized(lock) {
if (queue.isEmpty()) {
break;
} else {
try {
lock.wait(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
}
public void waitThreadStopped() {
for(;;) {
synchronized(lock) {
if (currThreadStatus == ThreadStatus.stopped) {
break;
} else {
try {
lock.wait(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
}
@Override
public void addEvent(RecordEventSummary event, Serializable objData,
RecordEventWriterCallback callback) {
if (!isEnable()) {
return;
}
if (!isEnable(event)) {
return;
}
QueueEventData queueObj = new QueueEventData(event, objData, callback);
synchronized(lock) {
queue.add(queueObj);
if (queue.size() == 1) { // was empty
lock.notify();
}
}
}
protected void doRunThreadLoop() {
for(;;) {
List<QueueEventData> tmpToProcess = null;
synchronized(lock) {
if (currThreadStatus == ThreadStatus.running_interrupting) {
currThreadStatus = ThreadStatus.stopped;
return; // stop main loop!
}
if (!queue.isEmpty()) {
tmpToProcess = new ArrayList<QueueEventData>(queue.size());
for(; !queue.isEmpty(); ) {
tmpToProcess.add(queue.poll());
}
} else {
lock.notify(); // wake up thread for waitEmptyQueue()...
try {
lock.wait();
} catch (InterruptedException e) {
currThreadStatus = ThreadStatus.stopped;
return; // stop main loop!
}
if (!queue.isEmpty()) {
tmpToProcess = new ArrayList<QueueEventData>(queue.size());
for(; !queue.isEmpty(); ) {
tmpToProcess.add(queue.poll());
}
} else {
continue; // wait more..
}
}
}
for(QueueEventData tmp : tmpToProcess) {
target.addEvent(tmp.event, tmp.objData, tmp.callback);
}
}
}
// override java.lang.Object
// -------------------------------------------------------------------------
@Override
public String toString() {
return "AsyncQueueEventStoreWriter["
+ "currThreadStatus=" + currThreadStatus
+ ", queue length=" + queue.size()
+ "]";
}
}