views:

1166

answers:

4

I'm trying to create a cache in a webservice. For this I've created a new Stateless Bean to provide this cache to other Stateless beans. This cache is simply a static ConcurrentMap where MyObject is a POJO. The problem is that it seems as there are different cache objects. One for the client beans, and another locally.

-CacheService
-CacheServiceBean
  -getMyObject()
  -insertMyObject(MyObject)
  -size()

-SomeOtherBean
 cache = jndiLookup(CacheService)
 cache.insertMyObject(x)
 cache.size() -> 1

After this assignment, if I call cache.size from inside the CacheServiceBean, I get 0. Is it even possible to share static singletons through beans? Finally I decided to use a database table, but I'm still thinking about this.

Thanks for your responses.

+3  A: 

As far as I remember you cant be certain that the stateless bean is global enough for you to keep data in static fields. There exist several caching frameworks that would help you with this. Maybe memcache?

EDIT: http://java.sun.com/blueprints/qanda/ejb_tier/restrictions.html#static_fields says:

Nonfinal static class fields are disallowed in EJBs because such fields make an enterprise bean difficult or impossible to distribute

svrist
A: 

When using stateless beans, you have no control of how many instances of them you have (that's up to your app server to take care of). You could have gotten the output from another bean than the one you looked up from your client. Did you print that in your log? In that case, you maybe should've seen more than one output. The point is that you can't know which instance you get when you look up a stateless bean via jndi (you just get one). And, you don't have state, so I don't know if this is the best choice for a cache.

By static singleton i suppose you mean a singleton object? Yes, it shouldn't be a problem to access a singleton through many beans. But remember the concurrency problems you'll probably get. The app server (beans in general) abstracts a lot from you.

Yngve Sneen Lindal
A: 
@Stateless
public class CacheSessionBean implements CacheSessionLocal {
    private static Map<String, Object> cacheMap = new HashMap<String, Object>();

    public Object getCache(String key) {
        return cacheMap.get(key);
    }

    public void putCache(String key, Object o) {
        cacheMap.put(key, o);
    }
}

The caveats regarding distribution of EJBs in a cluster apply to static variables. However, if you're not clustering, they pretty much don't apply to you, so statics are "okey dokey" at this level.

You WILL have synchronization issues.

One way to mitigate that is to configure your container to only create and pool a single instance of the CacheSession bean, then the container will manage that synchronization for you.

You could also manage the synchronization yourself, but you shouldn't do that at the EJB method level, rather you would likely be better having a synchronized Cache object (vs, say, a generic HashMap).

But the key take away is at this point, static variables are simply static variables.

In theory, you will need to be cognizant of the container lifecycle for your Session Bean (as it can potentially release all instances and thereby the actual bean class could be eligible for GC, thus potentially losing any static data). However, in practice, if the service is popular this is unlikely to happen. But, FYI it COULD happen.

Will Hartung
+3  A: 

On the surface, this seems like a contradiction in terms, as a cache is almost certainly something I would not consider stateless, at least in the general sense.

Ben Hardy
I was thinking the same thing!
Robin