package org.infinispan.counter.api; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.Collections; import java.util.Objects; import java.util.Set; import org.infinispan.commons.io.UnsignedNumeric; import org.infinispan.commons.marshall.AdvancedExternalizer; import org.infinispan.commons.marshall.Ids; import org.infinispan.commons.marshall.MarshallUtil; /** * A counter configuration used to define counters cluster wide via {@link CounterManager#defineCounter(String, * CounterConfiguration)}. * <p> * The configuration must be built using {@link CounterConfiguration#builder(CounterType)}. Only {@link CounterType} is * required. * * @author Pedro Ruivo * @see CounterType * @since 9.0 */ public class CounterConfiguration { public static final AdvancedExternalizer<CounterConfiguration> EXTERNALIZER = new Externalize(); private final long initialValue; private final long upperBound; private final long lowerBound; private final int concurrencyLevel; private final CounterType type; private final Storage storage; private CounterConfiguration(long initialValue, long lowerBound, long upperBound, int concurrencyLevel, CounterType type, Storage storage) { this.initialValue = initialValue; this.upperBound = upperBound; this.lowerBound = lowerBound; this.concurrencyLevel = concurrencyLevel; this.type = type; this.storage = storage; } public static Builder builder(CounterType type) { return new Builder(Objects.requireNonNull(type)); } public long initialValue() { return initialValue; } public long upperBound() { return upperBound; } public long lowerBound() { return lowerBound; } public CounterType type() { return type; } public int concurrencyLevel() { return concurrencyLevel; } public Storage storage() { return storage; } @Override public String toString() { return "CounterConfiguration{" + "initialValue=" + initialValue + ", upperBound=" + upperBound + ", lowerBound=" + lowerBound + ", concurrencyLevel=" + concurrencyLevel + ", type=" + type + ", storage=" + storage + '}'; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } CounterConfiguration that = (CounterConfiguration) o; return initialValue == that.initialValue && upperBound == that.upperBound && lowerBound == that.lowerBound && concurrencyLevel == that.concurrencyLevel && type == that.type && storage == that.storage; } @Override public int hashCode() { int result = (int) (initialValue ^ (initialValue >>> 32)); result = 31 * result + (int) (upperBound ^ (upperBound >>> 32)); result = 31 * result + (int) (lowerBound ^ (lowerBound >>> 32)); result = 31 * result + concurrencyLevel; result = 31 * result + type.hashCode(); result = 31 * result + storage.hashCode(); return result; } /** * The builder of {@link CounterConfiguration}. */ public static class Builder { private final CounterType type; private long initialValue = 0; private long lowerBound = Long.MIN_VALUE; private long upperBound = Long.MAX_VALUE; private Storage storage = Storage.VOLATILE; private int concurrencyLevel = 64; private Builder(CounterType type) { this.type = type; } /** * Sets the initial value. * <p> * The default value is zero. * * @param initialValue the new initial value. */ public Builder initialValue(long initialValue) { this.initialValue = initialValue; return this; } /** * Sets the lower bound (inclusive) of the counter. * <p> * Only for {@link CounterType#BOUNDED_STRONG} counters. * <p> * The default value is {@link Long#MIN_VALUE}. * * @param lowerBound the new lower bound. */ public Builder lowerBound(long lowerBound) { this.lowerBound = lowerBound; return this; } /** * Sets the upper bound (inclusive) of the counter. * <p> * Only for {@link CounterType#BOUNDED_STRONG} counters. * <p> * The default value is {@link Long#MAX_VALUE}. * * @param upperBound the new upper bound. */ public Builder upperBound(long upperBound) { this.upperBound = upperBound; return this; } /** * Sets the storage mode of the counter. * <p> * The default value is {@link Storage#VOLATILE}. * * @param storage the new storage mode. * @see Storage */ public Builder storage(Storage storage) { this.storage = Objects.requireNonNull(storage); return this; } /** * Sets the concurrency level of the counter. * <p> * Only for {@link CounterType#WEAK}. * <p> * <p> * The concurrency level set the amount of concurrent updates that can happen simultaneous. It is trade-off * between the write performance and read performance. A higher value will allow more concurrent updates, however * it will take more time to compute the counter value. * <p> * The default value is 64. * * @param concurrencyLevel the new concurrency level. */ public Builder concurrencyLevel(int concurrencyLevel) { this.concurrencyLevel = concurrencyLevel; return this; } /** * @return the {@link CounterConfiguration} with this configuration. */ public CounterConfiguration build() { return new CounterConfiguration(initialValue, lowerBound, upperBound, concurrencyLevel, type, storage); } } private static class Externalize implements AdvancedExternalizer<CounterConfiguration> { @Override public Set<Class<? extends CounterConfiguration>> getTypeClasses() { return Collections.singleton(CounterConfiguration.class); } @Override public Integer getId() { return Ids.COUNTER_CONFIGURATION; } @Override public void writeObject(ObjectOutput output, CounterConfiguration object) throws IOException { MarshallUtil.marshallEnum(object.type, output); MarshallUtil.marshallEnum(object.storage, output); output.writeLong(object.initialValue); switch (object.type) { case BOUNDED_STRONG: output.writeLong(object.lowerBound); output.writeLong(object.upperBound); break; case WEAK: UnsignedNumeric.writeUnsignedInt(output, object.concurrencyLevel); break; default: } } @Override public CounterConfiguration readObject(ObjectInput input) throws IOException, ClassNotFoundException { CounterType type = MarshallUtil.unmarshallEnum(input, CounterType::valueOf); Storage storage = MarshallUtil.unmarshallEnum(input, Storage::valueOf); long initialValue = input.readLong(); long lowerBound = 0; long upperBound = 0; int concurrencyLevel = 0; //noinspection ConstantConditions switch (type) { case BOUNDED_STRONG: lowerBound = input.readLong(); upperBound = input.readLong(); break; case WEAK: concurrencyLevel = UnsignedNumeric.readUnsignedInt(input); break; default: } return new CounterConfiguration(initialValue, lowerBound, upperBound, concurrencyLevel, type, storage); } } }