• Post Reply
  • Bookmark Topic Watch Topic
  • New Topic

Unreferenced without a lock manager - your ideas Max

 
Mike Southgate
Ranch Hand
Posts: 183
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Max;
as I've said before, I'm using the approach you suggest with respect to doing the locking within the Data class and using a HashMap within Data to track the locks. I'd like to trap the clients crashing while holding a lock so I've implemented Unreferenced in my server. Trouble is, even after there are no clients connected any more but there is a lock outstanding, unreferenced isn't being called.
I suspect it's because the unreferenced method works on references to the object it's a member of, in this case my server. Since the registry still has references to the server it doesn't get called. Actually, it wouldn't help much even if it did get called since I wouldn't know which lock to release.
I was thinking about having the server / localDataSource (they both form a wrapper around Data and implement an interface I specified) return a Lock object instead of a long. That way I could have the Lock class implement Unreferenced and it would get called when there were no more refs to it on the client side. Would that work? The Lock class would not extend UnicastRemoteObject, does that matter?
One last question, I've implemented a sorting algorithm in the table model. I think that would be very useful to any users but it does go beyond the requirements of the assignment. It obviously makes the table model more complex. Would it be better to take it out, do you think?
ms
 
Max Habibi
town drunk
( and author)
Sheriff
Posts: 4118
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi Mike,
Originally posted by Mike Southgate:
[QB]Max;
as I've said before, I'm using the approach you suggest with respect to doing the locking within the Data class and using a HashMap within Data to track the locks. I'd like to trap the clients crashing while holding a lock so I've implemented Unreferenced in my server. Trouble is, even after there are no clients connected any more but there is a lock outstanding, unreferenced isn't being called.

A few questions: what kind of Map are you using, and how are you storing elements in there? That is,recno-this, or this=recno? Also, which assignment do you have?

One last question, I've implemented a sorting algorithm in the table model. I think that would be very useful to any users but it does go beyond the requirements of the assignment. It obviously makes the table model more complex. Would it be better to take it out, do you think?
ms

I think it's useful too. I wouldn't do it, myself, but you need to put your signature on this thing, and let it express a bit of who you are. I don't think software developers are artists, but we can strive to be craftsmen. A good craftsman has a signature style, and this sounds like an expression of yours. If you decide that you want to do this, and it's easy, then I'd document the heck out of it, so your grader knows it's there and you credit for it.
All best,
M
[ July 22, 2003: Message edited by: Max Habibi ]
 
Mike Southgate
Ranch Hand
Posts: 183
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Max;
I'm doing the Bodgitt and Scarper Contractor assignment.
I'm using a HashMap :
private static HashMap lockedRecs = new HashMap();
and I populate it in the lock method of Data using the record number as the key:
cookie = new Long((long) (Math.random() * 10000000));
recNo = new Long(recNoIn);
//check the record isn't locked
if (lockedRecs.get(recNo) == null) {
lockedRecs.put(recNo, cookie);
ms
 
Mike Southgate
Ranch Hand
Posts: 183
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
BTW, I decided to pull out the sorting. I've given myself to the end of the month to have this thing submitted and I've still gotta figure out jar files and do the user documentation and double check my javadocs stuff is up to snuff. The sorting algorithm was very nice in itself (colections.sort) but it made the table model considerably more complex. The final straw was when it started throwing exceptions again this morning. It was working for several weeks...
ms
 
Max Habibi
town drunk
( and author)
Sheriff
Posts: 4118
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Does each gui client get thier own connection? And does each connection have a Data member variable?
 
Mike Southgate
Ranch Hand
Posts: 183
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I'm really not sure what you mean by 'connection' in this context. I'll explain how I've got things set up.
I've got my Data class which implements DBAccess, of course. I've also added a bunch of other methods to the data class (not part of the xfc) to provide other functionality you really need (like read the schema, get column count, get column names, get column sizes, etc. Basically Data does all of the file IO.
I wanted to change the DBAccess xfc to have it extend remote and throw remote exceptions but Sun won't let me, so...
I created another interface DataSourceXfc which extends remote and has the same methods as DBAccess except they throw RemoteException. Here it is:
public interface DataSourceXfc
extends Remote {

/** sequence number of the services column */
public static final int SERVICES_COLUMN = 2;

/** sequence number of the size column */
public static final int SIZE_COLUMN = 3;

/** sequence number of the rate column */
public static final int RATE_COLUMN = 4;

/** sequence number of the customer column */
public static final int CUSTOMER_COLUMN = 5;

/** int constant indicating record is NOT deleted */
public static final int RECORD_EFFECTIVE = 0;
/** int constant indicating record is deleted */
public static final int RECORD_DELETED = -1;

public String[] readRecord(long recNo)
throws RecordNotFoundException,
RemoteException;
public void updateRecord(long recNo, String[] data, long lockCookie)
throws RecordNotFoundException,
SecurityException,
RemoteException;

public void deleteRecord(long recNo, long lockCookie)
throws RecordNotFoundException,
SecurityException,
RemoteException;
public long[] findByCriteria(String[] criteria)
throws RemoteException;
public long createRecord(String[] data)
throws DuplicateKeyException,
RemoteException;
public long lockRecord(long recNo)
throws RecordNotFoundException,
RemoteException;
public void unlock(long recNo, long cookie)
throws SecurityException,
RemoteException;

public String getColumnName(int column, boolean rename)
throws IndexOutOfRangeException,
RemoteException;

public int getColumnWidth(int column)
throws RemoteException;

public int getTotalWidth()
throws RemoteException;

public int getColumnCount()
throws RemoteException;

public boolean isColumnEditable(int c)
throws RemoteException;

public ArrayList getData(String[] criteria)
throws RemoteException;

public boolean isUpdating()
throws RemoteException;

public String[] getUniqueValues(int column)
throws RemoteException;
} //interface
Then I created a class called LocalDataSource that implements DataSourceXfc. This is my class for getting to the datafile when it is local to the JVM, in other words, the server uses it to get data from the database and the clent uses it when connecting to a local database file ('alone' mode).
LocalDataSource has a member Data. all methods that are common to DBAccess and DataSourceXfc are implemented by simply calling the Data method of the same name. So, I have effectively created a wrapper for Data that does NOT extend remote but implements an interface that does.
My server is:
public class DbServer extends UnicastRemoteObject
implements DataSourceXfc,
Unreferenced {
and is a singleton. It has a member :
private LocalDataSource data = null;
data = new LocalDataSource(f); //in constructor
This gives the server access to the local file the same way local clients have access to it. Like LocalDataSource, the server implements the methods common to DBAccess and DataSourceXfc by simply calling the Data equivalent methods, except these methods have to throw RemoteException. Almost all of the methods in the server are duplicated in Data.
The clients is theoretically unaware of whether its data source is a local file or a server. The implementations of bith offer a common set of services so it doesn't matter. The client has a member:
private DataSourceXfc dataSource = null;
and has separate constructors to either connect to a local file or a server.
The local file constructor instantiates dataSource thus:
dataSource = new LocalDataSource(fileName);
The constructor for connecting remotely does this:
dataSource = (DataSourceXfc)Naming.lookup(hostAdrs + symbolicName);
So the client allways has to trap for RemoteException whether or not it connected to a local file. If it's connected locally, RemoteException will simply never happen.
So, to summarize, what I've done here is to create a common interface that both local and remote clients use. I also have two classes, one of which implements the interface locally (LocalDataSource) and one does so remotely (DBServer). The server, of course, also has a LocalDataSource member to actually get to the database file.
I hope this hasn't been too long winded, but I want this to be clear so we don't have to go back and forth too many times.
ms
 
Max Habibi
town drunk
( and author)
Sheriff
Posts: 4118
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
/*It sounds like a good, clean design, and I like it. I might recommend a factory that the client goes to, and that factory doles out connection objects(Remote or Local) but that's just nitpicking.

So, As I understand it, two gui clients will each have an instance of Data? That is, they will not be sharing the same instance of Data? If that's so, then you're almost done, with two minor modifications.
1. Simply add a private, non-static, member ArrayList to Data. Then, when you lock, insert the Integer that represents the recNo into that array list. When you unlock, remove that Integer.
2. Change your static HashMap to a static WeakHashMap.
That's it.
Here's how it works. If a GUI client holding onto a remote object(and by extension, a Data object) crashes, then the Distributed Garbage collection will claim that remote Data instance. When that happens, the Integer RecNo in Data.arrayList(the one we added in step 1) will be garbage collected too, because it's part of Data.ArrayList. When that happens, the weakHshmap will release the reference to that RecNO in Data that it's holding, because there are no references to it. And volia, the lock is released: the next time a client looks for that RecNo, they'll see that it isn't locked, and go on to lock it.
Now, if you'd rather implement the Unreferenced interface, that's also not hard. However, you'll have to create a separate RemoteFactory class that doles our RemoteData objects. That way, when a RemoteData object dies, it's reference count goes back to zero, and the Unreferenced method will be called.
HTH,
M
 
Mike Southgate
Ranch Hand
Posts: 183
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
So, As I understand it, two gui clients will each have an instance of Data? That is, they will not be sharing the same instance of Data? If that's so, then you're almost done, with two minor modifications.

No, Data is only used to actually do the reading and writing from the database file. The DbServer class wraps Data to make it accessible remotely. The client asks for the locks itself talking to the server.
The issue I have is that the Data class throws the SecurityException and it says it does the waiting when a record is already locked, and it passes a long as the lock, not a Long. I can't change that since Data implements DBAccess which was specified by Sun. If Data didn't throw the security exceptions I could just move the HashMap from Data and put it in the server as a WeakHashMap, and have the server do all the tracking of the locks, just have Data actually issue them.
But since the guy who's supposed to do the waiting and throwing of SecurityExceptions is hidden behind a long, I can't have him do it.
ms
 
Mike Southgate
Ranch Hand
Posts: 183
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I just had an idea. Change my DataSourceXfc to use a Long for the lock instead of a long. Have the server store these locks in a WeakHashMap. So, when a client dies the reference will be automatically removed from it. Also have the server implement Unreferenced. When Unref is called the server gets the locks from Data (as longs, of course) and compares them to it's own Longs. When it finds a long not in the weakHashMap it deletes the long in Data's HashMap. Not as elegant as your solution Max, not by a long shot...
ms
 
Max Habibi
town drunk
( and author)
Sheriff
Posts: 4118
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Mike Southgate:

No, Data is only used to actually do the reading and writing from the database file. The DbServer class wraps Data to make it accessible remotely. The client asks for the locks itself talking to the server.
ms

Yes, but each client gets it's own DbServer, and each DbServer has it's own instance of Data, correct? So indirectly, eah client their own Data, because each client has their own DbServer? Or am I missing it?
M
 
Mike Southgate
Ranch Hand
Posts: 183
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
One of us missing something. There is only one server running. When RMI connects to the server it doesn't start a new server instance, it uses the existing one. Since RMI is implicitly multi-threaded, each client accesses the same server on a different thread. There is only one server and thus only one Data instance. Since Data does the file io, you'd have file concurrency issues with the OS if this weren't the case. True? In any event I've set up the server as a singleton.
ms
[ July 24, 2003: Message edited by: Mike Southgate ]
 
Max Habibi
town drunk
( and author)
Sheriff
Posts: 4118
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Ok, I'm with you now.

I just had an idea. Change my DataSourceXfc to use a Long for the lock instead of a long. Have the server store these locks in a WeakHashMap.

Help me walk though this. GUI client #1 wants to lock record rec#1. So the DbServer stores a Long(recnNo) in a weakhashmap, then locks recNo in the Data.hashmap? Are you going to change the signature of the Data.lock method to take a Long, or just extract the Long from the dbServer and pass it along?

So, when a client dies the reference will be automatically removed from it.

I don't that will work, unless you return the Long to the client. Remember, there's a single instance of dbServer for all of your GUI clients, so that fact that a single GUI client dies doesn't kill dbServer.

Also have the server implement Unreferenced. When Unref is called the server gets the locks from Data (as longs, of course) and compares them to it's own Longs.

Let's take a second and talk about the nature of Unreferenced. unfortunately, it's name isn't as descriptive as you would hope. That is, it's not called every time a reference is removed. It's called when all references are removed. Thus, GuiClients 1,2, and 3 will all have a reference to dbServer. Unreferenced will not be called on dbServer just because gui client #1 dies. It's only called when all clients die, and the reference count of dbServer is reduced to 0.

When it finds a long not in the weakHashMap it deletes the long in Data's HashMap. Not as elegant as your solution Max, not by a long shot...

oh please, you're making my bald head blush
But seriously, I wouldn't worry about Unreferenced in your case. Given your architecture(which is nice, clean, simple architecture that I like btw: no need for a stealth helicopter when good car will do), I'd skip it.
All best,
M
 
Mike Southgate
Ranch Hand
Posts: 183
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Help me walk though this. GUI client #1 wants to lock record rec#1. So the DbServer stores a Long(recnNo) in a weakhashmap, then locks recNo in the Data.hashmap? Are you going to change the signature of the Data.lock method to take a Long, or just extract the Long from the dbServer and pass it along?

No, the way this would work is the server calls Data's lock method. It stores a long in a HashMap and returns the long to the server. Now, the server converts the long to a Long, saves it in the WeakHashMap and returns the Long to the client. Now, when the client dies, the only reference to the Long outside of the WHM is removed so the server's unreferenced method should be called. It calls a method in Data to get all of Data's locks and compares them to the contents of the WHM. Any that aren't found can be deleted out of the WHM.
ms
 
Max Habibi
town drunk
( and author)
Sheriff
Posts: 4118
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi Mike,

Originally posted by Mike Southgate:
[QB]
No, the way this would work is the server calls Data's lock method. It stores a long in a HashMap and returns the long to the server. Now, the server converts the long to a Long, saves it in the WeakHashMap and returns the Long to the client. Now, when the client dies, the only reference to the Long outside of the WHM is removed

So far, you're absolutely right. This would work exactly as described.

so the server's Unreferenced method should be called.

Why should the Server's Unreferenced method be called? Remember, the only thing that can trigger an all to Unreferenced is when the number of remote GUI clients drop to 0(because the remote clients hold a reference to the Server). That hasn't happened, because only a single one of n GUI clients has died. Thus, Unreferenced won't be called, unless that just happened to be the last client.
Remember, the Server implements Unreferenced, which we know only gets called when there are no more references to the Server. Imagine that there are currently three GUI clients who are holding the server. The fact that a given client dies is not sufficient to trigger Unreferenced, because there are still two more clients.
My advice is, don't trust me: I'm a shady character anyway. Fact refutes logic every time. Code it up
M
 
Mike Southgate
Ranch Hand
Posts: 183
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I don't need to trust you. Eventually I figure things out even if it does take 3x as long as it should. You're right unreferenced won't get called. I think I'll take your advice and leave well enough alone. But if I fail over this I'll be some (but not at you).
ms
 
Max Habibi
town drunk
( and author)
Sheriff
Posts: 4118
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Mike Southgate:
I don't need to trust you. Eventually I figure things out even if it does take 3x as long as it should. You're right unreferenced won't get called. I think I'll take your advice and leave well enough alone. But if I fail over this I'll be some (but not at you).
ms


I don't think you'll fail: as a matter of fact, I think you'll do well. However, I'm glad you won't be mad @ m .
All best,
M
 
  • Post Reply
  • Bookmark Topic Watch Topic
  • New Topic