views:

397

answers:

4

Is there a recommended way to synchronize Tomcat Servlet instances that happen to be competing for the same resource (like a file, or a database like MongoDB that isn't ACID)?

I'm familiar with thread synchronization to ensure two Java threads don't access the same Java object concurrently, but not with objects that have an existence outside the JRE.

edit: I only have 1 Tomcat server running. Whether that means different JVMs or not, I am not sure (I assume it's the same JVM, but potentially different threads).


edit: particular use case (but I'm asking the question in general):

Tomcat server acts as a file store, putting the raw files into a directory, and using MongoDB to store metadata. This is a pretty simple concept except for the concurrency issue. If there are two concurrent requests to store the same file, or to manage metadata on the same object at the same time, I need a way to resolve that and I'm not sure how. I suppose the easiest approach would be to serialize / queue requests somehow. Is there a way to implement queueing in Tomcat?

+1  A: 

Your external resource is going to be represented by Java object (e.g. java.io.File) in some way or another. You can always synchronize on that object if you need to.

Of course, that implies that said object would have to be shared across your servlet instances.

ChssPly76
And that further implies that there's some internal software to control creation of proxy objects that represent the contested resources. As it stands today there's nothing to stop multiple pieces of code from all creating their own File objects which all represent the same file. So you would have to have some kind of singleton factory that made sure there was only one object per contested resource.
John Munsch
@ChssPly76: no no no (what John said) -- if you execute this code { File f1 = new File("/usr/bin"); File f2 = new File("/usr/bin"); System.out.println(f1==f2); } you get false, and therefore synchronizing on one file has no bearing on whether a lock can be obtained on the other.
Jason S
@Jason - what part of "said object would have to be shared" implies that it's OK to create two copies of File?
ChssPly76
gack -- you have a point, how did I miss that :-(
Jason S
+1  A: 

Typically, your various servlets will be running in the same JVM, and if they're not, you should be able to configure your servlet runner so this is the case. So you can arrange for them to see some central, shared resource manager.

Then for the actual gubbinry, if plain old synchronized isn't appropriate, look for example at the Semaphore class (link is to part of a tutorial/example I wrote a while ago in case it's helpful), which allows you to handle "pools" of resources.

Neil Coffey
"So you can arrange for them to see some central, shared resource manager" -- meaning I should create a singleton of some sort?
Jason S
Yes, exactly -- create a singleton class with a static method getDatabasePool(), getCacheManager() etc that you call from any of your servlets.
Neil Coffey
If you're going to add that kind of bottleneck then he might as wel just use Tomcats SingleThreadedServlet (or something like that) strategy.
Gandalf
Whether the poster's planning to add a bottleneck or not I couldn't comment, but having shared resources between servers is a perfectly valid thing to do in principle.
Neil Coffey
A: 

IMO you're asking for trouble. There are reasons why things like databases and shared file systems were invented. Trying to write your own using some Singleton class or semaphores is going to get ugly real quick. Find a storage solution that does this for you and save yourself a lot of headaches.

Gandalf
I'm not trying to write my own, I'm just wondering what the recommended way to have separate, potentially concurrent Tomcat requests deal correctly with writing to a file or to MongoDB.
Jason S
and my answer is - It's not recommended.
Gandalf
+1  A: 

If you are running one tomcat server and all your servlets are on one context you can always synchronize on a java object present on that context class loader. If you are running multiple contexts then the "synchronization object" can not reside in any particular context but needs to reside at a higher level that is shared by all the contexts. You can use the "common" class loader in tomcat 6.0 documentation here to place your "synchronization object" there which will then be shared among all contexts.

rmarimon