Overview of User Managed Caches
What are user managed caches and what do they offer?
A user managed cache gives you a simple way to configure a cache directly,
without the complexity of setting up or using a CacheManager.
The choice whether to use a UserManagedCache
rather than a CacheManager
usually depends on
whether you need all of the built-in functionality of a CacheManager
.
In cases where your cache requirements are relatively straightforward,
and you do not require the full range of features of a CacheManager
, consider using a UserManagedCache
instead.
Typical scenarios for using a UserManagedCache
are: method local caches, thread local caches or any other place
where the lifecycle of the cache is shorter than the application lifecycle.
API Extensions
User Managed Cache
If you use a UserManagedCache
, you need to configure all required services by hand.
The UserManagedCache
class extends the Cache
class by offering additional methods:
-
init()
- initializes the cache -
close()
- releases the cache resources -
getStatus()
- returns a status
The init
and close
methods deal with the lifecycle of the cache and need to be called explicitly,
whereas these methods are hidden when the cache is inside a CacheManager
.
The interface definition is shown in this code:
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.
*
* @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.
*
* @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>
* Failure to release a resource will not prevent other resources from being released.
*
* @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();
}
User Managed Persistent Cache
A user managed persistent cache holds cached data in a persistent store such as disk, so that the stored data can outlive the JVM in which your caching application runs.
If you want to create a user managed persistent cache,
there is an additional interface PersistentUserManagedCache
that extends UserManagedCache
and adds the destroy
method.
The destroy
method deletes all data structures, including data stored persistently on disk, for a PersistentUserManagedCache
.
The destroy
method deals with the lifecycle of the cache and needs to be called explicitly.
The interface definition is shown in this code:
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;
}
Code examples for User Managed Caches
Example of a basic cache lifecycle
Here is a simple example showing a basic lifecycle of a user managed cache:
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. You can either pass true to have the builder init() it for you,
or you can pass false and it is up to you to init() it prior to using it. |
2 | Since false was passed in, you have to init() the UserManagedCache prior to using it. |
3 | You can use the cache exactly as a managed cache. |
4 | In the same vein, a UserManagedCache requires you to close it explicitly using UserManagedCache.close() .
If you are also using managed caches simultaneously, the CacheManager.close() operation would not impact the user managed cache(s). |
From this basic example, explore the API of UserManagedCacheBuilder
in code or through Javadoc to discover all the directly available features.
The following features apply in the exact same way to user managed caches:
Simply use the methods from UserManagedCacheBuilder
which are equivalent to the ones from CacheConfigurationBuilder
.
Below we will describe a more advanced setup where you need to maintain a service instance in order to have a working user managed cache.
Example with disk persistence and lifecycle
If you want to use disk persistent cache, you will need to create and lifecycle the persistence service:
LocalPersistenceService persistenceService = new DefaultLocalPersistenceService(new DefaultPersistenceConfiguration(new File(getStoragePath(), "myUserData"))); (1)
PersistentUserManagedCache<Long, String> cache = UserManagedCacheBuilder.newUserManagedCacheBuilder(Long.class, String.class)
.with(new UserManagedPersistenceContext<>("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)
persistenceService.stop(); (6)
1 | Create the persistence service to be used by the cache for storing data on disk. |
2 | Pass the persistence service to the builder as well as a name 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, since the cache is marked as persistent. |
5 | To delete the data on disk after closing the cache, you need to invoke the destroy method explicitly. |
6 | You need to stop the persistence service once you have finished using the cache. |
Example with cache event listeners
Cache event listeners require executor services in order to work.
You will have to provide either a CacheEventDispatcher
implementation or make use of the default one
by providing two executor services: one for ordered events and one for unordered ones.
The ordered events executor must be single threaded to guarantee ordering. |
For more information on cache event listeners, see the section Cache Event Listeners.
UserManagedCache<Long, String> cache = UserManagedCacheBuilder.newUserManagedCacheBuilder(Long.class, String.class)
.withEventExecutors(Executors.newSingleThreadExecutor(), Executors.newFixedThreadPool(5)) (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 the ExecutorService for ordered and unordered event delivery. |
2 | Provide a listener configuration using CacheEventListenerConfigurationBuilder . |