Skip to content

Commit

Permalink
Add CaffeineSpec for externalized configurations (fixes #37)
Browse files Browse the repository at this point in the history
Similar to Guava's CacheBuilderSpec, the CaffeineSpec simplifies the
creation of a builder from externalized flags. This can be useful for
configuration files, such as Spring properties.

The string format and options are the same as Guava's, except that
`concurrencyLevel` is not supported. The `disableCaching` factory
method was not ported over since its usefulness seemed questionable.

CaffeineSpec passes Guava's test cases, with the above changes made.
  • Loading branch information
ben-manes committed Jan 18, 2016
1 parent 71794bf commit 6e8b80a
Show file tree
Hide file tree
Showing 18 changed files with 1,336 additions and 155 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -602,9 +602,9 @@ void expireAfterAccessEntries(long now) {
void expireAfterAccessEntries(AccessOrderDeque<Node<K, V>> accessOrderDeque,
long expirationTime, long now) {
for (;;) {
final Node<K, V> node = accessOrderDeque.peekFirst();
Node<K, V> node = accessOrderDeque.peekFirst();
if ((node == null) || (node.getAccessTime() > expirationTime)) {
break;
return;
}
evictEntry(node, RemovalCause.EXPIRED, now);
}
Expand Down Expand Up @@ -925,7 +925,7 @@ void onAccess(Node<K, V> node) {
}
}

/** Updates the node's location in the probation queue. */
/** Promote the node from probation to protected on an access. */
@GuardedBy("evictionLock")
void reorderProbation(Node<K, V> node) {
if (!accessOrderProbationDeque().contains(node)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.annotation.Nonnegative;
Expand Down Expand Up @@ -120,6 +121,7 @@
* @param <V> the base value type for all caches created by this builder
*/
public final class Caffeine<K, V> {
static final Logger logger = Logger.getLogger(Caffeine.class.getName());
static final Supplier<StatsCounter> ENABLED_STATS_COUNTER_SUPPLIER = ConcurrentStatsCounter::new;

enum Strength { STRONG, WEAK, SOFT }
Expand All @@ -129,6 +131,8 @@ enum Strength { STRONG, WEAK, SOFT }
static final int DEFAULT_EXPIRATION_NANOS = 0;
static final int DEFAULT_REFRESH_NANOS = 0;

boolean strictParsing = true;

long maximumSize = UNSET_INT;
long maximumWeight = UNSET_INT;
int initialCapacity = UNSET_INT;
Expand All @@ -141,7 +145,6 @@ enum Strength { STRONG, WEAK, SOFT }
Supplier<StatsCounter> statsCounterSupplier;
CacheWriter<? super K, ? super V> writer;
Weigher<? super K, ? super V> weigher;
Supplier<String> nameSupplier;
Executor executor;
Ticker ticker;

Expand Down Expand Up @@ -189,6 +192,29 @@ public static Caffeine<Object, Object> newBuilder() {
return new Caffeine<Object, Object>();
}

/**
* Constructs a new {@code Caffeine} instance with the settings specified in {@code spec}.
*
* @return a new instance with the specification's settings
*/
@Nonnull
public static Caffeine<Object, Object> from(CaffeineSpec spec) {
Caffeine<Object, Object> builder = spec.toBuilder();
builder.strictParsing = false;
return builder;
}

/**
* Constructs a new {@code Caffeine} instance with the settings specified in {@code spec}.
*
* @param spec a String in the format specified by {@link CaffeineSpec}
* @return a new instance with the specification's settings
*/
@Nonnull
public static Caffeine<Object, Object> from(String spec) {
return from(CaffeineSpec.parse(spec));
}

/**
* Sets the minimum total size for the internal hash tables. Providing a large enough estimate at
* construction time avoids the need for expensive resizing operations later, but setting this
Expand Down Expand Up @@ -341,8 +367,9 @@ public <K1 extends K, V1 extends V> Caffeine<K1, V1> weigher(
@Nonnull Weigher<? super K1, ? super V1> weigher) {
requireNonNull(weigher);
requireState(this.weigher == null, "weigher was already set to %s", this.weigher);
requireState(this.maximumSize == UNSET_INT,
requireState(!strictParsing || this.maximumSize == UNSET_INT,
"weigher can not be combined with maximum size", this.maximumSize);

@SuppressWarnings("unchecked")
Caffeine<K1, V1> self = (Caffeine<K1, V1>) this;
self.weigher = weigher;
Expand Down Expand Up @@ -841,8 +868,10 @@ private void requireNonLoadingCache() {
private void requireWeightWithWeigher() {
if (weigher == null) {
requireState(maximumWeight == UNSET_INT, "maximumWeight requires weigher");
} else {
} else if (strictParsing) {
requireState(maximumWeight != UNSET_INT, "weigher requires maximumWeight");
} else if (maximumWeight == UNSET_INT) {
logger.log(Level.WARNING, "ignoring weigher specified without maximumWeight");
}
}

Expand Down
Loading

0 comments on commit 6e8b80a

Please sign in to comment.