Skip to content

Commit d9fd8c6

Browse files
committed
Support "no change" in JCache configuration
The expiration policy may return null for update or access queries to not change the entry's expiration time. This was not supported by the configuration file, which translated null to mean eternal. This is now fixed and the default set to "eternal" explicitly.
1 parent f9af06a commit d9fd8c6

File tree

7 files changed

+65
-58
lines changed

7 files changed

+65
-58
lines changed

build.gradle

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@ subprojects {
7878
rootProject.testReport.reportOn it
7979
}
8080
it.dependsOn('jar')
81+
82+
// ensure tasks don't overwrite the default report directories used by the 'test' task
83+
reports.html.destination = "${buildDir}/reports/${name}"
84+
reports.junitXml.destination = "${buildDir}/reports/${name}/results"
85+
binResultsDir = file("${buildDir}/reports/${name}/results/binary/${name}")
8186
}
8287

8388
task testJar(type: Jar, group: "Build") {

caffeine/testing.gradle

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,6 @@ tasks.withType(Test) {
7777
}
7878
}
7979
}
80-
81-
// ensure tasks don't overwrite the default report directories used by the 'test' task
82-
reports.html.destination = "${buildDir}/reports/${name}"
83-
reports.junitXml.destination = "${buildDir}/reports/${name}/results"
84-
binResultsDir = file("${buildDir}/reports/${name}/results/binary/${name}")
8580
}
8681

8782
task stress(type: JavaExec, group: 'Cache tests', description: 'Executes a stress test') {

gradle/wrapper/gradle-wrapper.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
33
distributionPath=wrapper/dists
44
zipStoreBase=GRADLE_USER_HOME
55
zipStorePath=wrapper/dists
6-
distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-rc-1-bin.zip
6+
distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-rc-2-bin.zip

jcache/build.gradle

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,6 @@ task testCompatibilityKit(type: Test, group: 'Build', description: 'Runs the JCa
6060

6161
useJUnit()
6262
testClassesDir = file("${buildDir}/tck")
63-
reports.html.destination = "${buildDir}/reports/${name}"
64-
reports.junitXml.destination = "${buildDir}/${name}-results"
65-
binResultsDir = file("${buildDir}/${name}-results/binary/${name}")
6663

6764
def pkg = 'com.github.benmanes.caffeine.jcache'
6865
systemProperty 'java.net.preferIPv4Stack', 'true'

jcache/src/main/java/com/github/benmanes/caffeine/jcache/CacheProxy.java

Lines changed: 38 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -145,30 +145,35 @@ public V get(K key) {
145145
return null;
146146
}
147147

148+
long start, millis;
148149
boolean statsEnabled = statistics.isEnabled();
149-
long start = (statsEnabled || !expirable.isEternal()) ? ticker.read() : 0L;
150-
long millis = nanosToMillis(start);
151-
if (expirable.hasExpired(millis)) {
152-
cache.asMap().computeIfPresent(key, (k, e) -> {
153-
if (e == expirable) {
154-
dispatcher.publishExpired(this, key, expirable.get());
155-
statistics.recordEvictions(1);
156-
return null;
157-
}
158-
return e;
159-
});
160-
dispatcher.awaitSynchronous();
161-
statistics.recordMisses(1L);
162-
return null;
150+
if (!expirable.isEternal()) {
151+
start = ticker.read();
152+
millis = nanosToMillis(start);
153+
if (expirable.hasExpired(millis)) {
154+
cache.asMap().computeIfPresent(key, (k, e) -> {
155+
if (e == expirable) {
156+
dispatcher.publishExpired(this, key, expirable.get());
157+
statistics.recordEvictions(1);
158+
return null;
159+
}
160+
return e;
161+
});
162+
dispatcher.awaitSynchronous();
163+
statistics.recordMisses(1L);
164+
return null;
165+
}
166+
} else if (statsEnabled) {
167+
start = ticker.read();
168+
millis = nanosToMillis(start);
169+
} else {
170+
start = millis = 0L;
163171
}
172+
164173
setAccessExpirationTime(expirable, millis);
165174
V value = copyValue(expirable);
166175
if (statsEnabled) {
167-
if (value == null) {
168-
statistics.recordMisses(1L);
169-
} else {
170-
statistics.recordHits(1L);
171-
}
176+
statistics.recordHits(1L);
172177
statistics.recordGetTime(ticker.read() - start);
173178
}
174179
return value;
@@ -304,19 +309,18 @@ public V getAndPut(K key, V value) {
304309
int[] puts = { 0 };
305310
V val = putNoCopyOrAwait(key, value, true, puts);
306311
dispatcher.awaitSynchronous();
307-
statistics.recordPuts(puts[0]);
308-
309-
if (val == null) {
310-
statistics.recordMisses(1L);
311-
} else {
312-
statistics.recordHits(1L);
313-
}
314312
V copy = copyOf(val);
315313

316314
if (statsEnabled) {
315+
if (val == null) {
316+
statistics.recordMisses(1L);
317+
} else {
318+
statistics.recordHits(1L);
319+
}
317320
long duration = ticker.read() - start;
318321
statistics.recordGetTime(duration);
319322
statistics.recordPutTime(duration);
323+
statistics.recordPuts(puts[0]);
320324
}
321325
return copy;
322326
}
@@ -547,15 +551,15 @@ public V getAndRemove(K key) {
547551
publishToCacheWriter(writer::delete, () -> key);
548552
V value = removeNoCopyOrAwait(key);
549553
dispatcher.awaitSynchronous();
550-
if (value == null) {
551-
statistics.recordMisses(1L);
552-
} else {
553-
statistics.recordHits(1L);
554-
statistics.recordRemovals(1L);
555-
}
556554
V copy = copyOf(value);
557555

558556
if (statsEnabled) {
557+
if (value == null) {
558+
statistics.recordMisses(1L);
559+
} else {
560+
statistics.recordHits(1L);
561+
statistics.recordRemovals(1L);
562+
}
559563
long duration = ticker.read() - start;
560564
statistics.recordRemoveTime(duration);
561565
statistics.recordGetTime(duration);
@@ -1013,10 +1017,7 @@ protected final void requireNotClosed() {
10131017
* @return a copy of the value if storing by value or the same instance if by reference
10141018
*/
10151019
protected final @Nullable V copyValue(@Nullable Expirable<V> expirable) {
1016-
if (expirable == null) {
1017-
return null;
1018-
}
1019-
return copier.copy(expirable.get(), cacheManager.getClassLoader());
1020+
return (expirable == null) ? null : copier.copy(expirable.get(), cacheManager.getClassLoader());
10201021
}
10211022

10221023
/**
@@ -1026,7 +1027,7 @@ protected final void requireNotClosed() {
10261027
* @return a copy of the mappings if storing by value or the same instance if by reference
10271028
*/
10281029
protected final Map<K, V> copyMap(Map<K, Expirable<V>> map) {
1029-
final ClassLoader classLoader = cacheManager.getClassLoader();
1030+
ClassLoader classLoader = cacheManager.getClassLoader();
10301031
return map.entrySet().stream().collect(Collectors.toMap(
10311032
entry -> (K) copier.copy(entry.getKey(), classLoader),
10321033
entry -> (V) copier.copy(entry.getValue().get(), classLoader)));

jcache/src/main/java/com/github/benmanes/caffeine/jcache/configuration/TypesafeConfigurator.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.logging.Level;
2424
import java.util.logging.Logger;
2525

26+
import javax.annotation.Nullable;
2627
import javax.cache.configuration.Factory;
2728
import javax.cache.configuration.FactoryBuilder;
2829
import javax.cache.configuration.MutableCacheEntryListenerConfiguration;
@@ -169,20 +170,25 @@ public void addLazyExpiration() {
169170
Duration update = getDurationFor("policy.lazy-expiration.update");
170171
Duration access = getDurationFor("policy.lazy-expiration.access");
171172

172-
boolean eternal = creation.isEternal() && update.isEternal() && access.isEternal();
173+
boolean eternal = (creation == Duration.ETERNAL)
174+
&& (update == Duration.ETERNAL)
175+
&& (access == Duration.ETERNAL);
173176
Factory<? extends ExpiryPolicy> factory = eternal
174177
? EternalExpiryPolicy.factoryOf()
175178
: FactoryBuilder.factoryOf(new JCacheExpiryPolicy(creation, update, access));
176179
configuration.setExpiryPolicyFactory(factory);
177180
}
178181

179182
/** Returns the duration for the expiration time. */
180-
private Duration getDurationFor(String path) {
181-
if (config.hasPath(path)) {
182-
long nanos = config.getDuration(path, TimeUnit.MILLISECONDS);
183-
return new Duration(TimeUnit.MILLISECONDS, nanos);
183+
private @Nullable Duration getDurationFor(String path) {
184+
if (!config.hasPath(path)) {
185+
return null;
184186
}
185-
return Duration.ETERNAL;
187+
if (config.getString(path).equalsIgnoreCase("eternal")) {
188+
return Duration.ETERNAL;
189+
}
190+
long nanos = config.getDuration(path, TimeUnit.MILLISECONDS);
191+
return new Duration(TimeUnit.MILLISECONDS, nanos);
186192
}
187193

188194
/** Adds the Caffeine eager expiration settings. */

jcache/src/main/resources/reference.conf

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,16 +53,19 @@ caffeine.jcache {
5353
# entry remains in place.
5454
lazy-expiration {
5555
# The duration before a newly created entry is considered expired. If set to 0 then the
56-
# entry is considered to be already expired and will not be added to the cache.
57-
creation = null
56+
# entry is considered to be already expired and will not be added to the cache. May be
57+
# a time duration or "eternal" to indicate no expiration.
58+
creation = "eternal"
5859

5960
# The duration before a updated entry is considered expired. If set to 0 then the entry is
60-
# considered immediately expired.
61-
update = null
61+
# considered immediately expired. May be null to indicate no change. May be a time duration,
62+
# null to indicate no change, or "eternal" to indicate no expiration.
63+
update = "eternal"
6264

6365
# The duration before a read of an entry is considered expired. If set to 0 then the entry
64-
# is considered immediately expired.
65-
access = null
66+
# is considered immediately expired. May be null to indicate no change. May be a time
67+
# duration, null to indicate no change, or "eternal" to indicate no expiration.
68+
access = "eternal"
6669
}
6770

6871
# The expiration thresholds before eagerly evicting an entry. This settings correspond to the

0 commit comments

Comments
 (0)