}
@Override
public <C> void addListener(Object listener, KeyValueFilter<? super K, ? super V> filter,
Converter<? super K, ? super V, C> converter, ClassLoader classLoader) {
Listener l = testListenerClassValidity(listener.getClass());
UUID generatedId = UUID.randomUUID();
CacheInvocationBuilder builder = new CacheInvocationBuilder();
CacheMode cacheMode = config.clustering().cacheMode();
builder.setClustered(l.clustered()).setOnlyPrimary(l.clustered() ? (cacheMode.isDistributed() ? true : false) : l.primaryOnly()).setFilter(filter).setConverter(converter)
.setIdentifier(generatedId).setIncludeCurrentState(l.includeCurrentState()).setClassLoader(classLoader);
boolean foundMethods = validateAndAddListenerInvocation(listener, builder);
if (foundMethods && l.clustered()) {
if (cacheMode.isInvalidation()) {
throw new UnsupportedOperationException("Cluster listeners cannot be used with Invalidation Caches!");
} else if (cacheMode.isDistributed()) {
clusterListenerIDs.put(listener, generatedId);
EmbeddedCacheManager manager = cache.getCacheManager();
Address ourAddress = manager.getAddress();
List<Address> members = manager.getMembers();
// If we are the only member don't even worry about sending listeners
if (members != null && members.size() > 1) {
DistributedExecutionCompletionService decs = new DistributedExecutionCompletionService(distExecutorService);
if (log.isTraceEnabled()) {
log.tracef("Replicating cluster listener to other nodes %s for cluster listener with id %s",
members, generatedId);
}
Callable callable = new ClusterListenerReplicateCallable(generatedId, ourAddress, filter, converter);
for (Address member : members) {
if (!member.equals(ourAddress)) {
decs.submit(member, callable);
}
}
for (int i = 0; i < members.size() - 1; ++i) {
try {
decs.take().get();
} catch (InterruptedException e) {
throw new CacheListenerException(e);
} catch (ExecutionException e) {
throw new CacheListenerException(e);
}
}
int extraCount = 0;
// If anyone else joined since we sent these we have to send the listeners again, since they may have queried
// before the other nodes got the new listener
List<Address> membersAfter = manager.getMembers();
for (Address member : membersAfter) {
if (!members.contains(member) && !member.equals(ourAddress)) {
if (log.isTraceEnabled()) {
log.tracef("Found additional node %s that joined during replication of cluster listener with id %s",
member, generatedId);
}
extraCount++;
decs.submit(member, callable);
}
}
for (int i = 0; i < extraCount; ++i) {
try {
decs.take().get();
} catch (InterruptedException e) {
throw new CacheListenerException(e);
} catch (ExecutionException e) {
throw new CacheListenerException(e);
}
}
}
}
}
// If we have a segment listener handler, it means we have to do initial state
QueueingSegmentListener handler = segmentHandler.remove(generatedId);
if (handler != null) {
if (log.isTraceEnabled()) {
log.tracef("Listener %s requests initial state for cache", generatedId);
}
try (CloseableIterator<CacheEntry<K, C>> iterator = entryRetriever.retrieveEntries(filter, converter, null, handler)) {
while (iterator.hasNext()) {
CacheEntry<K, C> entry = iterator.next();
// Mark the key as processed and see if we had a concurrent update
Object value = handler.markKeyAsProcessing(entry.getKey());
if (value == BaseQueueingSegmentListener.REMOVED) {
// Don't process this value if we had a concurrent remove
continue;
}
raiseEventForInitialTransfer(generatedId, entry, l.clustered());
handler.notifiedKey(entry.getKey());
}
}
Set<CacheEntry> entries = handler.findCreatedEntries();
for (CacheEntry entry : entries) {
raiseEventForInitialTransfer(generatedId, entry, l.clustered());
}
if (log.isTraceEnabled()) {
log.tracef("Listener %s initial state for cache completed", generatedId);
}