Caching Data

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.

  1. Determine whether to create a cache in a single VM or across multiple VMs (e.g., in a clustered environment).

  2. Determine if it’s necessary to serialize the data you’re caching.

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;

}

JournalContentKeys constructor populates fields that collectively define unique keys for each piece of journal content.

Note a cache key’s characteristics:

  1. A key instance’s field values relate to the cached data and distinguish it from other data instances.

  2. A key follows Serializable class best practices.

    • Overrides Object’s equals and hashcode 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.

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 ServiceTrackers too. These steps use fictitious key and value classes: SomeKey and SomeValue.

  1. 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();
  1. Access the VM pool you’re using. MultiVMPool and SingleVMPool 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 a ServiceTracker to access the pool.
@Reference
private MultiVMPool _multiVMPool;
  1. Declare a private static PortalCache instance.
private static PortalCache<SomeKey, SomeValue> _portalCache;
  1. 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);
    ...
}
  1. 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);
}
  1. 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.

  1. 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>
  1. 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 this module-multi-vm.xml file to configure its cache named com.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>
  1. Deploy your project.

Congratulations! Your data cache is in effect.

Overriding Cache

« Overriding CacheIntroduction to Collaboration »
Was this article helpful?
0 out of 0 found this helpful