- Home
- User documentation
General topics: - Caching Terms - Caching Concepts - Caching Patterns
- Resources
What are user managed caches and what do they offer?
User managed caches is a new concept introduced in Ehcache 3.
It offers the ability to create caches that are not managed by a CacheManager
.
Hence the name of user managed caches.
The objective of this feature is to satisfy cache use cases where the added complexity of a cache manager is of no added value. Ideas are: method local caches, thread local caches or any other place where the lifecycle of the cache is shorter than the application lifecycle.
Limitations
As there is no longer a cache manager offering up services, the main limitation of user managed caches is that the user has to configure all required services by hand. Of course, if you find yourself requiring plenty of services, maybe the cache manager is a better option!
API extensions
While a UserManagedCache
extends Cache
, it offers additional methods:
package org.ehcache;
import java.io.Closeable;
/**
* Represents a {@link Cache} that is not managed by a {@link org.ehcache.CacheManager CacheManager}.
* <P>
* These caches must be {@link #close() closed} in order to release all their resources.
* </P>
*
* @param <K> the key type for the cache
* @param <V> the value type for the cache
*/
public interface UserManagedCache<K, V> extends Cache<K, V>, Closeable {
/**
* Transitions this {@code UserManagedCache} to {@link org.ehcache.Status#AVAILABLE AVAILABLE}.
* <P>
* If an error occurs before the {@code UserManagedCache} is {@code AVAILABLE}, it will revert to
* {@link org.ehcache.Status#UNINITIALIZED UNINITIALIZED} and attempt to properly release all resources.
* </P>
*
* @throws IllegalStateException if the {@code UserManagedCache} is not {@code UNINITIALIZED}
* @throws StateTransitionException if the {@code UserManagedCache} could not be made {@code AVAILABLE}
*/
void init() throws StateTransitionException;
/**
* Transitions this {@code UserManagedCache} to {@link Status#UNINITIALIZED UNINITIALIZED}.
* <P>
* This will release all resources held by this cache.
* </P>
* <P>
* Failure to release a resource will not prevent other resources from being released.
* </P>
*
* @throws StateTransitionException if the {@code UserManagedCache} could not reach {@code UNINITIALIZED} cleanly
* @throws IllegalStateException if the {@code UserManagedCache} is not {@code AVAILABLE}
*/
@Override
void close() throws StateTransitionException;
/**
* Returns the current {@link org.ehcache.Status Status} of this {@code UserManagedCache}.
*
* @return the current {@code Status}
*/
Status getStatus();
}
As can be seen, these methods deal with the lifecycle of the cache and need to be called explicitly.
There is also the following interface which comes into play when a user managed persistent cache is created:
package org.ehcache;
/**
* A {@link UserManagedCache} that holds data that can outlive the JVM.
*
* @param <K> the key type for the cache
* @param <V> the value type for the cache
*/
public interface PersistentUserManagedCache<K, V> extends UserManagedCache<K, V> {
/**
* Destroys all persistent data structures for this {@code PersistentUserManagedCache}.
*
* @throws java.lang.IllegalStateException if state {@link org.ehcache.Status#MAINTENANCE MAINTENANCE} couldn't be reached
* @throws CachePersistenceException if the persistent data cannot be destroyed
*/
void destroy() throws CachePersistenceException;
}
Getting started with user managed caches
Basic example
UserManagedCache<Long, String> userManagedCache =
UserManagedCacheBuilder.newUserManagedCacheBuilder(Long.class, String.class)
.build(false); (1)
userManagedCache.init(); (2)
userManagedCache.put(1L, "da one!"); (3)
userManagedCache.close(); (4)
1 | Create a UserManagedCache instance, again you can either have the builder init() it for you, passing true or |
2 | pass false and it is up to you to UserManagedCache.init() them, prior to using them. |
3 | You can use the cache exactly as a managed cache |
4 | In the same vein, a UserManagedCache requires you to UserManagedCache.close() it explicitly. If you would also use
managed caches simultaneously, the CacheManager.close() operation would not impact the user managed cache(s). |
Disk persistent example
LocalPersistenceService persistenceService = new DefaultLocalPersistenceService(new DefaultPersistenceConfiguration(new File(getStoragePath(), "myUserData"))); (1)
PersistentUserManagedCache<Long, String> cache = UserManagedCacheBuilder.newUserManagedCacheBuilder(Long.class, String.class)
.with(new UserManagedPersistenceContext<Long, String>("cache-name", persistenceService)) (2)
.withResourcePools(ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(10L, EntryUnit.ENTRIES)
.disk(10L, MemoryUnit.MB, true)) (3)
.build(true);
// Work with the cache
cache.put(42L, "The Answer!");
assertThat(cache.get(42L), is("The Answer!"));
cache.close(); (4)
cache.destroy(); (5)
1 | Create the persistence service to be used by the cache for storing data on disk |
2 | Pass the persistence service to the builder next to an id for the cache - note that this will make the builder produce a more specific type: PersistentUserManagedCache |
3 | As usual, indicate here if the data should outlive the cache |
4 | Closing the cache will not delete the data it saved on disk when marked as persistent. |
5 | To delete the data, after closing the cache, destroy has to be explicitly invoked. |
Cache copier example
UserManagedCache<Long, String> cache = UserManagedCacheBuilder.newUserManagedCacheBuilder(Long.class, String.class)
.withKeyCopier(new LongCopier()) (1)
.withValueCopier(new StringCopier()) (2)
.build(true);
// Work with the cache
cache.put(42L, "The Answer!");
assertThat(cache.get(42L), is("The Answer!"));
cache.close();
1 | Configure a key copier. |
2 | Configure a value copier. |
Cache serializing copier example
UserManagedCache<Long, String> cache = UserManagedCacheBuilder.newUserManagedCacheBuilder(Long.class, String.class)
.withKeySerializingCopier() (1)
.withValueSerializingCopier() (2)
.build(true);
// Work with the cache
cache.put(42L, "The Answer!");
assertThat(cache.get(42L), is("The Answer!"));
cache.close();
1 | Configure a SerializingCopier as the key copier. |
2 | Configure a SerializingCopier is the value copier. |
Cache copier and serializer example
UserManagedCache<Long, String> cache = UserManagedCacheBuilder.newUserManagedCacheBuilder(Long.class, String.class)
.withKeyCopier(new LongCopier()) (1)
.withValueCopier(new StringCopier()) (2)
.withKeySerializer(new LongSerializer()) (3)
.withValueSerializer(new StringSerializer()) (4)
.withResourcePools(ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(10L, EntryUnit.ENTRIES) (5)
.offheap(10L, MemoryUnit.MB)) (6)
.build(true);
// Work with the cache
cache.put(42L, "The Answer!");
assertThat(cache.get(42L), is("The Answer!"));
cache.close();
1 | Configure a key copier. |
2 | Configure a value copier. |
3 | Configure a key serializer. |
4 | Configure a value serializer. |
5 | Configure a heap tier. |
6 | Configure a off-heap tier. |
By default, the heap tier stores by reference, unless a copier is configured. The other tiers must use a serializer to copy the keys and values.
Cache serializing copier and serializer example
UserManagedCache<Long, String> cache = UserManagedCacheBuilder.newUserManagedCacheBuilder(Long.class, String.class)
.withKeySerializer(new LongSerializer()) (1)
.withValueSerializer(new StringSerializer()) (2)
.withValueSerializingCopier() (3)
.withResourcePools(ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(10L, EntryUnit.ENTRIES) (4)
.offheap(10L, MemoryUnit.MB)) (5)
.build(true);
// Work with the cache
cache.put(42L, "The Answer!");
assertThat(cache.get(42L), is("The Answer!"));
cache.close();
1 | Configure a key serializer. |
2 | Configure a value serializer. |
3 | Configure a SerializingCopier is the value copier. |
4 | Configure a heap tier. |
5 | Configure a off-heap tier. |
In this case, the heap tier is storing keys by reference since no key copier has been configured, and it is storing values by value since a serializing copier has been configured. Such copier is going to use the configured serializer to perform the necessary copies. The off-heap tier will use both the key serializer as well as the value one to make its copies.
Write-Through Cache example
UserManagedCache<Long, String> cache = UserManagedCacheBuilder.newUserManagedCacheBuilder(Long.class, String.class)
.withLoaderWriter(new SampleLoaderWriter<Long, String>()) (1)
.build(true);
// Work with the cache
cache.put(42L, "The Answer!");
assertThat(cache.get(42L), is("The Answer!"));
cache.close();
1 | If you wish to use a cache in read-through/write-through caching pattern, you’ll have to implement
CacheLoaderWriter and register it. |
Cache with Eviction Advisor example
UserManagedCache<Long, String> cache = UserManagedCacheBuilder.newUserManagedCacheBuilder(Long.class, String.class)
.withEvictionAdvisor(new OddKeysEvictionAdvisor<Long, String>()) (1)
.withResourcePools(ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(2L, EntryUnit.ENTRIES)) (2)
.build(true);
// Work with the cache
cache.put(42L, "The Answer!");
cache.put(41L, "The wrong Answer!");
cache.put(39L, "The other wrong Answer!");
cache.close(); (3)
1 | If you want to hint the eviction algorithm to advise against the eviction of some mappings, you have to
configure an instance of EvictionAdvisor . |
Cache with Listeners example
UserManagedCache<Long, String> cache = UserManagedCacheBuilder.newUserManagedCacheBuilder(Long.class, String.class)
.withEventExecutors(Executors.newSingleThreadExecutor(), Executors.newSingleThreadExecutor()) (1)
.withEventListeners(CacheEventListenerConfigurationBuilder
.newEventListenerConfiguration(ListenerObject.class, EventType.CREATED, EventType.UPDATED)
.asynchronous()
.unordered()) (2)
.withResourcePools(ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(3, EntryUnit.ENTRIES))
.build(true);
cache.put(1L, "Put it");
cache.put(1L, "Update it");
cache.close();
1 | Provide ExecutorService for ordered and unordered event delivery. Note, it is required to provide either a CacheEventDispatcher or ordered/unOrdered ExecutorService for proper event delivery. |
2 | Provide listener configuration using CacheEventListenerConfigurationBuilder. |
Byte Sized Cache example
UserManagedCache<Long, String> cache = UserManagedCacheBuilder.newUserManagedCacheBuilder(Long.class, String.class)
.withSizeOfMaxObjectSize(500, MemoryUnit.B)
.withSizeOfMaxObjectGraph(1000) (1)
.withResourcePools(ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(3, MemoryUnit.MB)) (2)
.build(true);
cache.put(1L, "Put");
cache.put(1L, "Update");
assertThat(cache.get(1L), is("Update"));
cache.close();
1 | The sizing mechanism can be configured along two axis: The first one specifies the maximum number of objects to traverse while walking the object graph, the second defines the maximum size of a single object. |
2 | Size the heap tier in bytes. |