How to use Spring’s cache in Alfresco

In this post, we are going to explain how to use Spring’s cache in Alfresco. To implement this feature could be very frustrating if some details are unknown.

First, you should change your context. If you have an amp, go to \src\main\amp\config\alfresco\module\alfresco-repo\module-context.xml
and add the following code to add the cacheManager bean:

<cache:annotation-driven/>
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" name="cacheName" />
</set>
</property>
</bean>

At this point, if you try to use the cache, you will get these exceptions:

org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException: Line 4 in XML document from class path resource [alfresco/module/alfresco-repo/context/service-context.xml] is invalid; nested exception is org.xml.sax.SAXParseException; lineNumber: 4; columnNumber: 8; cvc-elt.1.a: Cannot find the declaration of element 'beans'.
or
nested exception is org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException: Line 8 in XML document from class path resource [alfresco/module/alfresco-repo/context/service-context.xml] is invalid; nested exception is org.xml.sax.SAXParseException; lineNumber: 8; columnNumber: 112; Attribute "xmlns" must be declared for element type "beans".

So, secondly, comment this line

<!--  <!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'> -->

and replace the tag <bean> with

&lt;beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd"&gt;

Now, Spring’s cache is ready.

To use the cache with a particular method, the class must implement an interface, and we should add the tag @Cacheable(“cacheName”) to the method. For instance:

public class SecurityService implements ISecurityService {
...
/**gets group's permissions list
* The result is cached using the key (groupId).
*
* @param groupId
* @return
*/
@Cacheable("userService")//Cache this method in userService cache
@Override
public Set&lt;String&gt; getGroupPermissions(String groupId) {
...
}

Note: The cache is not used when a method is called within the same class. we will show a trick to solve this issue later.

It’s important to know that if we want to use our bean, we should inject it as an interface, otherwise we will get the following exception (Spring works with a proxy which implements the interface, therefore it can’t cast the implementations with the interface):

java.lang.ClassCastException: com.sun.proxy.$Proxy158 cannot be cast to info.SecurityService

To solve that, inject the bean as follow:

@Autowired
@Qualifier("id.bean.securityService ")
private ISecurityService securityService ;

Finally, if we want to use the cache with methods within the same class, we can apply a similar solution to this code:

@Service
@Transactional(readOnly=true)
public class SettingServiceImpl implements SettingService {

@Inject
private SettingRepository settingRepository;

@Inject
private ApplicationContext applicationContext;

@Override
@Cacheable("settingsCache")
public String findValue(String name) {
Setting setting = settingRepository.findOne(name);
if(setting == null){
return null;
}
return setting.getValue();
}

@Override
public Boolean findBoolean(String name) {
String value = getSpringProxy().findValue(name);
if (value == null) {
return null;
}
return Boolean.valueOf(value);
}

/**
* Use proxy to hit cache
*/
private SettingService getSpringProxy() {
return applicationContext.getBean(SettingService.class);
}
...