package tc.oc.commons.core.event;
import java.lang.reflect.Field;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.SubscriberExceptionHandler;
public class ReentrantEventBus extends EventBus {
private final ThreadLocal<Boolean> isDispatching;
public ReentrantEventBus() {
this.isDispatching = getIsDispatching();
}
public ReentrantEventBus(String identifier) {
super(identifier);
this.isDispatching = getIsDispatching();
}
public ReentrantEventBus(SubscriberExceptionHandler subscriberExceptionHandler) {
super(subscriberExceptionHandler);
this.isDispatching = getIsDispatching();
}
private ThreadLocal<Boolean> getIsDispatching() {
try {
final Field field = EventBus.class.getDeclaredField("isDispatching");
field.setAccessible(true);
return (ThreadLocal<Boolean>) field.get(this);
} catch(IllegalAccessException | NoSuchFieldException e) {
throw new IllegalStateException();
}
}
/**
* HACK: Trick the superclass into allowing nested events
*
* EventBus is actually more of an event queue. If ANY event is posted inside
* another event, it will be queued and dispatched only after the outer
* event has finished dispatching. If your event posting code can't be sure
* whether not it is an outer-most event, then it has to handle both the
* possibility that handlers are called before the post returns AND the
* possibility that they are queued and called at some arbitrary later time.
* That seems pretty ridiculous to me.
*
* In any case, it is not the type of event bus that we need, and this was
* discovered too late. So, we use reflection here to reset the thread-local
* used to detect reentrancy before every event post, so the handlers are
* always called immediately.
*/
@Override
public void post(Object event) {
isDispatching.set(false);
super.post(event);
}
}