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

org.infinispan.counter.impl.strong.AbstractStrongCounter Maven / Gradle / Ivy

The newest version!
package org.infinispan.counter.impl.strong;

import static org.infinispan.counter.impl.Util.awaitCounterOperation;
import static org.infinispan.counter.impl.Utils.getPersistenceMode;
import static org.infinispan.counter.impl.entries.CounterValue.newCounterValue;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.CompletionStage;

import org.infinispan.AdvancedCache;
import org.infinispan.Cache;
import org.infinispan.commons.util.concurrent.CompletableFutures;
import org.infinispan.counter.api.CounterConfiguration;
import org.infinispan.counter.api.CounterEvent;
import org.infinispan.counter.api.CounterListener;
import org.infinispan.counter.api.Handle;
import org.infinispan.counter.api.StrongCounter;
import org.infinispan.counter.api.SyncStrongCounter;
import org.infinispan.counter.impl.SyncStrongCounterAdapter;
import org.infinispan.counter.impl.entries.CounterKey;
import org.infinispan.counter.impl.entries.CounterValue;
import org.infinispan.counter.impl.function.AddFunction;
import org.infinispan.counter.impl.function.CompareAndSwapFunction;
import org.infinispan.counter.impl.function.CreateAndAddFunction;
import org.infinispan.counter.impl.function.CreateAndCASFunction;
import org.infinispan.counter.impl.function.CreateAndSetFunction;
import org.infinispan.counter.impl.function.ReadFunction;
import org.infinispan.counter.impl.function.RemoveFunction;
import org.infinispan.counter.impl.function.ResetFunction;
import org.infinispan.counter.impl.function.SetFunction;
import org.infinispan.counter.impl.listener.CounterEventGenerator;
import org.infinispan.counter.impl.listener.CounterEventImpl;
import org.infinispan.counter.impl.listener.CounterManagerNotificationManager;
import org.infinispan.counter.impl.manager.InternalCounterAdmin;
import org.infinispan.functional.FunctionalMap;
import org.infinispan.functional.impl.FunctionalMapImpl;
import org.infinispan.functional.impl.ReadOnlyMapImpl;
import org.infinispan.functional.impl.ReadWriteMapImpl;

import net.jcip.annotations.GuardedBy;

/**
 * A base strong consistent counter implementation.
 * 

* Implementation: The value is stored in a single key and it uses the Infinispan's concurrency control and distribution * to apply the write and reads. It uses the functional API. *

* Writes: The writes are performed by the functional API in order. The single key approach allows us to provide atomic * properties for the counter value. *

* Reads: The reads read the value from the cache and it can go remotely. *

* Weak Reads: This implementation supports weak cached reads. It uses clustered listeners to receive the notifications * of the actual value to store it locally. * * @author Pedro Ruivo * @since 9.0 */ public abstract class AbstractStrongCounter implements StrongCounter, CounterEventGenerator, InternalCounterAdmin { final StrongCounterKey key; private final FunctionalMap.ReadWriteMap readWriteMap; private final FunctionalMap.ReadOnlyMap readOnlyMap; private final CounterManagerNotificationManager notificationManager; private final CounterConfiguration configuration; @GuardedBy("this") private CounterValue weakCounter; AbstractStrongCounter(String counterName, AdvancedCache cache, CounterConfiguration configuration, CounterManagerNotificationManager notificationManager) { this.notificationManager = notificationManager; FunctionalMapImpl functionalMap = FunctionalMapImpl.create(cache) .withParams(getPersistenceMode(configuration.storage())); key = new StrongCounterKey(counterName); readWriteMap = ReadWriteMapImpl.create(functionalMap); readOnlyMap = ReadOnlyMapImpl.create(functionalMap); weakCounter = null; this.configuration = configuration; } /** * It removes a strong counter from the {@code cache}, identified by the {@code counterName}. * * @param cache The {@link Cache} to remove the counter from. * @param counterName The counter's name. */ public static CompletionStage removeStrongCounter(Cache cache, String counterName) { return cache.removeAsync(new StrongCounterKey(counterName)).thenApply(CompletableFutures.toNullFunction()); } public final CompletionStage init() { registerListener(); return readOnlyMap.eval(key, ReadFunction.getInstance()).thenAccept(this::initCounterState).thenApply(unused -> this); } @Override public StrongCounter asStrongCounter() { return this; } @Override public CompletionStage destroy() { removeListener(); return remove(); } @Override public CompletionStage value() { return getValue(); } @Override public boolean isWeakCounter() { return false; } @Override public final String getName() { return key.getCounterName().toString(); } @Override public final CompletableFuture getValue() { return readOnlyMap.eval(key, ReadFunction.getInstance()).thenApply(this::handleReadResult); } @Override public final CompletableFuture addAndGet(long delta) { return readWriteMap.eval(key, new AddFunction<>(delta)).thenCompose(value -> checkAddResult(value, delta)); } @Override public final CompletableFuture reset() { return readWriteMap.eval(key, ResetFunction.getInstance()); } @Override public final Handle addListener(T listener) { awaitCounterOperation(notificationManager.registerCounterValueListener(readWriteMap.cache())); return notificationManager.registerUserListener(key.getCounterName(), listener); } @Override public CompletableFuture compareAndSwap(long expect, long update) { return readWriteMap.eval(key, new CompareAndSwapFunction<>(expect, update)) .thenCompose(result -> checkCasResult(result, expect, update)); } @Override public CompletableFuture getAndSet(long value) { return readWriteMap.eval(key, new SetFunction<>(value)) .thenCompose(result -> checkSetResult(result, value)); } @Override public synchronized CounterEvent generate(CounterKey key, CounterValue value) { CounterValue newValue = value == null ? newCounterValue(configuration) : value; if (weakCounter == null || weakCounter.equals(newValue)) { weakCounter = newValue; return null; } else { CounterEvent event = CounterEventImpl.create(weakCounter, newValue); weakCounter = newValue; return event; } } public CompletableFuture remove() { return readWriteMap.eval(key, RemoveFunction.getInstance()); } @Override public final CounterConfiguration getConfiguration() { return configuration; } @Override public SyncStrongCounter sync() { return new SyncStrongCounterAdapter(this); } protected abstract Long handleCASResult(Object state); /** * Extracts and validates the value after a read. *

* Any exception should be thrown using {@link CompletionException}. * * @param counterValue The new {@link CounterValue}. * @return The new value stored in {@link CounterValue}. */ protected abstract long handleAddResult(CounterValue counterValue); protected abstract Long handleSetResult(Object state); /** * Registers this instance as a cluster listener. *

* Note: It must be invoked when initialize the instance. */ private void registerListener() { notificationManager.registerCounter(key.getCounterName(), this, null); } /** * Removes this counter from the notification manager. */ private void removeListener() { notificationManager.removeCounter(key.getCounterName()); } /** * Initializes the weak value. * * @param currentValue The current value. */ private synchronized void initCounterState(Long currentValue) { if (weakCounter == null) { weakCounter = currentValue == null ? newCounterValue(configuration) : newCounterValue(currentValue, configuration); } } /** * Retrieves and validate the value after a read. * * @param value The current value. * @return The current value. */ private long handleReadResult(Long value) { return value == null ? configuration.initialValue() : value; } private CompletableFuture checkAddResult(CounterValue value, long delta) { if (value == null) { //key doesn't exist in the cache. create and add. return readWriteMap.eval(key, new CreateAndAddFunction<>(configuration, delta)) .thenApply(this::handleAddResult); } else { return CompletableFuture.completedFuture(handleAddResult(value)); } } private CompletionStage checkCasResult(Object result, long expect, long update) { if (result == null) { //key doesn't exist in the cache. create and CAS return readWriteMap.eval(key, new CreateAndCASFunction<>(configuration, expect, update)) .thenApply(this::handleCASResult); } else { return CompletableFuture.completedFuture(handleCASResult(result)); } } private CompletionStage checkSetResult(Object result, long update) { if (result == null) { return readWriteMap.eval(key, new CreateAndSetFunction<>(configuration, update)) .thenApply(this::handleSetResult); } else { return CompletableFuture.completedFuture(handleSetResult(result)); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy