I want to cache some reference data so that it is available to any stateless session bean instance executing in an EJB server.
Is there any reason that I shouldn't use an ordinary Java class with a static member variable to hold the cache?
Keeping cache data in static class members is a common pattern. Please consider that, the static data is managed by JVM. So if you have clustered configuration (many servers running on many machines), you may run into trouble trying to synchronize this data. This may limit system scalability.
Thanks Wojtech :-)
The cache will be read only.
When a new entry is put into the cache I'll need to synchronize the code section -- I believe the ejb standards discourage this. Is there any problem with using the synchronized keyword in ejbs, other than the obvious contention problems and reduction in scalability?
On another topic I think I have a solid understanding of a JVM -- i.e. I would know the answers to the questions I'm asking in the context of an ordinary multithreaded java program, or a servlet engine with a DB connection pool. Are there any issues with EJB servers which make them different?
The safe answer is, "maybe."
Since the EJB container is tasked with managing concurrency, you won't know how/when/where your code could screw up the containers threading until it's too late.
I would just make it a Singleton (a class with a private static instance variable of itself), and this will allow concurrent users without dealing with threads.
The problem with a singleton is that most app servers use customer ClassLoaders. If you use that singleton design pattern you will have one singleton per ClassLoader not per VM. You will find its not a singleotn at all.
Internet Applications Division
We implemented our read-only cache on an BMP entity bean.
To get a reference to the cache we uses the same known primary key, so we always get the same cache EJB.
>Since the EJB container is tasked with managing
>concurrency, you won't know how/when/where your code could >screw up the containers threading until it's too late.
Can having a class with a synchronized section of code (which *doesn't* call any EJB server code) possibly screw anything up?
>I would just make it a Singleton (a class with a private >static instance variable of itself), and this will allow >concurrent users without dealing with threads.
Won't I still need to synchronize access to the cache, as the data structure (e.g. Hashmap) may be inconsistent while a new entry is being added?
>The problem with a singleton is that most app servers use >custom ClassLoaders. If you use that singleton design >pattern you will have one singleton per ClassLoader not >per VM. You will find its not a singleton at all.
That would be OK for me -- the cache is read only, so worst case I use n times more memory and load each entry n more times.
Am I correct in saying that most (all?) EJB servers have a 'root' classloader which the other classloaders can see? If so perhaps I should ensure the cache class is loaded by the root classloader?
That seems like a good idea. I would like to avoid an EJB call on each cache access though (perhaps I'm just suffering from premature optimisation...)
Thanks for all your responses!
I cant speak for all app servers but I will speak for EAServer. At this time the only "root" loader we have is the system loader which wont help. As of 4.0 release we will support a cascading loader design which will allow you to load the singleton via the root loader.
Internet Applications Division
Don't forget multiple VM.
Due to the use of multiple VM/multiple classloader, it is
difficult to have real singlton in EJB.
>Can having a class with a synchronized section of code (which *doesn't* call any EJB server code) possibly screw
Be very careful about this..the container lose the
control of thread executing here, think about the
I would say use this design if it is absolutely read only.
Regarding synchronization, the EJB container should be viewed as a black box whose defining parameters come from the EJB spec, and/or the vendor's spec. Since the EJB spec says calling threads "from inside" an ejb container is a no-no, I'd stick with that.
With the singleton you may need to synchronize write functions, but as you said this is a read-only cache you probably won't need to.
The use of a BMP EJB as a cache is an idea we have been playing with. At present access is from stateless session beans and the first access from each invocation of a stateless session bean causes a reload from the database (so it's hardly a cache at all). We were now thinking of a static class that held the data from the BMP EJB, provided it to the stateless session beans and controlled when the database should be checked for changes.
Updates to the database are from an entirely separate system so we don't have issues about concurrency but do have issues about detecting updates.
Any opinions on whether this would work?
If your bean is marked as read-only you are OK. Set the re-read timeout to x and it will pick up changes every x seconds.
That is a polling approach. If you want to be event driven then you're stuffed since you can't ask the bean to delegate it's threading context to some asynchronous service.
Also, I could be wrong here, but if the server is using pessimistic concurrency then the BMP entity bean will have it's access single threaded won't it, even if it is read only? I know it will single thread if it isn't market read only, but the documentation on what happens if it is read only is not clear. If it single threads, it would rather defeat the object wouldn't it?
From what I read, the idea is to read the whole cache in during some thread safe event. From then on you want pure unfettered unsynchronized read access.
Most servers will simply refuse to build EJBs with the word synchronized mentioned in them so any synchronization has to go in your helper classes.
As Dave says, you can't ensure Singletons across VMs or in one VM for that matter so it's a big big cache then you have to be very very careful.
There is an interesting post elsewhere on theserverside which deals with a Read-Writer locking pattern which makes things a bit nicer.
You could propogate your cache using JNDI by rebind()ing it when you update it from some central location. Each server can periodically poll JNDI an update it's local copy. That would share the cache and I've seen it done to great effect.
Just my 2c worth.