Liferay’s caching framework
helps you use Ehcache to cache any data. The SingleVMPool
and
MultiVMPool
classes use Liferay’s
PortalCache
utility. Storing and retrieving cached data objects is as easy as using a hash
map: you associate a key with every cache value. The following steps demonstrate
implementing data caching.
Step 1: Determine Cache Pool Requirements
There are cache pools for single-VM and multi-VM environments. The pool types
and some Ehcache features require using Serializable
values.
-
Determine whether to create a cache in a single VM or across multiple VMs (e.g., in a clustered environment).
-
Determine if it’s necessary to serialize the data you’re caching.
-
MultiVMPool
requires both the cache key and cache value to beSerializable
. -
SingleVMPool
typically requires only cache keys to beSerializable
. Note that some Ehache features, such asoverflowToDisk
, requireSerializable
values too.
-
Step 2: Implement a Cache Key
Cache keys must be unique,
Serializable
objects. They should relate to the values being cached. For example, in
Liferay DXP’s JournalContentImpl
, a JournalContentKey
instance relates to each
cached JournalArticleDisplay
object. Here’s the JournalContentKey
class:
private static class JournalContentKey implements Serializable {
@Override
public boolean equals(Object obj) {
JournalContentKey journalContentKey = (JournalContentKey)obj;
if ((journalContentKey._groupId == _groupId) &&
Objects.equals(journalContentKey._articleId, _articleId) &&
(journalContentKey._version == _version) &&
Objects.equals(
journalContentKey._ddmTemplateKey, _ddmTemplateKey) &&
(journalContentKey._layoutSetId == _layoutSetId) &&
Objects.equals(journalContentKey._viewMode, _viewMode) &&
Objects.equals(journalContentKey._languageId, _languageId) &&
(journalContentKey._page == _page) &&
(journalContentKey._secure == _secure)) {
return true;
}
return false;
}
@Override
public int hashCode() {
int hashCode = HashUtil.hash(0, _groupId);
hashCode = HashUtil.hash(hashCode, _articleId);
hashCode = HashUtil.hash(hashCode, _version);
hashCode = HashUtil.hash(hashCode, _ddmTemplateKey);
hashCode = HashUtil.hash(hashCode, _layoutSetId);
hashCode = HashUtil.hash(hashCode, _viewMode);
hashCode = HashUtil.hash(hashCode, _languageId);
hashCode = HashUtil.hash(hashCode, _page);
return HashUtil.hash(hashCode, _secure);
}
private JournalContentKey(
long groupId, String articleId, double version,
String ddmTemplateKey, long layoutSetId, String viewMode,
String languageId, int page, boolean secure) {
_groupId = groupId;
_articleId = articleId;
_version = version;
_ddmTemplateKey = ddmTemplateKey;
_layoutSetId = layoutSetId;
_viewMode = viewMode;
_languageId = languageId;
_page = page;
_secure = secure;
}
private static final long serialVersionUID = 1L;
private final String _articleId;
private final String _ddmTemplateKey;
private final long _groupId;
private final String _languageId;
private final long _layoutSetId;
private final int _page;
private final boolean _secure;
private final double _version;
private final String _viewMode;
}
JournalContentKey
s constructor populates fields that collectively define
unique keys for each piece of journal content.
Note a cache key’s characteristics:
-
A key instance’s field values relate to the cached data and distinguish it from other data instances.
-
A key follows
Serializable
class best practices.- Overrides
Object
’sequals
andhashcode
methods. - Includes a private static final long
serialVersionUID
field. It is to be incremented when a new version of the class is incompatible with previous versions.
- Overrides
Your cache key class is ready for caching data values.
Step 3: Implement Cache Logic
When your application creates or requests the data type you’re caching, you must
handle getting existing data from cache and putting new/updated data into the
cache. Liferay DXP’s caching classes are easy to inject into a
Declarative Services (DS) Component,
but you can access them using
ServiceTracker
s
too. These steps use fictitious key and value classes: SomeKey
and SomeValue
.
- Name your cache. Cache names are arbitrary, but they must be unique in the cache pool, and typically identify the data type being cached.
protected static final String CACHE_NAME = SomeValue.class.getName();
- Access the VM pool you’re using.
MultiVMPool
andSingleVMPool
are Declarative Service (DS) components. To access a pool from a DS component, apply the@Reference
annotation to a pool field (see below). Otherwise, use aServiceTracker
to access the pool.
@Reference
private MultiVMPool _multiVMPool;
- Declare a private static
PortalCache
instance.
private static PortalCache<SomeKey, SomeValue> _portalCache;
- Initialize your
PortalCache
when your class is being activated or initialized. If you’re using a DS component, initialize the cache in your component’s activation method (annotated with@Activate
). Get the cache from your VM pool using your cache name. For example, this DS component’s activation method gets a cache from the multi-VM pool.
@Activate
public void activate() {
_portalCache =
(PortalCache<SomeKey, SomeValue>)
_multiVMPool.getPortalCache(CACHE_NAME);
...
}
- Similarly, remove your cache when your class instance is deactivated or
destroyed. If you’re using a DS component, remove the cache in your
deactivation method (annotated with
@Deactivate
). Use the VM pool to remove the cache.
@Deactivate
public void deactivate() {
_multiVMPool.removePortalCache(CACHE_NAME);
}
- In your code that uses the cached data, implement your caching logic. Here’s some example code:
SomeKey key = new SomeKey(...);
SomeValue value = _portalCache.get(
key);
if (value == null) {
value = createSomeValue(...);
_portalCache.put(key, value);
}
// continue using the data
...
The code above constructs a key based on the data being used. Then, the key is
used to check the PortalCache
for the data. If the cache doesn’t have data
associated with the key, data is created and put it into the cache. The code
continues using the cached data. Use similar logic for the data you are caching.
Configuring the cache and deploying your project is next.
Step 4: Configure the Cache
It’s time to specify your Ehcache configuration.
- Depending on the VM pool you’re using, start your XML file in one of the following ways.
Multi VM file:
<ehcache
dynamicConfig="true"
monitoring="off"
name="module-multi-vm"
updateCheck="false"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://www.ehcache.org/ehcache.xsd"
>
<!-- cache elements go here -->
</ehcache>
Single VM file:
<ehcache
dynamicConfig="true"
monitoring="off"
name="module-single-vm"
updateCheck="false"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://www.ehcache.org/ehcache.xsd"
>
<!-- cache elements go here -->
</ehcache>
-
Add a
<cache>
element for the cache you’re creating. Although the cache name is arbitrary, using a name-spaced name such as a fully qualified class name is a best practice.Configure your
<cache>
element to fit your caching requirements. The ehcache.xsd and Ehcache documentation describe the<cache>
attributes.For example, the Liferay Web Experience suite’s
com.liferay.journal.service
module uses thismodule-multi-vm.xml
file to configure its cache namedcom.liferay.journal.util.JournalContent
.
<ehcache
dynamicConfig="true"
monitoring="off"
name="module-multi-vm"
updateCheck="false"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://www.ehcache.org/ehcache.xsd"
>
<cache
eternal="false"
maxElementsInMemory="10000"
name="com.liferay.journal.util.JournalContent"
overflowToDisk="false"
timeToIdleSeconds="600"
>
</cache>
</ehcache>
- Deploy your project.
Congratulations! Your data cache is in effect.