@ Mark
The main question was: Should there be the possibility to identify a client or not. The answer in my opinion must be "yes", for the requirement: "If an attempt is made to unlock a record that has not been locked by this connection, then no action is be taken." If there would be no client identification, then it could not be under control who is the one trying to unlock a record.
So I decided for client identification. The next question with this decision in hand was: How to implement it. I had decided to use RMI as the network communication approach, so I could not rely on a certain
thread communicating with a certain client.
That was the reason why I had to implement something like a connection ID (it also could have been a locking ID, but with a locking ID one would have to manage more ID's, whereas with a connection ID the client server sight is better documented: one connection ID for each client connecting to the server; I also added some extra information in the ID, the time of the connection, so it would be easy to add some functionality like maximum connection time or to add some statistical functionality like a server side table with all clients connected and their time being connected etc.; nothing of this is implemented, for there have no requirements been for that, but it would be very easy to expand the server side functionality with this information provided.)
The next question was how to implement a good synchronization model. It would be a bad solution to synchronize on the whole database if one knows that just a certain record gets locked and that it would be enough to synchronize on this record. So on the one hand I needed a lock manager anyhow, on the other hand I had the solution with for the given problem with lock manager containing one locker object for each record - so these would be the objects to synchronize on! Not very difficult, and no more synchronization than needed.
An extra question was what should happen if a client tries to lock a record which is already locked. It seemed too poor for me just to return an exception saying "already locked", because locking should not take too much time (the normal sequenze is lock-manipulate-unlock, and this will not take more than some seconds). Ok, so I implemented a queue for each record (in the RecordLocker, of course), where a client can get in with it's ID. Only if also the queue is full, the server response with an exception.
With the idea of "locking will not take more than some seconds", it also made sense to guarantee this behaviour, on the one hand for the clients waiting in the queue, on the other hand answering the fundamental question what happens if a client locks a record and then dies without having unlocked the record before. So for each successful locking a CheckLockTimer will be instantiated, which will check after a certain amount of time if the record is still locked; in this case, the record will be unlocked, so the next client in the queue will get the lock. If the client first locking the record unlocks the record before the time given for locking, the CheckLockTimer gets canceled.
With all this in hand, the next problem also was quite easy (and nice) to solve: The locking of the whole database. If a client wants to lock the whole database, no other client may lock a record. So the trick is to try to lock all records, and if this is achieved, the whole database is ready to be locked by one client. So if a client wants to lock the whole database, BlockingThread's are started, one for each record. The LockManager who tries to lock the database then joins all these threads, and then the LockManager has locked the whole database.
I think it's a nice solution - what do you think?!
[ December 11, 2002: Message edited by: Detlev Beutner ]