com.kolibrifx.plovercrest.server.internal.ThreadedEventDispatcher Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of plovercrest-server Show documentation
Show all versions of plovercrest-server Show documentation
Plovercrest server library.
The newest version!
/*
* Copyright (c) 2010-2017, KolibriFX AS. Licensed under the Apache License, version 2.0.
*/
package com.kolibrifx.plovercrest.server.internal;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import org.apache.log4j.Logger;
/**
* Event dispatcher that runs on a separate thread.
*
* The thread is started automatically (from the constructor). It can be stopped either by
* interrupting the thread or calling shutDown(). The latter makes sure that all queued events are
* dispatched before the thread stops.
*
* For performance reasons, events may be dropped if several events of the same type (listener,
* type, table) is queued. This is currently possible because of how FoldListener reacts to events,
* so think twice before changing this.
*/
public class ThreadedEventDispatcher extends Thread implements EventDispatcher {
private static final Logger log = Logger.getLogger(ThreadedEventDispatcher.class);
private static class QueuedEvent {
final TableListener listener;
final TableEvent event;
QueuedEvent(final TableListener listener, final TableEvent event) {
this.listener = listener;
this.event = event;
}
@Override
public boolean equals(final Object other) {
if (!(other instanceof QueuedEvent)) {
return false;
}
if (this == other) {
return true;
}
final QueuedEvent otherEvent = (QueuedEvent) other;
return listener == otherEvent.listener && event.getType() == otherEvent.event.getType()
&& event.getTableName().equals(otherEvent.event.getTableName());
}
@Override
public int hashCode() {
if (listener == null) {
return super.hashCode();
}
return listener.hashCode();
}
}
private final LinkedHashSet eventSet;
private final QueuedEvent shutDownEvent;
private boolean isThreadWaiting;
public ThreadedEventDispatcher() {
eventSet = new LinkedHashSet();
shutDownEvent = new QueuedEvent(null, null);
isThreadWaiting = false;
// set as daemon to prevent thread from keeping JVM alive, maybe find a
// better way?
setDaemon(true);
setName("Plovercrest threaded event dispatcher");
start();
}
private void dispatchEvent(final QueuedEvent e) {
synchronized (eventSet) {
eventSet.add(e);
// for performance reasons, only call notify if the thread is
// waiting
if (isThreadWaiting) {
eventSet.notify();
}
}
}
@Override
public void dispatch(final TableListener listener, final TableEvent event) {
dispatchEvent(new QueuedEvent(listener, event));
}
@Override
public void run() {
final ArrayList uniqueEvents = new ArrayList();
boolean shouldQuit = false;
try {
while (!shouldQuit) {
synchronized (eventSet) {
if (eventSet.isEmpty()) {
isThreadWaiting = true;
eventSet.wait();
isThreadWaiting = false;
}
// drain queued events, then release the lock
uniqueEvents.addAll(eventSet);
eventSet.clear();
}
for (final QueuedEvent qe : uniqueEvents) {
if (qe == shutDownEvent) {
shouldQuit = true;
} else {
try {
qe.listener.handleTableEvent(qe.event);
} catch (final Exception e) {
log.error(String.format("Exception while processing %s for table %s", qe.event.getType(),
qe.event.getTableName()), e);
}
}
}
uniqueEvents.clear();
}
log.trace("Dispatcher thread shutting down");
} catch (final InterruptedException e) {
log.info("Dispatcher thread interrupted");
}
}
/**
* Clean shut down. Joins the thread, making sure all currently queued events are processed
* first.
*/
@Override
public void shutDown() {
dispatchEvent(shutDownEvent);
try {
join();
} catch (final InterruptedException e) {
log.error("Interrupted during join", e);
}
}
}