Jan Vladimir Mostert Jan Vladimir Mostert - 5 months ago 10
Java Question

Spring Memcached annotations not Caching

I'm trying to get Memcache working in Spring.

I've setup a local Memcached server using Docker and Kitematic:

enter image description here

I can access the Memcached server using telnet:

telnet 192.168.99.100 32780
and then run
stats
or
stats items
(which only prints out
END
if the cache is empty);

My
pom.xml
:

<dependency>
<groupId>com.google.code.simple-spring-memcached</groupId>
<artifactId>simple-spring-memcached</artifactId>
<version>3.6.0</version>
</dependency>
<dependency>
<groupId>com.google.code.simple-spring-memcached</groupId>
<artifactId>spring-cache</artifactId>
<version>3.6.0</version>
</dependency>
<dependency>
<groupId>com.google.code.simple-spring-memcached</groupId>
<artifactId>xmemcached-provider</artifactId>
<version>3.6.0</version>
</dependency>


In my
applicationContext.xml
I have the following:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd">

<context:property-placeholder location="classpath*:META-INF/spring/*.properties"/>
...
<import resource="cacheContext.xml" />
...


In
cacheContext.xml
my config is as follow:

<?xml version="1.0" encoding="UTF-8"?>
<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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd">

<aop:aspectj-autoproxy/>
<cache:annotation-driven/>

<context:component-scan base-package="com.google.code.ssm"/>
<context:component-scan base-package="com.mycee.application"/>

<bean id="cacheBase" class="com.google.code.ssm.aop.CacheBase"/>

<bean id="readThroughSingleCache" class="com.google.code.ssm.aop.ReadThroughSingleCacheAdvice">
<property name="cacheBase" ref="cacheBase"/>
</bean>
<bean id="readThroughMultiCache" class="com.google.code.ssm.aop.ReadThroughMultiCacheAdvice">
<property name="cacheBase" ref="cacheBase"/>
</bean>
<bean id="readThroughAssignCache" class="com.google.code.ssm.aop.ReadThroughAssignCacheAdvice">
<property name="cacheBase" ref="cacheBase"/>
</bean>
<bean id="updateSingleCache" class="com.google.code.ssm.aop.UpdateSingleCacheAdvice">
<property name="cacheBase" ref="cacheBase"/>
</bean>
<bean id="updateMultiCache" class="com.google.code.ssm.aop.UpdateMultiCacheAdvice">
<property name="cacheBase" ref="cacheBase"/>
</bean>
<bean id="updateAssignCache" class="com.google.code.ssm.aop.UpdateAssignCacheAdvice">
<property name="cacheBase" ref="cacheBase"/>
</bean>
<bean id="invalidateSingleCache" class="com.google.code.ssm.aop.InvalidateSingleCacheAdvice">
<property name="cacheBase" ref="cacheBase"/>
</bean>
<bean id="invalidateMultiCache" class="com.google.code.ssm.aop.InvalidateMultiCacheAdvice">
<property name="cacheBase" ref="cacheBase"/>
</bean>
<bean id="invalidateAssignCache" class="com.google.code.ssm.aop.InvalidateAssignCacheAdvice">
<property name="cacheBase" ref="cacheBase"/>
</bean>

<bean id="incrementCounterInCache" class="com.google.code.ssm.aop.counter.IncrementCounterInCacheAdvice">
<property name="cacheBase" ref="cacheBase"/>
</bean>
<bean id="decrementCounterInCache" class="com.google.code.ssm.aop.counter.DecrementCounterInCacheAdvice">
<property name="cacheBase" ref="cacheBase"/>
</bean>
<bean id="readCounterFromCache" class="com.google.code.ssm.aop.counter.ReadCounterFromCacheAdvice">
<property name="cacheBase" ref="cacheBase"/>
</bean>
<bean id="updateCounterInCache" class="com.google.code.ssm.aop.counter.UpdateCounterInCacheAdvice">
<property name="cacheBase" ref="cacheBase"/>
</bean>

<bean name="cacheManager" class="com.google.code.ssm.spring.SSMCacheManager">
<property name="caches">
<set>
<bean class="com.google.code.ssm.spring.SSMCache">
<constructor-arg name="cache" index="0" ref="defaultCache"/>
<constructor-arg name="expiration" index="1" value="300"/>
<constructor-arg name="allowClear" index="2" value="false"/>
</bean>
</set>
</property>
</bean>

<bean name="defaultCache" class="com.google.code.ssm.CacheFactory" depends-on="cacheBase">
<property name="cacheName" value="defaultCache"/>
<property name="cacheClientFactory">
<bean class="com.google.code.ssm.providers.xmemcached.MemcacheClientFactoryImpl"/>
</property>
<property name="addressProvider">
<bean class="com.google.code.ssm.config.DefaultAddressProvider">
<property name="address" value="localhost:11211"/>
</bean>
</property>
<property name="configuration">
<bean class="com.google.code.ssm.providers.CacheConfiguration">
<property name="consistentHashing" value="true"/>
</bean>
</property>
</bean>


</beans>


I've created three different methods, each using different caching mechanisms:

@Component("cacheEndpoint")
public class CacheClass {

@Autowired
SSMCacheManager cache;

public String getDateTime1(String anything) {

SSMCache c = cache.getCache("defaultCache");

String s = c.get(anything, String.class);
if (s != null) {
return s;
}

Date d = new Date();
String response = d.toString() + " - " + d.getTime();
c.put(anything, response);

return response;

}

@Cacheable("defaultCache")
public String getDateTime2(String anything) {
Date d = new Date();
String response = d.toString() + " - " + d.getTime();
return response;
}

@ReadThroughSingleCache(namespace = "defaultCache", expiration = 15000)
public String getDateTime3(String anything) {
Date d = new Date();
String response = d.toString() + " - " + d.getTime();
return response;
}


}


To access it I do:

@Autowired
CacheClass c;

...

// caches perfectly
c.getDateTime1("test");

// doesn't do any caching
c.getDateTime2("test");

// doesn't do any caching
c.getDateTime3("test");


After placing runtime exceptions in
getDateTime2
and
getDateTime3
, it was established that the interceptors aren't being invoked.

Any idea what could be the cause of
@Cachable
and
@ReadThroughSingleCache
not doing their interception magic?

Update based on Matjaž Pečan's response:

CacheClass Interface:

public interface CacheClass {

public String getDateTime1(String anything);

public String getDateTime2(String anything);

public String getDateTime3(String anything);

}


CacheClass Implementation:

@Component("cacheEndpoint")
public class CacheClassImpl implements CacheClass {

@Autowired
SSMCacheManager cache;

public String getDateTime1(String anything) {

SSMCache c = cache.getCache("defaultCache");

String s = c.get(anything, String.class);
if (s != null) {
return s;
}

Date d = new Date();
String response = d.toString() + " - " + d.getTime();
c.put(anything, response);

return response;

}

@Cacheable("defaultCache")
public String getDateTime2(String anything) {

Date d = new Date();
String response = d.toString() + " - " + d.getTime();
return response;

}

@ReadThroughSingleCache(namespace = "defaultCache", expiration = 15000)
public String getDateTime3(String anything) {

Date d = new Date();
String response = d.toString() + " - " + d.getTime();
return response;

}


}


SOAP Endpoint where I'm testing the cache:

@Endpoint
public class PingEndpoint {

@Autowired
CacheClass c;

@ResponsePayload
@PayloadRoot(localPart = "PingRequest", namespace = "http://www.mycee.com/Application")
public PingResponse doPing(@RequestPayload PingRequest request) {

// caches perfectly
System.out.println(c.getDateTime1("test"));

// doesn't do any caching
System.out.println(c.getDateTime2("test"));

// doesn't do any caching
System.out.println(c.getDateTime3("test"));

}
}


cacheContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
<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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd">

<aop:aspectj-autoproxy proxy-target-class="true"/>
<cache:annotation-driven/>

...

Answer

There is an error in SSM 3.6.0, please downgrade to 3.5.0 to solve the issue or try to add

depends-on="cacheBase"

to defaultCache bean definition.

Update 1

Self invocations don't work. A call won't be intercepted and result won't be cached if a invocation is through this object. Make sure that method defined in a bean is invoked from another Spring bean.

Update 2

For SSM method has to be annotated as below:

@ReadThroughSingleCache(namespace = "defaultCache", expiration = 15000)
public String getDateTime3(@ParameterValueKeyProvider String anything) {
     ...
}

Still interceptors for some reasons aren't triggered.