Multiple @Cacheable not working

2,165 views
Skip to first unread message

Matthieu MARC

unread,
May 17, 2011, 7:59:48 AM5/17/11
to ehcache-sprin...@googlegroups.com
Hi,

I am trying to use your great package, and I have a problem.

I have a Spring Component class LdapManagerImpl like :

@Component
public class LdapManagerImpl implements LdapManager {

@Override
@Cacheable(cacheName = CACHE_NAME_USER)
    public final User findUserFromId(final String id) { ... };

@Override
    @Cacheable(cacheName = CACHE_NAME_CONTACT)
    public Contact findContactFromDn(String dn) { .... };

...
}


When starting my application in eclipse with Tomcat6, I see that only the first method findUserFromId() is added :
DEBUG CacheAttributeSourceImpl:183 - Adding CACHE advised method 'findUserFromId' with attribute: CacheableAttributeImpl

and the next method are not explored : I added some trace in CacheStaticMethodMatcherPointcut.matches(Method, Class<?>), and my trace stopped after the first method is added.


My ApplicationContext.xml look like :

<ehcache:annotation-driven cache-manager="cacheManager" />
    
     <ehcache:config cache-manager="cacheManager">
        <ehcache:evict-expired-elements interval="60" />
    </ehcache:config>
   
     <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
        <property name="configLocation"  value="/WEB-INF/ehCache.xml"/>
    </bean>


What can be my problem ?

Do you need some more information ?

Thanks.

Matthieu MARC


Eric Dalquist

unread,
May 17, 2011, 11:00:26 AM5/17/11
to ehcache-sprin...@googlegroups.com
Spring only finds the first annotation on each class during context initialization. Essentially spring is just deciding if each bean needs to have a JDK proxy added to it. The first time you invoke findContactFromDn you should see another log message from CacheAttributeSource adding the advice.

-Eric

Matthieu MARC

unread,
May 17, 2011, 11:24:23 AM5/17/11
to ehcache-sprin...@googlegroups.com
ok.

this is two methods in my class :

    @Override
    @Cacheable(CACHE_NAME_GROUP)
    public Group findGroupFromDn(String dn) {
        try {
            System.out.println("findGroupFromDn(" + dn + ")");
            return ldap.findGroupFromDn(dn);
        } catch (Exception e) {
            return null;
        }
    }

    @Override
    @Cacheable(CACHE_NAME_USER)
    public User findUserFromDn(String dn) {
        try {
            System.out.println("findUserFromDn(" + dn + ")");
            return ldap.findUserFromDn(dn);
        } catch (Exception e) {
            return null;
        }
    }

my program : findUserFromDn("dn") ,findGroupFromDn("dn2"), findGroupFromDn("dn3") and findUserFromDn("dn")

my trace say :
findUserFromDn("dn")
findGroupFromDn("dn2")
findGroupFromDn("dn3")

so it is good because the second findUserFromDn("dn") trace does not occured, meaning result are fetched from cache.

If I rerun my program, I will have :
findGroupFromDn("dn2")
findGroupFromDn("dn3")

good because findUserFromDn("dn") is not called, but not good because findGroupFromDn are called.

Group and User class returned by the two function implements Serializable.

I did not manage to find why findGroupFromDn is not put in cache.

What must I looked for to resolv my problem ?

PS : I am trying with Spring 3.1 @Cacheable new annotation, and I have the same problem.

Matthieu MARC


2011/5/17 Eric Dalquist <eric.d...@gmail.com>

Nicholas Blair

unread,
May 17, 2011, 12:04:10 PM5/17/11
to ehcache-sprin...@googlegroups.com
Your output matches what I would expect.
Clarification:

Program calls:
1 findUserFromDn("dn")
2 findGroupFromDn("dn2")
3 findGroupFromDn("dn3")
4 findUserFromDn("dn")

Here's what happens:

Call #1 results in a cache lookup, and a miss (nothing in CACHE_NAME_USER cache with key "dn"). The method is invoked, and the result is placed in the CACHE_NAME_USER cache.
Call #2 results in a cache lookup, and a miss (nothing in CACHE_NAME_GROUP cache with key "dn2"). The method is invoked, and the result is placed in the CACHE_NAME_GROUP cache.
Call #3 results in a cache lookup, and a miss (nothing in CACHE_NAME_GROUP cache with key "dn3"). The method is invoked, and the result is placed in the CACHE_NAME_GROUP cache.
Call #4 results in a cache lookup, and a hit: cached result is returned.

If you change your Program calls to something like:
1 findUserFromDn("dn")
2 findGroupFromDn("dn2")
3 findGroupFromDn("dn3")
4 findUserFromDn("dn")
5 findGroupFromDn("dn2")
6 findGroupFromDn("dn3")

You'll see calls number 4, 5, and 6 result in cache hits. @Cacheable doesn't pre-emptively put things in the cache for you.

Matthieu MARC

unread,
May 17, 2011, 12:19:10 PM5/17/11
to ehcache-sprin...@googlegroups.com
I do not agree.

My web program findUserFromDn() and then for each group the user belong I call findGroupFromDn(). There is a second findUserFromDn() call because there is a second action which create the photo for the user.

When I call the web program for one user, I have :


1 findUserFromDn("dn")
2 findGroupFromDn("dn2")
3 findGroupFromDn("dn3")
4 NO findUserFromDn("dn") (System.out.println output I put in the code)

4 does not occured because the result have been placed in the CACHE_NAME_USER cache.

When I called it again to check group are in the cache, I have :

1 NO findUserFromDn("dn")
2 findGroupFromDn("dn2")
3 findGroupFromDn("dn3")
4 NO findUserFromDn("dn")

1 and 4 does not occured because the result is already in the CACHE_NAME_USER cache.

But group occured, because the result is not in cache CACHE_NAME_GROUP , no ?

How to see what is in the cache ?

Which method to trace to understand why group result is not put in the cache ?

Matthieu MARC


2011/5/17 Nicholas Blair <nichola...@gmail.com>

Eric Dalquist

unread,
May 17, 2011, 12:20:25 PM5/17/11
to ehcache-sprin...@googlegroups.com
And you don't see any logging related to "findGroupFromDn"?

-Eric

Matthieu MARC

unread,
May 17, 2011, 12:29:18 PM5/17/11
to ehcache-sprin...@googlegroups.com
I do not understand your question because I see logging related to findGroupFromDn, and I don't want to see it because it mean that an ldap request is done, meaning that the result is not picked from the cache.

Or must I looked for something else elsewhere ?

Matthieu

2011/5/17 Eric Dalquist <eric.d...@gmail.com>

Eric Dalquist

unread,
May 17, 2011, 12:34:25 PM5/17/11
to ehcache-sprin...@googlegroups.com
Sorry I meant logging from the ehcache-spring-annotations project related to findGroupFromDn

Matthieu MARC

unread,
May 18, 2011, 2:58:04 AM5/18/11
to ehcache-sprin...@googlegroups.com
I found some stuff.

I put some trace in com.googlecode.ehcache.annotations.impl.CacheStaticMethodMatcherPointcut.matches(Method method, Class<?> targetClass) like :

public boolean matches(Method method, Class<?> targetClass) {
       
        boolean ret = AdviceType.NONE != this.cacheAttributeSource.getAdviceType(method, targetClass);
       
        if ( targetClass.getCanonicalName().endsWith("LdapManagerImpl")){
            System.out.println(targetClass + " : " + method + " = " + ret);
        }
        return ret;
    }

When running, I have two trace :

LdapManagerImpl : findUserFromDn = true
LdapManagerImpl : getGroups = false

What is getGroups() ? it is a function which return a List<Group> the user belong. The getGroups() make a findGroupFromDn call on each DN of groups I got from the User object (List <String> user.memberof.getgroups()).

and after , I have all my findGroupFromDn() traces.

So, the pointcut does not reach the findGroupFromDn() method, only the the getGroups() method (his parent). If I add a @Cacheable on the getGroups() method, all are working, Group object return by the getGroups() are put in Cache, and a second call of getGroups() with same parameters look for result in cache (no more findGroupFromDn call).

Is it normal that a function called by another in the same class is not cacheable ? if I understand, Spring intercept only method call from another class ? (my action class call findUserFromDn and getGroups(), not findGroupFromDn()). For information, if a make a call on findGroupFromDn() from my action class, the trace show me : LdapManagerImpl : findGroupFromDn = true (so pointcut match)

here my ldapManagerImpl code :


    @Override
    @Cacheable(cacheName = CACHE_NAME_USER)

    public User findUserFromDn(String dn) {
        try {
            System.out.println("findUserFromDn(" + dn + ")");
            return ldap.findUserFromDn(dn);
        } catch (Exception e) {
            return null;
        }
    }

    @Override
    @Cacheable(cacheName = CACHE_NAME_GROUP)

    public Group findGroupFromDn(String dn) {
        try {
            System.out.println("findGroupFromDn(" + dn + ")");
            return ldap.findGroupFromDn(dn);
        } catch (Exception e) {
            return null;
        }
    }

    @Override
    @Cacheable(cacheName = CACHE_NAME_GROUP)
    public List<Group> getGroups(User u) {

        List<Group> groupes = new ArrayList<Group>();

        for (String dn : u.getMemberOf().getGroups()) {
            Group g = this.findGroupFromDn(dn);
            if (g != null) {
                groupes.add(g);
            }
        }
        return groupes;
    }


Thanks

Matthieu MARC

unread,
May 18, 2011, 5:08:41 AM5/18/11
to ehcache-sprin...@googlegroups.com
I found a solution

I create a LdapProxyManager interface which will proxy all LdapManager method call.

LdapManager is implementing only method which return one result (so not implementing getGroups())
LdapProxyManager is implementing all method and specialy those whicgh return list.
LdapProxyManager contains a LdapManager object to proxy all method which return one result.

So that, the getGroups() method implemented in the LdapProxyManager make call to findXXXFromDn() of the LdapManager, and each result of findXXXFromDn() are put in cache (@Cacheable method are in LdapManager).

Matthieu MARC


2011/5/18 Matthieu MARC <matthi...@gmail.com>

Eric Dalquist

unread,
May 18, 2011, 9:07:26 AM5/18/11
to ehcache-sprin...@googlegroups.com
You're correct, Spring only proxies external calls to an object. So calling method A from method B in the same class will never result in caching.

Adam Gent

unread,
May 18, 2011, 9:45:46 AM5/18/11
to Ehcache Spring Annotations
N.B. With the new AspectJ support and AspectJ turned on this is not
true.
The weaving is done inline so even methods in the same class will call
the @Cacheable code.
I can look into making the pointcuts so that the behave like proxies
but I have never done that before.

On May 18, 9:07 am, Eric Dalquist <eric.dalqu...@gmail.com> wrote:
> You're correct, Spring only proxies external calls to an object. So calling
> method A from method B in the same class will never result in caching.
>
> On Wed, May 18, 2011 at 4:08 AM, Matthieu MARC <matthieu.m...@gmail.com>wrote:
>
> > I found a solution
>
> > I create a LdapProxyManager interface which will proxy all LdapManager
> > method call.
>
> > LdapManager is implementing only method which return one result (so not
> > implementing getGroups())
> > LdapProxyManager is implementing all method and specialy those whicgh
> > return list.
> > LdapProxyManager contains a LdapManager object to proxy all method which
> > return one result.
>
> > So that, the getGroups() method implemented in the LdapProxyManager make
> > call to findXXXFromDn() of the LdapManager, and each result of
> > findXXXFromDn() are put in cache (@Cacheable method are in LdapManager).
>
> > Matthieu MARC
>
> > 2011/5/18 Matthieu MARC <matthieu.m...@gmail.com>
> >> 2011/5/17 Eric Dalquist <eric.dalqu...@gmail.com>
>
> >>> Sorry I meant logging from the ehcache-spring-annotations project related
> >>> to findGroupFromDn
>
> >>> On Tue, May 17, 2011 at 11:29 AM, Matthieu MARC <matthieu.m...@gmail.com
> >>> > wrote:
>
> >>>> I do not understand your question because I see logging related to
> >>>> findGroupFromDn, and I don't want to see it because it mean that an ldap
> >>>> request is done, meaning that the result is not picked from the cache.
>
> >>>> Or must I looked for something else elsewhere ?
>
> >>>> Matthieu
>
> >>>> 2011/5/17 Eric Dalquist <eric.dalqu...@gmail.com>
> >>>>>> 2011/5/17 Nicholas Blair <nicholas.bl...@gmail.com>
> >>>>>>>> 2011/5/17 Eric Dalquist <eric.dalqu...@gmail.com>
>
> >>>>>>>>> Spring only finds the first annotation on each class during context
> >>>>>>>>> initialization. Essentially spring is just deciding if each bean needs to
> >>>>>>>>> have a JDK proxy added to it. The first time you invoke findContactFromDn
> >>>>>>>>> you should see another log message from CacheAttributeSource adding the
> >>>>>>>>> advice.
>
> >>>>>>>>> -Eric
>
> >>>>>>>>> On Tue, May 17, 2011 at 6:59 AM, Matthieu MARC <
> >>>>>>>>> matthieu.m...@gmail.com> wrote:
>
> >>>>>>>>>> Hi,
>
> >>>>>>>>>> I am
>
> ...
>
> read more »
Reply all
Reply to author
Forward
0 new messages