-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Notify JCache listener in key-order on eviction
The specification requires that listeners are notified in sequence of key operations on the cache. Like all other jsr107 implementations, Caffeine was notifying in entry order due to the eviction event being published asynchronously to the write. A Caffeine CacheWriter is used to publish the eviction atomically with the cache update, so a JCache listener should always receive events in the proper sequence.
- Loading branch information
Showing
10 changed files
with
68 additions
and
57 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,13 +40,13 @@ | |
* | ||
* @author [email protected] (Ben Manes) | ||
*/ | ||
public class SerializationAwareCopyStrategy extends AbstractCopyStrategy<byte[]> { | ||
public class JavaSerializationCopyStrategy extends AbstractCopyStrategy<byte[]> { | ||
|
||
public SerializationAwareCopyStrategy() { | ||
public JavaSerializationCopyStrategy() { | ||
super(); | ||
} | ||
|
||
public SerializationAwareCopyStrategy(Set<Class<?>> immutableClasses, | ||
public JavaSerializationCopyStrategy(Set<Class<?>> immutableClasses, | ||
Map<Class<?>, Function<Object, Object>> deepCopyStrategies) { | ||
super(immutableClasses, deepCopyStrategies); | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,30 +13,30 @@ | |
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package com.github.benmanes.caffeine.jcache.integration; | ||
package com.github.benmanes.caffeine.jcache.event; | ||
|
||
import static java.util.Objects.requireNonNull; | ||
|
||
import javax.cache.Cache; | ||
|
||
import com.github.benmanes.caffeine.cache.RemovalListener; | ||
import com.github.benmanes.caffeine.cache.RemovalNotification; | ||
import com.github.benmanes.caffeine.cache.CacheWriter; | ||
import com.github.benmanes.caffeine.cache.RemovalCause; | ||
import com.github.benmanes.caffeine.jcache.Expirable; | ||
import com.github.benmanes.caffeine.jcache.event.EventDispatcher; | ||
import com.github.benmanes.caffeine.jcache.management.JCacheStatisticsMXBean; | ||
|
||
/** | ||
* A Caffeine listener that publishes eviction events to the JCache listeners. | ||
* A Caffeine {@link CacheWriter} that provides an adapter to publish events in the order of the | ||
* actions being performed on a key. | ||
* | ||
* @author [email protected] (Ben Manes) | ||
*/ | ||
public final class JCacheRemovalListener<K, V> implements RemovalListener<K, Expirable<V>> { | ||
public final class JCacheEvictionListener<K, V> implements CacheWriter<K, Expirable<V>> { | ||
private final JCacheStatisticsMXBean statistics; | ||
private final EventDispatcher<K, V> dispatcher; | ||
|
||
private Cache<K, V> cache; | ||
|
||
public JCacheRemovalListener(EventDispatcher<K, V> dispatcher, | ||
public JCacheEvictionListener(EventDispatcher<K, V> dispatcher, | ||
JCacheStatisticsMXBean statistics) { | ||
this.dispatcher = requireNonNull(dispatcher); | ||
this.statistics = requireNonNull(statistics); | ||
|
@@ -52,9 +52,16 @@ public void setCache(Cache<K, V> cache) { | |
} | ||
|
||
@Override | ||
public void onRemoval(RemovalNotification<K, Expirable<V>> notification) { | ||
if (notification.wasEvicted()) { | ||
dispatcher.publishRemoved(cache, notification.getKey(), notification.getValue().get()); | ||
public void write(K key, Expirable<V> value) {} | ||
|
||
@Override | ||
public void delete(K key, Expirable<V> value, RemovalCause cause) { | ||
if (cause.wasEvicted()) { | ||
if (cause == RemovalCause.EXPIRED) { | ||
dispatcher.publishExpired(cache, key, value.get()); | ||
} else { | ||
dispatcher.publishRemoved(cache, key, value.get()); | ||
} | ||
dispatcher.ignoreSynchronous(); | ||
statistics.recordEvictions(1L); | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,8 +33,8 @@ | |
/** | ||
* @author [email protected] (Ben Manes) | ||
*/ | ||
public final class SerializationAwareCopyStrategyTest { | ||
final CopyStrategy copier = new SerializationAwareCopyStrategy(); | ||
public final class JavaSerializationCopyStrategyTest { | ||
final CopyStrategy copier = new JavaSerializationCopyStrategy(); | ||
|
||
@Test(expectedExceptions = NullPointerException.class) | ||
public void null_object() { | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,7 +13,7 @@ | |
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package com.github.benmanes.caffeine.jcache.integration; | ||
package com.github.benmanes.caffeine.jcache.event; | ||
|
||
import static org.hamcrest.MatcherAssert.assertThat; | ||
import static org.hamcrest.Matchers.is; | ||
|
@@ -26,6 +26,7 @@ | |
|
||
import javax.cache.Cache; | ||
import javax.cache.configuration.MutableCacheEntryListenerConfiguration; | ||
import javax.cache.event.CacheEntryExpiredListener; | ||
import javax.cache.event.CacheEntryRemovedListener; | ||
|
||
import org.mockito.Mock; | ||
|
@@ -37,27 +38,26 @@ | |
import com.github.benmanes.caffeine.cache.RemovalCause; | ||
import com.github.benmanes.caffeine.cache.RemovalNotification; | ||
import com.github.benmanes.caffeine.jcache.Expirable; | ||
import com.github.benmanes.caffeine.jcache.event.EventDispatcher; | ||
import com.github.benmanes.caffeine.jcache.management.JCacheStatisticsMXBean; | ||
import com.google.common.util.concurrent.MoreExecutors; | ||
|
||
/** | ||
* @author [email protected] (Ben Manes) | ||
*/ | ||
public final class JCacheRemovalListenerTest { | ||
JCacheRemovalListener<Integer, Integer> listener; | ||
public final class JCacheEvictionListenerTest { | ||
JCacheEvictionListener<Integer, Integer> listener; | ||
JCacheStatisticsMXBean statistics; | ||
|
||
@Mock Cache<Integer, Integer> cache; | ||
@Mock CacheEntryRemovedListener<Integer, Integer> entryListener; | ||
@Mock EvictionListener entryListener; | ||
|
||
@BeforeMethod | ||
public void before() { | ||
MockitoAnnotations.initMocks(this); | ||
statistics = new JCacheStatisticsMXBean(); | ||
EventDispatcher<Integer, Integer> dispatcher = | ||
new EventDispatcher<>(MoreExecutors.directExecutor()); | ||
listener = new JCacheRemovalListener<>(dispatcher, statistics); | ||
listener = new JCacheEvictionListener<>(dispatcher, statistics); | ||
listener.setCache(cache); | ||
statistics.enable(true); | ||
|
||
|
@@ -75,14 +75,21 @@ public Iterator<Object[]> notifications() { | |
|
||
@Test(dataProvider = "notifications") | ||
public void publishIfEvicted(RemovalNotification<Integer, Expirable<Integer>> notification) { | ||
listener.onRemoval(notification); | ||
listener.delete(notification.getKey(), notification.getValue(), notification.getCause()); | ||
|
||
if (notification.wasEvicted()) { | ||
verify(entryListener).onRemoved(any()); | ||
if (notification.getCause() == RemovalCause.EXPIRED) { | ||
verify(entryListener).onExpired(any()); | ||
} else { | ||
verify(entryListener).onRemoved(any()); | ||
} | ||
assertThat(statistics.getCacheEvictions(), is(1L)); | ||
} else { | ||
verify(entryListener, never()).onRemoved(any()); | ||
assertThat(statistics.getCacheEvictions(), is(0L)); | ||
} | ||
} | ||
|
||
interface EvictionListener extends CacheEntryRemovedListener<Integer, Integer>, | ||
CacheEntryExpiredListener<Integer, Integer> {} | ||
} |