� quick questiuon for all you URLybirds out there.
My understanding of the lockCookie is that for each client,they are given a unique number to associate them with the locking of a articular record. Then -- when a new client comes along,hios lcokCookie number is compared to the lockCookie associated with the record.If these are different then the new client is locked out. How are we expected to allot a unique lockCookie to each client?
It is returned in the following method:
public long lockRecord(long recNo) throws RecordNotFoundException;
Must we write an algoritm in this method to return the lon lockCookie or what?
It is my understanding that lock cookie is generated for each transaction that needs a lock and not for each client. You can generate the lock by a simple combination of current time and the record number.
Correct me if this is wrong.
SCJP 1.6, SCJD, SCWCD, SCBCD.
Be nice to people on the way up cos, you'll need 'em on your way down - From somewhere I can't remember!
I was under the impressesion that 1 transaction = 1 client. Why wouldn't that be the case? If you have 2 calls from 2 different clients both trying to update a record, then thats 2 transactions - 2 threads of execution, right?
I've solved this by having a ClientToken object created for every client when they initially connect to the server. The client then uses this token as an argument when calling update/delete/create rmi methods. These methods each create a DataAccess (implements DBMain) instance with the ClientToken object as a constructor argument. Then when lock(int recNo) is called the token is used to identify the locking client and the recNo the locked record.
Will this not work?
Joined: Jun 09, 2004
I have some confusion on synchronization and locking, so I suppose this is as good a thread to post them in as any. First of all let me introduce you to my classes:
ClientToken: a token issued to the client when it first connects to the server. In all subsequent get/update calls to the server the token is passed as an argument to the server to uniquely identify the client.
DataFile: singleton class that maintains an in-memory cache of records as well as being responsible for reading and updating the data file.
RecordService: implements IRecordService which implents Remote. Provides methods for creating/updating/deleting records. In the implementation each method takes a ClientToken as an argument, as well as the necessary Record information. The methods then instantiate a DataAccess object, passing in the ClientToken to the constructor. The appropriate create/update/delete method is then called.
DataAccess: implements the DBMain interface. There is also a private static inner LockManager class (a single instance for all DataAccess objects). The lock() method takes a client token and a record number, identifying which client has locked which record.
No one else seems to have used a design like this, and I'm beginning to wonder if I've missed something important. At the moment only 3 of my DataAccess methods contain synchronized blocks; the lock(), unlock() and isLocked() methods which both synchronize on the single instance of the LockManager. So they look like:
I don't think anything else needs to be synchronized at all. Can anyone see any flaws with this?
Also, I have just realised that if 2 threads update the owner field on the same record, then (assuming the first thread gets the lock first) the owner field will be updated twice and the first update will be lost. Obviously this is a problem. How did other people solve this? I'm trying to keep buisiness logic out of the data access layer, so I don't really want to explicitly check in the update method if a record has been booked or not before I update that particular part of the String array. Any thoughts?
Joined: Jun 09, 2004
OK. After thinking for a bit I have come to the following conclusions:
1) The entire update() method needs to synchronise on the single instance of the LockManager. If it doesn't then the problem I mentioned in the last paragraph of the above post will occur. Following on from that, I can see no alternative but to put a check in at the start of the update method (within the synchronised block) that checks the record to make sure it has not been booked; I guess it throws some form of Exception if it HAS been booked.
2) The delete/create methods also need to be synchronised on the single instance of the LockManager, this is becuase deletion, creation and updating all call an update() method on the DataFile class. Unless these methods execute one at a time multiple threads will be attempting to update the file.
3) The sychronised blocks need to synchronise on a peice of static data all DataAccess objects share; the LockManager class (as a static inner class) is an obvious choice. I cannot simply make the create()/delete()/update() methods in DataAccess synchronised as this would lock only the individual instance of each class, not any of the shared data. By locking on the single instance of LockManager I ensure that only one thread at a time can perform an update()/delete()/or create().
I think thats fairly logical. If anyone can see any flaws in the reasoning (or even if you think its OK), please feel free to point them out.
I have to admit that my assignment doesn't use a lock cookie. But I think you can use any lock cookie that you want. The main choices I see are:
1. a unique lock cookie for each client
2. a unique lock cookie for each request
If you have one RMI server for each client (this is what the Habibi book does), then each client will get its own server object, so the server object or one of the objects it uses could create the cookie. If you use choice 1, you have to think about what happens if a client tries to lock multiple records (all using the same token), or if it tries to lock the same record multiple times. If you use choice 2, you don't have these worries, but maybe you are only trading them in for other worries
If you have one RMI server that all clients use, then you won't know which client is making the request, but you can use choice 2 by using a counter. (But, be sure to synchronize things properly in your counter.)
For those of us who don't have lock cookies, I think we are almost forced to use multiple RMI servers and choice 1 (only the cookie is not visible to the user of the Data interface, but we do need to keep track of which client is doing the locking). [ May 26, 2005: Message edited by: Lara McCarver ]
Joined: Dec 09, 2003
There is something about your design that I don't like. I don't like having two methods:
It would be better to have the LockManager have a lock(...) method which takes care of the logic of how to do the lock on its own, e.g. isLocked() and setLocked() are used inside of the LockManager's lock(...) method. That is the principle of encapsulation. An object should take care of its own state, in preference to letting other objects set its state. [ May 26, 2005: Message edited by: Lara McCarver ]
Joined: Jun 09, 2004
Thanks for the feedback Lara.
Hmm. I see your point. I didn't think it was a problem as the LockManger class is a static inner class and as such is not exposed in any way. Do you mean that you think the LockManager class should only have 2 (public) methods lock() and unlock()? That would mean doing any synchronization inside the LockManager class .. which I don't like - at the moment all the thread safety code is all confined to the DataAccess class.
Frankly the LockManager class ended up a lot smaller than I initially thought it might be; its just a HashMap that contains record numbers as keys (which are unique) and ClientTokens as the mapped object. I thinking of doing away with it altogether and having a private static HashMap instead..
Anyway, what I was most concerned about was this particular method in DataAccess:
I'm not sure about the try-catch part. As I understand it if the thread within the block is interrupted when wait() is being called this will result in an InterruptedException, right? In this case, I'm telling it to ignore the exception and test to see if a lock can be obtained again.
Joined: May 16, 2005
I have decided to never allow the client (GUI) to manage the cookie as I thought it increases the possibility to locking and dieing suddenly. So I have methods in the DBController that would acquire a lock, update or add or delete and will unlock it. For this reason I am having a unique cookie for every transaction. Though the interface has 'public void deleteRecord(long recNo, long lockCookie)', I intend not to use that, but use a 'public void deleteRecord(long recNo)'.
Regarding the update method - Jon, I agree with your reasoning. But I would check for a lock before the synchronization block inside the method. Here is what I have for update
To handle abnormal cases, in the lock manager class, I set a timer when a lock is set on a record and if even after timer ends, if the record is not unlocked I forefully unlock it. May be you should consider this also......