package com.flexpoker.framework.event.subscriber;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.PriorityBlockingQueue;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.flexpoker.framework.event.Event;
import com.flexpoker.framework.event.EventHandler;
@Scope("prototype")
@Component
public class InMemoryThreadSafeEventSubscriberHelper<T extends Event> {
private final Map<UUID, PriorityBlockingQueue<T>> listOfEventsNeededToProcess;
private final Map<UUID, Integer> nextExpectedEventVersion;
private Map<Class<T>, EventHandler<T>> handlerMap;
public InMemoryThreadSafeEventSubscriberHelper() {
listOfEventsNeededToProcess = new HashMap<>();
nextExpectedEventVersion = new HashMap<>();
}
public void receive(T event) {
synchronized (this) {
listOfEventsNeededToProcess.putIfAbsent(event.getAggregateId(), new PriorityBlockingQueue<>(10, Comparator.comparingInt(Event::getVersion)));
nextExpectedEventVersion.putIfAbsent(event.getAggregateId(), Integer.valueOf(1));
if (isExpectedEvent(event)) {
handleEventAndRunAnyOthers(event);
} else {
listOfEventsNeededToProcess.get(event.getAggregateId()).add(event);
}
}
}
private void handleEventAndRunAnyOthers(T event) {
handleEvent(event);
removeEventFromUnhandleList(event);
incrementNextEventVersion(event);
handleAnyPreviouslyUnhandledEvents(event);
}
private boolean isExpectedEvent(Event event) {
int expectedEventVersion = nextExpectedEventVersion.get(event.getAggregateId())
.intValue();
return expectedEventVersion == event.getVersion();
}
private void handleEvent(T event) {
handlerMap.get(event.getClass()).handle(event);
}
private void removeEventFromUnhandleList(Event event) {
listOfEventsNeededToProcess.get(event.getAggregateId()).remove(event);
}
private void incrementNextEventVersion(Event event) {
nextExpectedEventVersion.compute(event.getAggregateId(),
(eventId, eventVersion) -> eventVersion + 1);
}
private void handleAnyPreviouslyUnhandledEvents(T event) {
T earliestUnrunTableEvent = listOfEventsNeededToProcess.get(event.getAggregateId()).peek();
if (earliestUnrunTableEvent != null && isExpectedEvent(earliestUnrunTableEvent)) {
handleEventAndRunAnyOthers(earliestUnrunTableEvent);
}
}
public void setHandlerMap(Map<Class<T>, EventHandler<T>> handlerMap) {
this.handlerMap = handlerMap;
}
}