views:

68

answers:

2

I have an application that uses ehcache for cache (but I think this problem is framework-agnostic), with a method interceptor so basically if I mark my method for caching something like this happnes:

public Object invoke(MethodInvocation mi) throws Throwable {
        Object result = cache.get(key); 
        //key comes from MethodInvocation processing
        if (result == null) {
            result = mi.proceed();
            cache.put(key, result);
        }
        return result;
}

So far so good. The thing I'm caching a method that returns an Array, and gets called like this:

List<Object> result = methodWithCaching();
result.add(new Object()); //!

As you can imagine, the line marked with ! also updates the cache instance, and this is not what I want.

Can someone think of a way to stop this behavior without modifying the client, only the interceptor?

A: 

What do you mean by "updating the cache"? Are you concerned that users can modify the List returned by methodWithCaching()? If so, I would propose that method return an unmodifiable collection. Or perhaps the cache could detect that the result is a Collection and wrap it with an unmodifiable wrapper.

Kevin
I want the users to do whatever they want with the list. But imagine this scenario: methodinvocation(1), cache miss, userupdates the lists and the cache gets updated too. methodinvocation(2), cache hit (now the list contains 1 element), user updates the list and cache gets updated too. methodinvocation(3), cache hit (now the list contains 2 elements), and so on.
Pablo Fernandez
Do you want the user to have a local copy of the cached object and not a shared instance?
Kevin
That's right, and possibly without modifying the client, just the interceptor
Pablo Fernandez
I would try to implement something using Cloneable as akhnaten suggested. You need to either return a copy of the data or a different data structure all together.
Kevin
I cant. Implementing `Cloneable` does not bring the `clone()` method to public visibility. I would have to implement that myself in __every single object I want to cache__
Pablo Fernandez
A: 

My understanding is that you want the interceptor to return a copy of the cached result, so that any client modifications would only affect the copy?

I can't think of a good and general way to do that though to be honest. Ugly ones would be to rely on clone(), or create a new List each time.

Sounds like you should really modify the client instead if at all possible.

Mirko Nasato
The thing is that I cant create a new object. I surely can't invoke `clone` since it's a protected method. And also the return value can be anything not just lists.
Pablo Fernandez
As I said there's no good solution. `clone()` is broken by design; you'd need to do `if (result instanceof Cloneable)` then invoke `clone()` via reflection.
Mirko Nasato