/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.toolkit.modules.concurrency.internal;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import de.rcenvironment.toolkit.modules.concurrency.api.AsyncCallback;
import de.rcenvironment.toolkit.modules.concurrency.api.AsyncCallbackExceptionPolicy;
import de.rcenvironment.toolkit.modules.concurrency.api.AsyncOrderedCallbackManager;
import de.rcenvironment.toolkit.modules.concurrency.api.AsyncOrderedExecutionQueue;
/**
* Default {@link AsyncOrderedCallbackManager} implementation.
*
* @param <T> the listener class (usually an interface)
*
* @author Robert Mischke
*/
public class AsyncOrderedCallbackManagerImpl<T> implements AsyncOrderedCallbackManager<T> {
/**
* Wrapper class to queue callbacks per listener.
*
* @author Robert Mischke
*/
private final class InternalAsyncOrderedCallbackQueue extends AsyncOrderedExecutionQueueImpl {
private final T listener;
private InternalAsyncOrderedCallbackQueue(final T listener, final AsyncCallbackExceptionPolicy exceptionPolicy) {
super(exceptionPolicy, internalServiceHolder);
this.listener = listener;
}
private void enqueue(final AsyncCallback<T> callback) {
super.enqueue(new Runnable() {
@Override
public void run() {
callback.performCallback(listener);
// TODO improve fetching
internalServiceHolder.getStatisticsTrackerService()
.getCounterCategory(AsyncOrderedExecutionQueue.STATS_COUNTER_SHARED_CATEGORY_NAME).count(
"Asynchronous callback");
}
});
}
}
private final AsyncCallbackExceptionPolicy exceptionPolicy;
private final Map<T, InternalAsyncOrderedCallbackQueue> listenerMap = new HashMap<T, InternalAsyncOrderedCallbackQueue>();
private final Log log = LogFactory.getLog(getClass());
private ConcurrencyUtilsServiceHolder internalServiceHolder;
public AsyncOrderedCallbackManagerImpl(ConcurrencyUtilsServiceHolder internalServiceHolder,
AsyncCallbackExceptionPolicy exceptionPolicy) {
this(exceptionPolicy, internalServiceHolder);
}
public AsyncOrderedCallbackManagerImpl(AsyncCallbackExceptionPolicy exceptionPolicy,
ConcurrencyUtilsServiceHolder internalServiceHolder) {
this.internalServiceHolder = internalServiceHolder;
this.exceptionPolicy = exceptionPolicy;
// explicitly check for known policies
if (exceptionPolicy != AsyncCallbackExceptionPolicy.LOG_AND_PROCEED
&& exceptionPolicy != AsyncCallbackExceptionPolicy.LOG_AND_CANCEL_LISTENER) {
log.warn(exceptionPolicy + " policy not implemented yet");
}
}
/**
* Adds an asynchronous listener.
*
* @param listener the listener to add
*/
@Override
public void addListener(T listener) {
synchronized (listenerMap) {
// TODO check if already present?
listenerMap.put(listener, new InternalAsyncOrderedCallbackQueue(listener, exceptionPolicy));
}
}
/**
* Atomically adds an asynchronous listener and enqueues an asynchronous callback. The given callback is guaranteed to be the first one
* that the listener receives. This is useful to initialize listeners using the callback method without risk of race conditions.
*
* @param listener the listener to add
* @param callback the callback to execute asynchronously
*/
@Override
public void addListenerAndEnqueueCallback(T listener, AsyncCallback<T> callback) {
synchronized (listenerMap) {
InternalAsyncOrderedCallbackQueue singleListenerQueue =
new InternalAsyncOrderedCallbackQueue(listener, exceptionPolicy);
listenerMap.put(listener, singleListenerQueue);
singleListenerQueue.enqueue(callback);
}
}
/**
* Enqueues a callback to send asynchronously to all current listeners.
*
* @param callback the callback to execute asynchronously
*/
@Override
public void enqueueCallback(AsyncCallback<T> callback) {
// TODO lock contention could probably be reduced; keep it simple for now - misc_ro
synchronized (listenerMap) {
for (InternalAsyncOrderedCallbackQueue wrappedListener : listenerMap.values()) {
wrappedListener.enqueue(callback);
}
}
}
/**
* Removes an asynchronous listener.
*
* @param listener the listener to remove
*/
@Override
public void removeListener(T listener) {
// TODO review: cancel already-enqueued callbacks?
synchronized (listenerMap) {
listenerMap.remove(listener);
}
}
}