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

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

There is a newer version: 15.1.0.Dev04
Show 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.logging.Log;
import org.infinispan.client.hotrod.logging.LogFactory;
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;

/**
 * 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 ConcurrentMap>> clientListeners = new ConcurrentHashMap<>(); private final Lock lock = new NonReentrantLock(); private volatile CounterEventDispatcher dispatcher; NotificationManager(ClientListenerNotifier notifier, CounterOperationFactory factory) { this.notifier = notifier; this.factory = factory; 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, address); if (await(op.execute())) { if (address == null) { this.dispatcher = new CounterEventDispatcher(listenerId, clientListeners, op.getChannel().remoteAddress(), this::failover, op::cleanup); 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, dispatcher.address()); if (!await(op.execute())) { 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, null); log.debugf("Lock %s", lock); lock.lock(); if (dispatcher == null) { op.execute().whenComplete((useChannel, throwable) -> { if (throwable != null) { lock.unlock(); log.debugf(throwable, "Failed to failover counter listener %s", firstCounterName); cf.completeExceptionally(throwable); } else { AtomicInteger counter = new AtomicInteger(1); SocketAddress address; try { if (useChannel) { log.debugf("Creating new counter event dispatcher on %s", op.getChannel()); dispatcher = new CounterEventDispatcher(listenerId, clientListeners, op.getChannel().remoteAddress(), this::failover, op::cleanup); 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(); factory.newAddListenerOperation(counterName, listenerId, address).execute() .whenComplete((useChannel2, throwable2) -> { if (throwable2 != null) { log.debugf(throwable2, "Failed to failover counter listener %s", counterName); cf.completeExceptionally(throwable2); } else { if (useChannel2) { cf.completeExceptionally(new IllegalStateException("Unexpected to use another channel for the same counter")); } 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 { CompletableFuture[] futures = clientListeners.keySet().stream().map(counterName -> factory.newRemoveListenerOperation(counterName, listenerId, dispatcher.address()).execute()) .toArray(CompletableFuture[]::new); await(CompletableFuture.allOf(futures)); 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 - 2024 Weber Informatics LLC | Privacy Policy