Application Caching using Ehcache
Ehcache is an open source caching system that handles application caching.
Advantages
Using Ehcache gives Self-Managed Commerce the following benefits:
- JMX Support
- Unified run-time configuration for application and OpenJPA L2 caches
- Improved cache visibility and control
Application caching is enabled in Cortex, Search Server, Integration Server, and the Import/Export CLI tool.
Cache Behaviour
Most application caches are initialized using the EhcacheCacheFactory class. This creates a new cache in the EhCache cache manager and then wraps the result using EhcacheCacheAdapter which implements the com.elasticpath.cache.Cache interface.
The com.elasticpath.cache.Cache interface exposes several important methods which are described below.
CacheResult<V> get(K key)
This simple method retrieves the value from the cache for the passed key. The CacheResult object allows the caller to determine if the object was present in the cache by calling the isPresent method. It is possible to cache a null result, which will return true when isPresent is invoked, but the get method will return null.
It is usually recommended to use the get method with the fallbackLoader parameter instead of this method to ensure thread safety.
V get(K key, Function<K, V> fallbackLoader)
This method retrieves the value from the cache for the passed key and falls back to the fallbackLoader function to retrieve the value if it isn't present. The result from the fallbackLoader is inserted into the cache.
This method also uses XSync to ensure that if multiple threads request the same key value, only the first will invoke the fallbackLoader and the other threads will block until the fallbackLoader completes. After the value is loaded, all of the waiting threads will receive this value.
V get(K key, Function<K, V> fallbackLoader, BiFunction<K, V, V> populateCacheFunction)
This method retrieves the value from the cache for the passed key and falls back to the fallbackLoader function to retrieve the value if it isn't present. The populateCacheFunction is called after the fallbackLoader function completes, and allows more complex cache population logic. It is the responsibility of the populateCacheFunction to populate the cache, if necessary.
This method also uses XSync to ensure that if multiple threads request the same key value, only the first will invoke the fallbackLoader and the other threads will block until the fallbackLoader completes. After the value is loaded, all of the waiting threads will receive this value.
Map<K, V> getAll(Collection<? extends K> keyValues)
This method retrieves multiple value from the cache for the passed keys. Each result is returned in the map associated to it's corresponding key.
Map<K, V> getAll(Collection<K> keyValues, Function<Collection<K>, Map<K, V>> fallbackLoader)
This method retrieves multiple value from the cache for the passed keys. For any values not present in the cache, a single call to the fallbackLoader function is made to retrieve the missing values. The results from the fallbackLoader are inserted into the cache as separate entries.
V put(K key, V value)
This method inserts a single value into the cache corresponding to the passed key.
boolean remove(K key)
This method removes a single value from the cache for the passed key.
void removeAll()
This method removes all values from the cache.
boolean containsKey(K key)
This method returns true if the specified key exists in the cache.
Configuration
Elastic Path provides the following Ehcache configuration file in the extensions/database/ext-data module:
src/main/resources/environments/default/files/conf/cache/ehcache-fast-updates.xmlfor use in development environments
For development use, you need to copy configuration files to your ${user.home}/ep/conf directory as described in Updating Configuration Files.
For deployed environments, the configuration file is specified using the ep.external.ehcache.xml.path property, either as a JVM parameter or in the ep.properties file.
The file path in ep.external.ehcache.xml.path must be an absolute path. There are three supported formats as shown in the following examples:
${user.home}/ep/conf/cache/ehcache.xml(Linux and Windows)/ep/conf/cache/ehcache.xml(Linux) orc:/ep/conf/cache/ehcache.xml(Windows)file:///ep/conf/cache/ehcache.xml(Linux) orfile:///c:/ep/conf/cache/ehcache.xml(Windows)
Additional Slash
The additional slash in file:/// is required by Spring.
If a particular cache is not specified in the file referenced by ep.external.ehcache.xml.path (or if the ep.external.ehcache.xml.path value is not set), then the cache will use the default values specified in the Spring bean. The Spring bean defaults represent the values that Elastic Path recommends for most production-like environments.
All Ehcaches can also be completely disabled by setting the -Dnet.sf.ehcache.disabled=true JVM parameter.
An application restart is required for configuration changes to take effect.
Cortex and Integration Server Application Caches
The application level caches sit above the data access layer and are used by Elastic Path to cache longer lived application data.
Application cache configuration (time-to-live, time-to-idle, and max entries) can be defined in the external configuration file described above. If no external Ehcache file is defined, or if the file doesn’t contain an entry for a cache, then the fallback configuration is defined by the cache bean definition in Spring. See Application Data Caching a for a list of caches.
Search Server Application Caches
In the Search Server, many application caches have their configuration overridden to a shorter time-to-live. This is done to ensure that the search server doesn’t index out-of-date cached values.
By default, all caches, except domain (starting with com.elasticpath) and openjpa-query, have time-to-live and time-to-idle overridden to 5 seconds by the ehCacheConfigurationConfigurator bean.
These values can be changed via system properties, if required:
-Dep.ehcache.ttl.seconds.override=<N>-Dep.ehcache.tti.seconds.override=<N>
Additionally, which caches are overridden by the ehCacheConfigurationConfigurator bean can be configured in its Spring properties:
cachesToOverride: the list of caches for which time-to-live/time-to-idle will be overriddencachesToExclude: the list of caches that will be excluded from overridingdomainCacheNamePrefixesToExclude: the list of domain cache prefixes that will be excluded from overriding
Cache Invalidation
Objects in the Ehcache can be evicted for two reasons:
- The time-to-live (seconds since object was added to the cache) or time-to-idle (seconds since object was accessed in the cache) expires. This is managed internally by Ehcache.
- The object was modified by Commerce Manager, Import/Export, or Data Sync, and message-based cache invalidation is supported for the object type.
Message-based cache invalidation is currently only supported for product objects, but support for this will be expanded to other object types in upcoming versions. When an object is updated, a message is sent to the ep.domain JMS queue. This is processed by the cacheEventMessageRouteBuilder Camel route in the Integration Server, which then sends a message to the ep.cache topic. All running services listen to this topic, and evict the object referenced in the message.
Messages on the ep.cache topic are formatted as follows:
{
"eventType": {
"@class": "CacheEventType",
"name": "PRODUCT"
},
"guid": "{PRODUCT_CODE}",
"data": {}
}
The diagram below shows the interactions between JMS queues and the Self-Managed Commerce services:

OpenJPA L2 Cache
OpenJPA uses ehCache as a backing store for its level 2 JPA cache.
The default Ehcache OpenJPA L2 cache configuration is defined in ehcache.xml in the commerce-engine/core/openjpa-osgi-wrapper module.
<defaultCache
maxEntriesLocalHeap="10000"
eternal="false"
timeToIdleSeconds="1"
timeToLiveSeconds="1"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
statistics="true"
/>
L2 Data Cache
The L2 (Level 2) data cache holds entity instances using their primary keys as identifiers. A separate cache is created for each domain entity with a cache name of the fully qualified class name (e.g. com.elasticpath.domain.catalog.impl.ProductImpl).
Entities will not be stored in the data cache if they have the @DataCache(enabled = false) annotation set. We recommend that the data cache be completely disabled for entities that are modified by shoppers, such as shopping cart entities.
The time-to-live for each data cache can be set in the following ways, evaluated from top to bottom:
- A timeout (in seconds) can be set in the data cache annotation as follows:
@DataCache(timeout = 3600) - An explicit
cacheentry can be specified for the entity using its fully-qualified class name. - The
openjpa.DataCacheTimeoutJPA property can be set for all entities. - If none of the above overrides are set, the
timeToLiveSecondsattribute of thedefaultCacheis used.
We recommend against using a data cache with more than a one-second timeout, as this can lead to dirty reads and inconsistent behaviour.
L2 Query Cache
The L2 (Level 2) query cache caches query results so that repeated queries with the same parameters can retrieve results directly from the cache rather than hitting the database again. The cache holds the list of primary keys for the entities that match the query criteria, not the actual entities themselves which are stored in the L2 data cache.
The OpenJPA query cache is configured by the following element:
<cache name="openjpa-querycache"
maxEntriesLocalHeap="10000"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="3600"
statistics="true"
/>
Configuration Example
Here is a sample configuration override file:
note
For information on Ehcache’s configurable properties, see Ehcache’s Cache Configuration guide.
<?xml version='1.0' encoding='UTF-8'?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd"
name="ep-default-cache"
updateCheck="false">
<!--
Read the Ehcache documentation for help on configuring this file.
The <defaultCache> configuration is used as the default for programmatically created caches.
<cache> entries will not inherit configuration from <defaultCache>.
-->
<defaultCache
timeToIdleSeconds="1"
timeToLiveSeconds="1"
maxEntriesLocalHeap="10000"
eternal="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
statistics="true"
/>
<cache name="openjpa-querycache"
timeToIdleSeconds="5"
timeToLiveSeconds="5"
maxEntriesLocalHeap="10000"
eternal="false"
statistics="true"
/>
<!--
Override application cache configuration.
-->
<cache name="baseAmountCache"
timeToIdleSeconds="3600"
timeToLiveSeconds="3600"
maxEntriesLocalHeap="10000"
statistics="true"
/>
</ehcache>
An example of overriding all caches can be found in the extensions/database/ext-data/src/main/resources/environments/default/files/conf/cache/ehcache-fast-updates.xml file.
Recommended Practices
Every commerce implementation is unique. No one configuration fits all. You will need to discover the configurations that work for your implementation. Keep in mind:
- Configure lower cache timeouts in development, QA and authoring environments where users want to see changes quickly and performance isn’t a primary consideration.
- Configure much higher timeouts for production and performance test live environments.
- Stress your application while using JMX console to monitor the number of cache requests. This will help you determine if the DB entities needs to be cached and for how long.
- Be cautious about increasing L2 cache timeouts. Complex domain object graphs, such a products or categories, can get out of sync when L2 objects are refreshed independently of each other.
- Keep conducting performance tests until the optimal results are achieved.
- Ehcache documentation contains comprehensive information about sizing caches.
Switchable Proxy Binder
Within the ep-core module, all service class beans that support application-level caching are defined using the com.elasticpath.commons.beanframework.SwitchableProxyFactoryBean class. SwitchableProxyBinder allows a bean definition to use the default non-caching implementation of a service unless a caching implementation is available.
For non-OSGi webapps (such as Integration Server), this allows us to optionally include the ep-core-caching dependency, and the bean definition will switch to the caching or non-caching service automatically, depending on whether the ep-core-caching dependency is present.
For OSGi webapps (such as Cortex), this allows us to reference a caching service from OSGi if it's available, otherwise use the default non-caching service defined in Spring. As a result, if the ep-core-caching bundle is unavailable or inactive, the non-caching service will be used.
How does it work?
Services with a caching override class declare their bean using SwitchableProxyFactoryBean as in the following example:
<bean id="ruleServiceProxy" class="com.elasticpath.commons.beanframework.SwitchableProxyBinder">
<property name="fallbackImplementation" ref="nonCachingRuleService"/>
</bean>
<bean id="ruleService" class="com.elasticpath.commons.beanframework.SwitchableProxyFactoryBean">
<property name="proxyInterface" value="com.elasticpath.service.rules.RuleService"/>
<property name="proxy" ref="ruleServiceProxy"/>
</bean>
The SwitchableProxyFactoryBean class implements the Spring Framework FactoryBean interface, so whenever Spring needs to inject this bean into another bean that requires it, the SwitchableProxyFactoryBean#getObject method is called. This method returns a proxy which delegates all method calls to the object returned by SwitchableProxyBinder#getProxy.
The SwitchableProxyBinder class returns the fallbackImplementation object unless a boundImplementation object has been set. The boundImplementation field is set by calling SwitchableProxyBinder#bindImplementation. This is called in one of two ways:
For non-OSGi webapps (such as Integration Server), if the ep-core-caching dependency is present, the cache-spring-overrides.xml calls bindImplementation for each SwitchableProxyBinder bean, as in the following example:
<bean factory-bean="ruleServiceProxy" factory-method="bindImplementation">
<constructor-arg ref="cachingRuleService"/>
</bean>
This causes the cachingRuleService to be used instead of the fallback nonCachingRuleService.
For OSGi webapps (such as Cortex), the core-blueprint.xml looks up an OSGi service for the RuleService interface, and when it's found, calls bindImplementation for the corresponding SwitchableProxyBinder bean, as in the following example:
<reference id="cachingRuleService"
availability="optional"
interface="com.elasticpath.service.rules.RuleService"
filter="(caching=true)"
timeout="1">
<reference-listener ref="ruleServiceProxy" bind-method="bindImplementation" unbind-method="unbindImplementation"/>
</reference>
This reference will find any OSGi service that implements the RuleService interface and has the caching=true property defined.
<service ref="cachingRuleService" interface="com.elasticpath.service.rules.RuleService" ranking="200">
<service-properties>
<entry key="caching">
<value type="java.lang.Boolean">true</value>
</entry>
</service-properties>
</service>
Extending services that use the Switchable Proxy Binder
If you need to extend a service with a bean that uses the SwitchableProxyBinder, you will also need to extend the corresponding caching service class and export it through OSGi.
Continuing the RuleService extension example, assume that you have already done the following:
- Created an
ExtRuleServiceinterface inext-corewhich extendsRuleServiceand declared your new methods. - Created an
ExtRuleServiceImplclass inext-corewhich implementsExtRuleServiceand extendsRuleServiceImpl. - Overridden the
ruleServiceLocalbean definition inextensions/core/ext-core/src/main/resources/META-INF/conf/ep-core-plugin.xmlto point to theExtRuleServiceImplclass.
Now you need to extend the caching service class and make it visible to the SwitchableProxyBinder:
Create a
CachingExtRuleServiceImplin theext-core-cachingmodule, which extendsCachingRuleServiceImpland implements theExtRuleServiceinterface.Declare a new bean for
cachingRuleServicein one of theext-core-cachingSpring configuration files, such asextensions/core/ext-core-caching/src/main/resources/spring/core-caching/ext-cache-example.xml. You may want to create a new XML file and import it inextensions/core/ext-core-caching/src/main/resources/spring/core-caching/plugin.xml.Declare an OSGi reference to the
nonCachingRuleServiceinextensions/core/ext-core-caching/src/main/resources/OSGI-INF/blueprint/ext-core-caching-blueprint.xml:<reference id="nonCachingRuleService" interface="com.elasticpath.extensions.service.rules.ExtRuleService" filter="(caching=false)"/>Export the
cachingRuleServicebean as an OSGi service inextensions/core/ext-core-caching/src/main/resources/OSGI-INF/blueprint/ext-core-caching-blueprint.xml:<service ref="cachingRuleService" interface="com.elasticpath.extensions.service.rules.ExtRuleService" ranking="200"> <service-properties> <entry key="caching"> <value type="java.lang.Boolean">true</value> </entry> </service-properties> </service>Override the
nonCachingRuleServiceOSGi reference inextensions/core/ext-core/src/main/resources/OSGI-INF/blueprint/ext-core-blueprint.xml:<service ref="nonCachingRuleService" interface="com.elasticpath.extensions.service.rules.ExtRuleService" ranking="100"> <service-properties> <entry key="caching"> <value type="java.lang.Boolean">false</value> </entry> </service-properties> </service>
After these changes, the ruleService SwitchableProxyBinder bean should use the caching implementation of the service automatically.
Determining if extensions are registered properly
Start up your webapp and check the console output. If any of the SwitchableProxyBinder beans are using the fallback implementation, you will see a warning similar to the following:
WARN: Using fallback implementation of 'com.elasticpath.service.pricing.PriceListAssignmentService'. This usually means that caching is disabled for this service. See https://documentation.elasticpath.com/commerce/docs/core/platform/cross-platform/ehcache.html#switchable-proxy-binder for more information.
However, if you see a follow-up information message similar to the following, then the warning can be ignored:
INFO: A binding implementation of 'com.elasticpath.service.pricing.PriceListAssignmentService' has been set. This usually means that caching was late-bound for this service, and the earlier warning can be ignored.
note
These log messages were enabled by a recent patch (Jul 2025). Make sure you are up-to-date on the latest patches for your version of Self-Managed Commerce.