All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.infinispan.client.hotrod.counter.impl.NotificationManager Maven / Gradle / Ivy

The newest version!
package org.infinispan.client.hotrod.counter.impl;

import static org.infinispan.client.hotrod.impl.Util.await;

import java.net.SocketAddress;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.function.Consumer;

import org.infinispan.client.hotrod.counter.operation.AddListenerOperation;
import org.infinispan.client.hotrod.counter.operation.RemoveListenerOperation;
import org.infinispan.client.hotrod.event.impl.ClientListenerNotifier;
import org.infinispan.client.hotrod.event.impl.CounterEventDispatcher;
import org.infinispan.client.hotrod.impl.protocol.HotRodConstants;
import org.infinispan.client.hotrod.impl.transport.netty.ChannelRecord;
import org.infinispan.client.hotrod.impl.transport.netty.HeaderDecoder;
import org.infinispan.client.hotrod.impl.transport.netty.OperationDispatcher;
import org.infinispan.client.hotrod.logging.Log;
import org.infinispan.client.hotrod.logging.LogFactory;
import org.infinispan.commons.util.concurrent.AggregateCompletionStage;
import org.infinispan.commons.util.concurrent.CompletionStages;
import org.infinispan.commons.util.concurrent.NonReentrantLock;
import org.infinispan.counter.api.CounterListener;
import org.infinispan.counter.api.CounterManager;
import org.infinispan.counter.api.Handle;

import io.netty.channel.Channel;

/**
 * A Hot Rod client notification manager for a single {@link CounterManager}.
 * 

* This handles all the users listeners. * * @author Pedro Ruivo * @since 9.2 */ public class NotificationManager { private static final Log log = LogFactory.getLog(NotificationManager.class, Log.class); private static final CompletableFuture NO_ERROR_FUTURE = CompletableFuture.completedFuture((short) HotRodConstants.NO_ERROR_STATUS); private final byte[] listenerId; private final ClientListenerNotifier notifier; private final CounterOperationFactory factory; private final OperationDispatcher operationDispatcher; private final ConcurrentMap>> clientListeners = new ConcurrentHashMap<>(); private final Lock lock = new NonReentrantLock(); private volatile CounterEventDispatcher dispatcher; NotificationManager(ClientListenerNotifier notifier, CounterOperationFactory factory, OperationDispatcher operationDispatcher) { this.notifier = notifier; this.factory = factory; this.operationDispatcher = operationDispatcher; this.listenerId = new byte[16]; ThreadLocalRandom.current().nextBytes(listenerId); } public Handle addListener(String counterName, T listener) { if (log.isTraceEnabled()) { log.tracef("Add listener for counter '%s'", counterName); } CounterEventDispatcher dispatcher = this.dispatcher; if (dispatcher != null) { return registerListener(counterName, listener, dispatcher.address()); } log.debugf("ALock %s", lock); lock.lock(); try { dispatcher = this.dispatcher; return registerListener(counterName, listener, dispatcher == null ? null : dispatcher.address()); } finally { lock.unlock(); log.debugf("AUnLock %s", lock); } } private Handle registerListener(String counterName, T listener, SocketAddress address) { HandleImpl handle = new HandleImpl<>(counterName, listener); clientListeners.computeIfAbsent(counterName, name -> { AddListenerOperation op = factory.newAddListenerOperation(counterName, listenerId); if (address == null) { Channel channel = await(operationDispatcher.execute(op)); channel.pipeline().get(HeaderDecoder.class).addListener(listenerId); this.dispatcher = new CounterEventDispatcher(listenerId, clientListeners, ChannelRecord.of(channel), this::failover, () -> channel.eventLoop().execute(() -> { if (log.isTraceEnabled()) { log.tracef("Cleanup for %s on %s", this, channel); } HeaderDecoder decoder = channel.pipeline().get(HeaderDecoder.class); if (decoder != null) { decoder.removeListener(listenerId); } }) ); } else { await(operationDispatcher.executeOnSingleAddress(op, address)); } notifier.addDispatcher(dispatcher); notifier.startClientListener(listenerId); return new CopyOnWriteArrayList<>(); }).add(handle); return handle; } private void removeListener(String counterName, HandleImpl handle) { if (log.isTraceEnabled()) { log.tracef("Remove listener for counter '%s'", counterName); } clientListeners.computeIfPresent(counterName, (name, list) -> { list.remove(handle); if (list.isEmpty()) { if (dispatcher != null) { RemoveListenerOperation op = factory.newRemoveListenerOperation(counterName, listenerId); if (!await(operationDispatcher.executeOnSingleAddress(op, dispatcher.address()))) { log.debugf("Failed to remove counter listener %s on server side", counterName); } } return null; } return list; }); } private CompletableFuture failover() { dispatcher = null; Iterator iterator = clientListeners.keySet().iterator(); if (!iterator.hasNext()) { return null; } CompletableFuture cf = new CompletableFuture<>(); String firstCounterName = iterator.next(); AddListenerOperation op = factory.newAddListenerOperation(firstCounterName, listenerId); log.debugf("Lock %s", lock); lock.lock(); if (dispatcher == null) { operationDispatcher.execute(op).whenComplete((channel, throwable) -> { if (throwable != null) { lock.unlock(); log.debugf(throwable, "Failed to failover counter listener %s", firstCounterName); cf.completeExceptionally(throwable); } else { SocketAddress address; AtomicInteger counter = new AtomicInteger(1); try { if (channel != null) { log.debugf("Creating new counter event dispatcher on %s", channel); // TODO: figure out counter issue here dispatcher = new CounterEventDispatcher(listenerId, clientListeners, channel.remoteAddress(), this::failover, null); notifier.addDispatcher(dispatcher); notifier.startClientListener(listenerId); } address = dispatcher.address(); } catch (Throwable t) { cf.completeExceptionally(t); return; } finally { lock.unlock(); log.debugf("UnLock %s", lock); } while (iterator.hasNext()) { String counterName = iterator.next(); operationDispatcher.executeOnSingleAddress(factory.newAddListenerOperation(counterName, listenerId), address) .whenComplete((___, throwable2) -> { if (throwable2 != null) { log.debugf(throwable2, "Failed to failover counter listener %s", counterName); cf.completeExceptionally(throwable2); } else { if (counter.decrementAndGet() == 0) { cf.complete(null); } } }); } if (counter.decrementAndGet() == 0) { cf.complete(null); } } }); return cf; } else { lock.unlock(); log.debugf("UnLock %s", lock); return null; } } public void stop() { log.debugf("Stopping %s (%s)", this, lock); lock.lock(); try { AggregateCompletionStage aggregateCompletionStage = CompletionStages.aggregateCompletionStage(); for (String counterName : clientListeners.keySet()) { var op = factory.newRemoveListenerOperation(counterName, listenerId); aggregateCompletionStage.dependsOn(operationDispatcher.executeOnSingleAddress(op, dispatcher.address())); } await(aggregateCompletionStage.freeze()); clientListeners.clear(); } finally { lock.unlock(); } } private class HandleImpl implements Handle, Consumer { private final T listener; private final String counterName; private HandleImpl(String counterName, T listener) { this.counterName = counterName; this.listener = listener; } @Override public T getCounterListener() { return listener; } @Override public void remove() { removeListener(counterName, this); } @Override public void accept(HotRodCounterEvent event) { try { listener.onUpdate(event); } catch (Throwable t) { log.debug("Exception in user listener", t); } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy