package pl.net.bluesoft.util.eventbus;
import pl.net.bluesoft.util.lang.Maps;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Logger;
/**
* A thread-safe observer pattern implementation. It does not handle errors.
*
* @author amichalak@bluesoft.net.pl
*/
public class ConcurrentEventBusManager implements EventBusManager {
private static final Logger logger = Logger.getLogger(ConcurrentEventBusManager.class.getName());
protected final Map<Class, Set<WeakReference<EventListener>>> listenerMap = new HashMap<Class, Set<WeakReference<EventListener>>>();
protected final ExecutorService executorService;
public ConcurrentEventBusManager() {
this.executorService = Executors.newCachedThreadPool();
}
public ConcurrentEventBusManager(ExecutorService executorService) {
this.executorService = executorService;
}
public void subscribe(Class cls, EventListener listener) {
Map<Class, Set<WeakReference<EventListener>>> map = getListenerMap();
synchronized (map) {
cleanListenerMap(map, cls, listener).add(new WeakReference(listener));
}
}
public void unsubscribe(Class cls, EventListener listener) {
Map<Class, Set<WeakReference<EventListener>>> map = getListenerMap();
synchronized (map) {
cleanListenerMap(map, cls, listener);
}
}
private Set<WeakReference<EventListener>> cleanListenerMap(Map<Class, Set<WeakReference<EventListener>>> map, Class cls, EventListener listener) {
Set<WeakReference<EventListener>> set = Maps.getSetFromMap(map, cls);
for (Iterator<WeakReference<EventListener>> it = set.iterator(); it.hasNext();) {
WeakReference<EventListener> ref = it.next();
if (ref == null || ref.get() == null || ref.get() == listener) {
it.remove();
}
}
return set;
}
public void publish(Object event) {
logger.info("Publishing event: " + event.getClass());
Map<Class, Set<WeakReference<EventListener>>> map = getListenerMap();
Class cls = event.getClass();
while (cls != null) {
synchronized (map) {
Set<WeakReference<EventListener>> set = Maps.getSetFromMap(map, cls);
for (Iterator<WeakReference<EventListener>> it = set.iterator(); it.hasNext(); ) {
WeakReference<EventListener> ref = it.next();
if (ref != null && ref.get() != null) {
EventListener listener = ref.get();
logger.info("Receiving event by listener: " + listener.getClass().getName());
listener.onEvent(event);
}
else {
it.remove();
}
}
}
cls = cls.getSuperclass();
}
}
public void post(Object event) {
logger.info("Concurrent event posted: " + event.getClass());
executorService.submit(getEventRunnable(event));
}
protected Map<Class, Set<WeakReference<EventListener>>> getListenerMap() {
return listenerMap;
}
protected Runnable getEventRunnable(final Object event) {
return new Runnable() {
@Override
public void run() {
publish(event);
}
};
}
public ExecutorService getExecutorService() {
return executorService;
}
}