package org.infinispan.iteration;
/** * Adds the listener using the provided filter converter and class loader. The provided builder is * used to add additional configuration including (clustered, onlyPrimary & identifier) which can * be used after this method is completed to see what values were used in the addition of this * listener * * @param listener * @param filter * @param converter * @param classLoader * @param <C> * @return */ public <C> void addListener( Object listener, CacheEventFilter<? super K, ? super V> filter, CacheEventConverter<? super K, ? super V, C> converter, ClassLoader classLoader) { Listener l = testListenerClassValidity(listener.getClass()); UUID generatedId = UUID.randomUUID(); CacheMode cacheMode = config.clustering().cacheMode(); CacheInvocationBuilder builder = new CacheInvocationBuilder(); builder .setIncludeCurrentState(l.includeCurrentState()) .setClustered(l.clustered()) .setOnlyPrimary( l.clustered() ? (cacheMode.isDistributed() ? true : false) : l.primaryOnly()) .setFilter(filter) .setConverter(converter) .setIdentifier(generatedId) .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, l.sync()); 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 == null ? null : new CacheEventFilterAsKeyValueFilter(filter), converter == null ? null : new CacheEventConverterAsConverter(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, builder.isClustered()); handler.notifiedKey(entry.getKey()); } } Set<CacheEntry> entries = handler.findCreatedEntries(); for (CacheEntry entry : entries) { raiseEventForInitialTransfer(generatedId, entry, builder.isClustered()); } if (log.isTraceEnabled()) { log.tracef("Listener %s initial state for cache completed", generatedId); } handler.transferComplete(); } }