|
|
@ -1,12 +1,13 @@ |
|
|
|
package mightypork.util.control.eventbus; |
|
|
|
package mightypork.util.control.eventbus; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import java.lang.reflect.ParameterizedType; |
|
|
|
|
|
|
|
import java.lang.reflect.Type; |
|
|
|
import java.util.Collection; |
|
|
|
import java.util.Collection; |
|
|
|
import java.util.concurrent.DelayQueue; |
|
|
|
import java.util.concurrent.DelayQueue; |
|
|
|
import java.util.concurrent.Delayed; |
|
|
|
import java.util.concurrent.Delayed; |
|
|
|
import java.util.concurrent.TimeUnit; |
|
|
|
import java.util.concurrent.TimeUnit; |
|
|
|
|
|
|
|
|
|
|
|
import mightypork.util.annotations.FactoryMethod; |
|
|
|
|
|
|
|
import mightypork.util.control.Destroyable; |
|
|
|
import mightypork.util.control.Destroyable; |
|
|
|
import mightypork.util.control.eventbus.clients.DelegatingClient; |
|
|
|
import mightypork.util.control.eventbus.clients.DelegatingClient; |
|
|
|
import mightypork.util.control.eventbus.events.Event; |
|
|
|
import mightypork.util.control.eventbus.events.Event; |
|
|
@ -18,107 +19,150 @@ import mightypork.util.logging.Log; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* An event bus, accommodating multiple {@link EventChannel}s. |
|
|
|
* An event bus, accommodating multiple EventChannels.<br> |
|
|
|
|
|
|
|
* Channel will be created when an event of type is first encountered. |
|
|
|
* |
|
|
|
* |
|
|
|
* @author MightyPork |
|
|
|
* @author MightyPork |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
final public class EventBus implements Destroyable { |
|
|
|
final public class EventBus implements Destroyable { |
|
|
|
|
|
|
|
|
|
|
|
/** Message channels */ |
|
|
|
/** |
|
|
|
private final BufferedHashSet<EventChannel<?, ?>> channels = new BufferedHashSet<>(); |
|
|
|
* Queued event holder |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private class DelayQueueEntry implements Delayed { |
|
|
|
|
|
|
|
|
|
|
|
/** Registered clients */ |
|
|
|
private final long due; |
|
|
|
private final BufferedHashSet<Object> clients = new BufferedHashSet<>(); |
|
|
|
private final Event<?> evt; |
|
|
|
|
|
|
|
|
|
|
|
/** Messages queued for delivery */ |
|
|
|
|
|
|
|
private final DelayQueue<DelayQueueEntry> sendQueue = new DelayQueue<>(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Queue polling thread */ |
|
|
|
public DelayQueueEntry(double seconds, Event<?> event) { |
|
|
|
private final QueuePollingThread busThread; |
|
|
|
super(); |
|
|
|
|
|
|
|
this.due = System.currentTimeMillis() + (long) (seconds * 1000); |
|
|
|
|
|
|
|
this.evt = event; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** Whether the bus was destroyed */ |
|
|
|
|
|
|
|
private boolean dead = false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Log detailed messages (debug) */ |
|
|
|
@Override |
|
|
|
public boolean detailedLogging = false; |
|
|
|
public int compareTo(Delayed o) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return Long.valueOf(getDelay(TimeUnit.MILLISECONDS)).compareTo(o.getDelay(TimeUnit.MILLISECONDS)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@Override |
|
|
|
* Make a new bus and start it's queue thread. |
|
|
|
public long getDelay(TimeUnit unit) |
|
|
|
*/ |
|
|
|
{ |
|
|
|
public EventBus() { |
|
|
|
return unit.convert(due - System.currentTimeMillis(), TimeUnit.MILLISECONDS); |
|
|
|
busThread = new QueuePollingThread(); |
|
|
|
|
|
|
|
busThread.setDaemon(true); |
|
|
|
|
|
|
|
busThread.start(); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private boolean shallLog(Event<?> event) |
|
|
|
public Event<?> getEvent() |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (!detailedLogging) return false; |
|
|
|
return evt; |
|
|
|
if (event.getClass().isAnnotationPresent(UnloggedEvent.class)) return false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Add a {@link EventChannel} to this bus.<br> |
|
|
|
* Thread handling queued events |
|
|
|
* If a channel of matching types is already added, it is returned instead. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param channel channel to be added |
|
|
|
|
|
|
|
* @return the channel that's now in the bus |
|
|
|
|
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public EventChannel<?, ?> addChannel(EventChannel<?, ?> channel) |
|
|
|
private class QueuePollingThread extends Thread { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public volatile boolean stopped = false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public QueuePollingThread() { |
|
|
|
|
|
|
|
super("Queue Polling Thread"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public void run() |
|
|
|
{ |
|
|
|
{ |
|
|
|
assertLive(); |
|
|
|
DelayQueueEntry evt; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while (!stopped) { |
|
|
|
|
|
|
|
evt = null; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
evt = sendQueue.take(); |
|
|
|
|
|
|
|
} catch (final InterruptedException ignored) { |
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// if the channel already exists, return this instance instead.
|
|
|
|
if (evt != null) { |
|
|
|
for (final EventChannel<?, ?> ch : channels) { |
|
|
|
dispatch(evt.getEvent()); |
|
|
|
if (ch.equals(channel)) { |
|
|
|
} |
|
|
|
Log.w("<bus> Channel of type " + Log.str(channel) + " already registered."); |
|
|
|
|
|
|
|
return ch; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
channels.add(channel); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return channel; |
|
|
|
static final String logMark = "<BUS> "; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static Class<?> getEventListenerClass(Event<?> event) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// BEHOLD, MAGIC!
|
|
|
|
|
|
|
|
final Type[] interfaces = event.getClass().getGenericInterfaces(); |
|
|
|
|
|
|
|
for (final Type interf : interfaces) { |
|
|
|
|
|
|
|
if (interf instanceof ParameterizedType) { |
|
|
|
|
|
|
|
if (((ParameterizedType) interf).getRawType() == Event.class) { |
|
|
|
|
|
|
|
final Type[] types = ((ParameterizedType) interf).getActualTypeArguments(); |
|
|
|
|
|
|
|
for (final Type genericType : types) { |
|
|
|
|
|
|
|
return (Class<?>) genericType; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
throw new RuntimeException("Could not detect event listener type."); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Log detailed messages (debug) */ |
|
|
|
|
|
|
|
public boolean detailedLogging = false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Queue polling thread */ |
|
|
|
|
|
|
|
private final QueuePollingThread busThread; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Registered clients */ |
|
|
|
|
|
|
|
private final BufferedHashSet<Object> clients = new BufferedHashSet<>(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Whether the bus was destroyed */ |
|
|
|
|
|
|
|
private boolean dead = false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Message channels */ |
|
|
|
|
|
|
|
private final BufferedHashSet<EventChannel<?, ?>> channels = new BufferedHashSet<>(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Messages queued for delivery */ |
|
|
|
|
|
|
|
private final DelayQueue<DelayQueueEntry> sendQueue = new DelayQueue<>(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Make & connect a channel for given event and client type. |
|
|
|
* Make a new bus and start it's queue thread. |
|
|
|
* |
|
|
|
|
|
|
|
* @param eventClass event type |
|
|
|
|
|
|
|
* @param clientClass client type |
|
|
|
|
|
|
|
* @return the created channel instance |
|
|
|
|
|
|
|
*/ |
|
|
|
*/ |
|
|
|
@FactoryMethod |
|
|
|
public EventBus() { |
|
|
|
public <F_EVENT extends Event<F_CLIENT>, F_CLIENT> EventChannel<?, ?> addChannel(Class<F_EVENT> eventClass, Class<F_CLIENT> clientClass) |
|
|
|
busThread = new QueuePollingThread(); |
|
|
|
{ |
|
|
|
busThread.setDaemon(true); |
|
|
|
assertLive(); |
|
|
|
busThread.start(); |
|
|
|
|
|
|
|
|
|
|
|
final EventChannel<F_EVENT, F_CLIENT> channel = EventChannel.create(eventClass, clientClass); |
|
|
|
|
|
|
|
return addChannel(channel); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Remove a {@link EventChannel} from this bus |
|
|
|
* Halt bus thread and reject any future events. |
|
|
|
* |
|
|
|
|
|
|
|
* @param channel true if channel was removed |
|
|
|
|
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public void removeChannel(EventChannel<?, ?> channel) |
|
|
|
@Override |
|
|
|
|
|
|
|
public void destroy() |
|
|
|
{ |
|
|
|
{ |
|
|
|
assertLive(); |
|
|
|
assertLive(); |
|
|
|
|
|
|
|
|
|
|
|
channels.remove(channel); |
|
|
|
busThread.stopped = true; |
|
|
|
|
|
|
|
dead = true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Send based on annotation.clients |
|
|
|
* Send based on annotation |
|
|
|
* |
|
|
|
* |
|
|
|
* @param event event |
|
|
|
* @param event event |
|
|
|
*/ |
|
|
|
*/ |
|
|
@ -166,7 +210,9 @@ final public class EventBus implements Destroyable { |
|
|
|
|
|
|
|
|
|
|
|
final DelayQueueEntry dm = new DelayQueueEntry(delay, event); |
|
|
|
final DelayQueueEntry dm = new DelayQueueEntry(delay, event); |
|
|
|
|
|
|
|
|
|
|
|
if (shallLog(event)) Log.f3("<bus> Qu " + Log.str(event) + ", t = +" + delay + "s"); |
|
|
|
if (shallLog(event)) { |
|
|
|
|
|
|
|
Log.f3(logMark + "Qu [" + Log.str(event) + "]" + (delay == 0 ? "" : (", delay: " + delay + "s"))); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
sendQueue.add(dm); |
|
|
|
sendQueue.add(dm); |
|
|
|
} |
|
|
|
} |
|
|
@ -183,7 +229,7 @@ final public class EventBus implements Destroyable { |
|
|
|
{ |
|
|
|
{ |
|
|
|
assertLive(); |
|
|
|
assertLive(); |
|
|
|
|
|
|
|
|
|
|
|
if (shallLog(event)) Log.f3("<bus> Di " + Log.str(event)); |
|
|
|
if (shallLog(event)) Log.f3(logMark + "Di [" + Log.str(event) + "]"); |
|
|
|
|
|
|
|
|
|
|
|
dispatch(event); |
|
|
|
dispatch(event); |
|
|
|
} |
|
|
|
} |
|
|
@ -193,61 +239,12 @@ final public class EventBus implements Destroyable { |
|
|
|
{ |
|
|
|
{ |
|
|
|
assertLive(); |
|
|
|
assertLive(); |
|
|
|
|
|
|
|
|
|
|
|
if (shallLog(event)) Log.f3("<bus> Di sub " + Log.str(event)); |
|
|
|
if (shallLog(event)) Log.f3(logMark + "Di->sub [" + Log.str(event) + "]"); |
|
|
|
|
|
|
|
|
|
|
|
doDispatch(delegatingClient.getChildClients(), event); |
|
|
|
doDispatch(delegatingClient.getChildClients(), event); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Send immediately.<br> |
|
|
|
|
|
|
|
* Should be used for real-time events that require immediate response, such |
|
|
|
|
|
|
|
* as timing events. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param event event |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private void dispatch(Event<?> event) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
assertLive(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
channels.setBuffering(true); |
|
|
|
|
|
|
|
clients.setBuffering(true); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
doDispatch(clients, event); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
channels.setBuffering(false); |
|
|
|
|
|
|
|
clients.setBuffering(false); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Send to a set of clients |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param clients clients |
|
|
|
|
|
|
|
* @param event event |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private void doDispatch(Collection<Object> clients, Event<?> event) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
boolean sent = false; |
|
|
|
|
|
|
|
boolean accepted = false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
final boolean singular = event.getClass().isAnnotationPresent(SingleReceiverEvent.class); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (final EventChannel<?, ?> b : channels) { |
|
|
|
|
|
|
|
if (b.canBroadcast(event)) { |
|
|
|
|
|
|
|
accepted = true; |
|
|
|
|
|
|
|
sent |= b.broadcast(event, clients); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (sent && singular) break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!accepted) Log.e("<bus> Not accepted by any channel: " + Log.str(event)); |
|
|
|
|
|
|
|
if (!sent && shallLog(event)) Log.w("<bus> Not delivered: " + Log.str(event)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Connect a client to the bus. The client will be connected to all current |
|
|
|
* Connect a client to the bus. The client will be connected to all current |
|
|
|
* and future channels, until removed from the bus. |
|
|
|
* and future channels, until removed from the bus. |
|
|
@ -262,7 +259,7 @@ final public class EventBus implements Destroyable { |
|
|
|
|
|
|
|
|
|
|
|
clients.add(client); |
|
|
|
clients.add(client); |
|
|
|
|
|
|
|
|
|
|
|
if (detailedLogging) Log.f3("<bus> Client joined: " + Log.str(client)); |
|
|
|
if (detailedLogging) Log.f3(logMark + "Client joined: " + Log.str(client)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -277,120 +274,114 @@ final public class EventBus implements Destroyable { |
|
|
|
|
|
|
|
|
|
|
|
clients.remove(client); |
|
|
|
clients.remove(client); |
|
|
|
|
|
|
|
|
|
|
|
if (detailedLogging) Log.f3("<bus> Client left: " + Log.str(client)); |
|
|
|
if (detailedLogging) Log.f3(logMark + "Client left: " + Log.str(client)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
* Check if client can be accepted by any channel |
|
|
|
private boolean addChannelForEvent(Event<?> event) |
|
|
|
* |
|
|
|
|
|
|
|
* @param client tested client |
|
|
|
|
|
|
|
* @return would be accepted |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public boolean isClientValid(Object client) |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
assertLive(); |
|
|
|
try { |
|
|
|
|
|
|
|
if (detailedLogging) { |
|
|
|
if (client == null) return false; |
|
|
|
Log.f2(logMark + "Setting up channel for new event type: " + Log.str(event.getClass())); |
|
|
|
|
|
|
|
|
|
|
|
for (final EventChannel<?, ?> ch : channels) { |
|
|
|
|
|
|
|
if (ch.isClientValid(client)) { |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private class DelayQueueEntry implements Delayed { |
|
|
|
final Class<?> listener = getEventListenerClass(event); |
|
|
|
|
|
|
|
final EventChannel<?, ?> ch = EventChannel.create(event.getClass(), listener); |
|
|
|
|
|
|
|
|
|
|
|
private final long due; |
|
|
|
if (ch.canBroadcast(event)) { |
|
|
|
private Event<?> evt = null; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
channels.add(ch); |
|
|
|
|
|
|
|
//channels.flush();
|
|
|
|
|
|
|
|
|
|
|
|
public DelayQueueEntry(double seconds, Event<?> event) { |
|
|
|
if (detailedLogging) { |
|
|
|
super(); |
|
|
|
Log.f2("<bus> Created new channel: " + Log.str(event.getClass()) + " -> " + Log.str(listener)); |
|
|
|
this.due = System.currentTimeMillis() + (long) (seconds * 1000); |
|
|
|
|
|
|
|
this.evt = event; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
} else { |
|
|
|
public int compareTo(Delayed o) |
|
|
|
Log.w(logMark + "Could not create channel for event " + Log.str(event.getClass())); |
|
|
|
{ |
|
|
|
|
|
|
|
return Long.valueOf(getDelay(TimeUnit.MILLISECONDS)).compareTo(o.getDelay(TimeUnit.MILLISECONDS)); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} catch (final Throwable t) { |
|
|
|
|
|
|
|
Log.w(logMark + "Error while trying to add channel for event.", t); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
return false; |
|
|
|
public long getDelay(TimeUnit unit) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return unit.convert(due - System.currentTimeMillis(), TimeUnit.MILLISECONDS); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public Event<?> getEvent() |
|
|
|
/** |
|
|
|
|
|
|
|
* Make sure the bus is not destroyed. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @throws IllegalStateException if the bus is dead. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private void assertLive() throws IllegalStateException |
|
|
|
{ |
|
|
|
{ |
|
|
|
return evt; |
|
|
|
if (dead) throw new IllegalStateException("EventBus is dead."); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private class QueuePollingThread extends Thread { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public boolean stopped = false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Send immediately.<br> |
|
|
|
|
|
|
|
* Should be used for real-time events that require immediate response, such |
|
|
|
|
|
|
|
* as timing events. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param event event |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private synchronized void dispatch(Event<?> event) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
assertLive(); |
|
|
|
|
|
|
|
|
|
|
|
public QueuePollingThread() { |
|
|
|
clients.setBuffering(true); |
|
|
|
super("Queue Polling Thread"); |
|
|
|
doDispatch(clients, event); |
|
|
|
|
|
|
|
clients.setBuffering(false); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
/** |
|
|
|
public void run() |
|
|
|
* Send to a set of clients |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param clients clients |
|
|
|
|
|
|
|
* @param event event |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private synchronized void doDispatch(Collection<Object> clients, Event<?> event) |
|
|
|
{ |
|
|
|
{ |
|
|
|
DelayQueueEntry evt; |
|
|
|
boolean sent = false; |
|
|
|
|
|
|
|
boolean accepted = false; |
|
|
|
|
|
|
|
|
|
|
|
while (!stopped) { |
|
|
|
final boolean singular = event.getClass().isAnnotationPresent(SingleReceiverEvent.class); |
|
|
|
evt = null; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
for (int i = 0; i < 2; i++) { // two tries.
|
|
|
|
evt = sendQueue.take(); |
|
|
|
|
|
|
|
} catch (final InterruptedException ignored) { |
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (evt != null) { |
|
|
|
channels.setBuffering(true); |
|
|
|
dispatch(evt.getEvent()); |
|
|
|
for (final EventChannel<?, ?> b : channels) { |
|
|
|
} |
|
|
|
if (b.canBroadcast(event)) { |
|
|
|
} |
|
|
|
accepted = true; |
|
|
|
|
|
|
|
sent |= b.broadcast(event, clients); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (sent && singular) break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
channels.setBuffering(false); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!accepted) if (addChannelForEvent(event)) continue; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
break; |
|
|
|
* Halt bus thread and reject any future events. |
|
|
|
} |
|
|
|
*/ |
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public void destroy() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
assertLive(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
busThread.stopped = true; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
dead = true; |
|
|
|
if (!accepted) Log.e(logMark + "Not accepted by any channel: " + Log.str(event)); |
|
|
|
|
|
|
|
if (!sent && shallLog(event)) Log.w(logMark + "Not delivered: " + Log.str(event)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
private boolean shallLog(Event<?> event) |
|
|
|
* Make sure the bus is not destroyed. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @throws IllegalStateException if the bus is dead. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private void assertLive() throws IllegalStateException |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
if (dead) throw new IllegalStateException("EventBus is dead."); |
|
|
|
if (!detailedLogging) return false; |
|
|
|
|
|
|
|
if (event.getClass().isAnnotationPresent(UnloggedEvent.class)) return false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|