package com.google.code.joto.eventrecorder.impl;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.code.joto.eventrecorder.RecordEventData;
import com.google.code.joto.eventrecorder.RecordEventListener;
import com.google.code.joto.eventrecorder.RecordEventStore;
import com.google.code.joto.eventrecorder.RecordEventStoreChange;
import com.google.code.joto.eventrecorder.RecordEventStoreChange.AddRecordEventStoreEvent;
import com.google.code.joto.eventrecorder.RecordEventStoreChange.TruncateRecordEventStoreEvent;
import com.google.code.joto.eventrecorder.RecordEventSummary;
import com.google.code.joto.eventrecorder.writer.AbstractRecordEventWriter;
import com.google.code.joto.eventrecorder.writer.AsyncQueueRecordEventWriter;
import com.google.code.joto.eventrecorder.writer.RecordEventWriter;
import com.google.code.joto.eventrecorder.writer.RecordEventWriterCallback;
/**
* abstract helper base-class for RecordEventStore implementations
*/
public abstract class AbstractRecordEventStore implements RecordEventStore {
private static Logger log = LoggerFactory.getLogger(AbstractRecordEventStore.class);
protected boolean canRead;
protected boolean canWriteAppend;
private List<RecordEventListener> listeners = new ArrayList<RecordEventListener>();
/**
* first id of event available, i.e. not truncated
*/
private int firstEventId = 1;
/**
* exclusive last event id... also serves as idGenerator!
*/
private int lastEventId = 1; // exclusive
private RecordEventWriter eventWriterAdapter;
private AsyncQueueRecordEventWriter asyncEventWriter;
// ------------------------------------------------------------------------
protected AbstractRecordEventStore() {
eventWriterAdapter = new RecordEventWriterAdapter(this);
asyncEventWriter = new AsyncQueueRecordEventWriter(eventWriterAdapter);
}
// ------------------------------------------------------------------------
@Override
public void open(String mode) {
setMode(mode);
}
protected void setMode(String mode) {
if (mode.equals("ra")) {
canRead = true;
canWriteAppend = true;
} else if (mode.equals("rw")) {
canRead = true;
canWriteAppend = true;
} else if (mode.equals("r")) {
canRead = true;
canWriteAppend = false;
} else {
throw new IllegalArgumentException("invalid mode '" + mode + "', expecting one of { ra, rw, r }");
}
if (canWriteAppend) {
asyncEventWriter.startQueue();
}
}
public boolean getCanRead() {
return canRead;
}
public boolean getCanWriteAppend() {
return canWriteAppend;
}
@Override
public void close() {
if (canWriteAppend) {
asyncEventWriter.stopQueue();
}
canRead = false;
canWriteAppend = false;
}
@Override
public void flush() {
// do nothing
}
@Override
public final int getFirstEventId() {
return firstEventId;
}
@Override
public final int getLastEventId() {
return lastEventId;
}
@Override
public final int getEventsCount() {
return lastEventId - firstEventId;
}
@Override
public final int getFirstEventIdWithMaxCount(int maxCount) {
int count = lastEventId - firstEventId;
if (maxCount != -1 && count > maxCount) {
count = maxCount;
}
return lastEventId - count;
}
protected void initSetFirstEventId(int p) {
this.firstEventId = p;
}
protected void setLastEventId(int p) {
this.lastEventId = p;
}
protected TruncateRecordEventStoreEvent onTruncateSetFirstEventId(int p, List<RecordEventSummary> optTruncatedEvents) {
int truncateFromEventId = firstEventId;
this.firstEventId = p;
// TODO sanity check if optTruncatedEvents is given...
return new TruncateRecordEventStoreEvent(truncateFromEventId, firstEventId, optTruncatedEvents);
}
public synchronized void addRecordEventListener(RecordEventListener p) {
List<RecordEventListener> tmp = new ArrayList<RecordEventListener>(listeners);
tmp.add(p);
this.listeners = tmp;
}
public synchronized void removeRecordEventListener(RecordEventListener p) {
List<RecordEventListener> tmp = new ArrayList<RecordEventListener>(listeners);
tmp.remove(p);
this.listeners = tmp;
}
@Override
public synchronized void getEventsAndAddEventListener(
int fromEventId,
RecordEventListener listener) {
// 1) fire replayed events
List<RecordEventStoreChange> replayStoreEvts = new ArrayList<RecordEventStoreChange>();
List<RecordEventSummary> events = getEvents(fromEventId, -1);
for(RecordEventSummary event : events) {
RecordEventStoreChange storeEvt = new AddRecordEventStoreEvent(event);
replayStoreEvts.add(storeEvt);
}
listener.onEvents(replayStoreEvts);
// 2) add listener
addRecordEventListener(listener);
}
public synchronized RecordEventData addEvent(RecordEventSummary event, Serializable objData) {
RecordEventData eventData = doAddEvent(event, objData);
fireStoreEvent(new AddRecordEventStoreEvent(eventData));
return eventData;
}
/** implements RecordEventStore */
@Override
public RecordEventWriter getEventWriter() {
return eventWriterAdapter;
}
/** implements RecordEventStore */
@Override
public RecordEventWriter getAsyncEventWriter() {
return asyncEventWriter;
}
abstract RecordEventData doAddEvent(RecordEventSummary eventInfo, Serializable objData);
protected static List<RecordEventSummary> eventDataListToEventHandleList(List<RecordEventData> eventDataList) {
List<RecordEventSummary> res = new ArrayList<RecordEventSummary>();
for(RecordEventData eventData : eventDataList) {
res.add(eventData.getEventSummary());
}
return res;
}
protected RecordEventData createNewEventData(RecordEventSummary eventInfo, Object objData) {
int newEventId = lastEventId++;
RecordEventSummary event = new RecordEventSummary(newEventId, eventInfo);
RecordEventData eventData = new RecordEventData(event, objData);
return eventData;
}
protected void fireStoreEvent(RecordEventStoreChange event) {
List<RecordEventListener> listenersCopy = listeners;
for(RecordEventListener elt : listenersCopy) {
try {
elt.onEvent(event);
} catch(Exception ex) {
log.warn("Failed to fire event to listener ... ignore, no rethrow!", ex);
}
}
}
protected void fireStoreEvents(List<RecordEventStoreChange> events) {
List<RecordEventListener> listenersCopy = listeners;
for(RecordEventListener elt : listenersCopy) {
try {
elt.onEvents(events);
} catch(Exception ex) {
log.warn("Failed to fire event to listener ... ignore, no rethrow!", ex);
}
}
}
/**
* adapter class for RecordEventStore -> RecordEventWriter
*
* for writing event using RecordEventWriter api into eventstore using SPI
*/
private static class RecordEventWriterAdapter extends AbstractRecordEventWriter {
private RecordEventStore eventStore;
// -------------------------------------------------------------------------
public RecordEventWriterAdapter(RecordEventStore eventStore) {
this.eventStore = eventStore;
}
// -------------------------------------------------------------------------
public void addEvent(RecordEventSummary info, Serializable objData,
RecordEventWriterCallback callback) {
RecordEventData eventData = eventStore.addEvent(info, objData);
if (callback != null) {
callback.onStore(eventData);
}
}
}
}