package org.infinispan.counter.impl.manager;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.infinispan.commons.logging.LogFactory;
import org.infinispan.counter.api.CounterConfiguration;
import org.infinispan.counter.api.CounterManager;
import org.infinispan.counter.api.Storage;
import org.infinispan.counter.api.StrongCounter;
import org.infinispan.counter.api.WeakCounter;
import org.infinispan.counter.impl.strong.BoundedStrongCounter;
import org.infinispan.counter.impl.strong.UnboundedStrongCounter;
import org.infinispan.counter.impl.weak.WeakCounterImpl;
import org.infinispan.counter.logging.Log;
import org.infinispan.counter.util.Utils;
/**
* A {@link CounterManager} implementation for embedded cache manager.
*
* @author Pedro Ruivo
* @since 9.0
*/
public class EmbeddedCounterManager implements CounterManager {
private static final long WAIT_CACHES_TIMEOUT = TimeUnit.SECONDS.toNanos(15);
private static final Log log = LogFactory.getLog(EmbeddedCounterManager.class, Log.class);
private final Map<String, Object> counters;
private final CompletableFuture<CacheHolder> future;
private final boolean allowPersistence;
public EmbeddedCounterManager(CompletableFuture<CacheHolder> future, boolean allowPersistence) {
this.allowPersistence = allowPersistence;
this.counters = new ConcurrentHashMap<>();
this.future = future;
}
private static <T> T validateCounter(Class<T> tClass, Object retVal) {
Class<?> rClass = retVal.getClass();
if (tClass.isAssignableFrom(rClass)) {
return tClass.cast(retVal);
}
throw log.invalidCounterType(tClass.getSimpleName(), rClass.getSimpleName());
}
private static CacheHolder extractCacheHolder(CompletableFuture<CacheHolder> future) {
try {
return future.get(WAIT_CACHES_TIMEOUT, TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.interruptedWhileWaitingForCaches();
return null;
} catch (ExecutionException | TimeoutException e) {
log.exceptionWhileWaitingForCached(e);
return null;
}
}
private static WeakCounter createWeakCounter(String counterName, CounterConfiguration configuration,
CacheHolder holder) {
WeakCounterImpl counter = new WeakCounterImpl(counterName, holder.getCounterCache(configuration), configuration);
counter.init();
return counter;
}
private static StrongCounter createBoundedStrongCounter(String counterName, CounterConfiguration configuration,
CacheHolder holder) {
BoundedStrongCounter counter = new BoundedStrongCounter(counterName, holder.getCounterCache(configuration),
configuration);
counter.init();
return counter;
}
private static StrongCounter createUnboundedStrongCounter(String counterName, CounterConfiguration configuration,
CacheHolder holder) {
UnboundedStrongCounter counter = new UnboundedStrongCounter(counterName, holder.getCounterCache(configuration),
configuration);
counter.init();
return counter;
}
@Override
public StrongCounter getStrongCounter(String name) {
Object counter = counters.computeIfAbsent(name, this::createCounter);
return validateCounter(StrongCounter.class, counter);
}
@Override
public WeakCounter getWeakCounter(String name) {
Object counter = counters.computeIfAbsent(name, this::createCounter);
return validateCounter(WeakCounter.class, counter);
}
@Override
public boolean defineCounter(String name, CounterConfiguration configuration) {
validateConfiguration(configuration);
CacheHolder holder = extractCacheHolder(future);
//all the defined counters' configuration are persisted (if enabled)
return holder != null && holder.addConfiguration(name, configuration);
}
@Override
public boolean isDefined(String name) {
CacheHolder holder = extractCacheHolder(future);
return holder != null && holder.getConfiguration(name) != null;
}
@Override
public CounterConfiguration getConfiguration(String counterName) {
CacheHolder holder = extractCacheHolder(future);
return holder == null ? null : holder.getConfiguration(counterName);
}
private Object createCounter(String counterName) {
CacheHolder holder = extractCacheHolder(future);
if (holder == null) {
throw log.unableToFetchCaches();
}
CounterConfiguration configuration = holder.getConfiguration(counterName);
if (configuration == null) {
throw log.undefinedCounter(counterName);
}
switch (configuration.type()) {
case WEAK:
return createWeakCounter(counterName, configuration, holder);
case BOUNDED_STRONG:
return createBoundedStrongCounter(counterName, configuration, holder);
case UNBOUNDED_STRONG:
return createUnboundedStrongCounter(counterName, configuration, holder);
default:
throw new IllegalStateException("[should never happen] unknown counter type: " + configuration.type());
}
}
private void validateConfiguration(CounterConfiguration configuration) {
if (!allowPersistence && configuration.storage() == Storage.PERSISTENT) {
throw log.invalidPersistentStorageMode();
}
switch (configuration.type()) {
case BOUNDED_STRONG:
if (Utils
.isValid(configuration.lowerBound(), configuration.initialValue(), configuration.upperBound())) {
throw log.invalidInitialValueForBoundedCounter(configuration.lowerBound(), configuration.upperBound(),
configuration.initialValue());
}
break;
case WEAK:
if (configuration.concurrencyLevel() < 1) {
throw log.invalidConcurrencyLevel(configuration.concurrencyLevel());
}
break;
}
}
}