Win a copy of Beginning Java 17 Fundamentals: Object-Oriented Programming in Java 17 this week in the Java in General forum!

Christian Garcia

Ranch Hand
+ Follow
since Jan 29, 2002
Cows and Likes
Total received
In last 30 days
Total given
Total received
Received in last 30 days
Total given
Given in last 30 days
Forums and Threads
Scavenger Hunt
expand Ranch Hand Scavenger Hunt
expand Greenhorn Scavenger Hunt

Recent posts by Christian Garcia

I agree with your comments 100%. I went over my code last night for quite a few hours re-analyzing the design. I came to the conclusion that having locking/unlocking occur on a local database shouldn't impact performance. Your point:

I could even argue that from a mainenance point of view, it is easier for someone to maintain code if the locking is always happening, rather than the maintainer needing to determine what mode they are in

falls in line with the existing implementation of my code.
Thank you for your input.
I designed my FlightModel class to serve as an adapter for an instance of the Data class. FlightModel provides the same functionality regardless of whether the "data" instance held within is created on the local machine (DataExtension) or returned by an RMI request (RemoteData).
My dilemma is that I cannot find a solution that will enable my code to circumvent the locking process that occurs when reading/writing/modifying the database when the Data instance resides on the local machine. The reason for this being that the adapter methods of the FlightModel are atomic regardless of the location of the Data instance.
Now, I could add conditional logic to check an "isRemote" flag that's tripped when an instance of RemoteData is returned. When this flag is false, the code would delegate to non-locking code that performs the read/write/modify actions. To me, this seems an ugly way getting around the locking issue.
Has anyone experienced this and/or successfully designed around the issue?
I'm guessing that by the time you call the close method you're remote object has already fallen out of scope and has been garbage collected. You can test this by implementing the Unreferenced inteface. Add code to the unreferenced function to handle any cleanup you may need to do.
Just a suggestion.
I failed to properly describe my Remote implementation. I didn't recreate the code of the Data class into a 'RemoteData' version. My implementation uses the Adapter pattern on the server-side.
I do use two interfaces: LocalData and RemoteData. RemoteData implements Remote to mark the object as a "remote" object. RemoteData implements LocalData and throws RemoteException for all of the methods. This relationship between LocalData and RemoteData is possible because IOException thrown in LocalData is the superclass of RemoteException thrown in RemoteData.
I apologize for omitting this information.
[ July 01, 2003: Message edited by: Christian Garcia ]
My advice is to separate the implementation of the Data class into Local and Remote versions. If you modify the Data class to implement the Remote Interface and extend UnicastRemoteObject, that will cause definite issues for you. One major pain will be how you handle exceptions.
A 'remote' object must throw RemoteException from all of its' methods. Having a 'local' version of Data throwing that type of exception doesn't really make sense.
You already have the 'local' implementation of the Data class. I create an instance of it directly on the local machine for my project typed as LocalDataInterface. On the RMI side I have a RemoteData class that performs the same actions as the Data class, but from a Remote context (i.e. through the RMI registry). I return instances of it as RemoteDataInterface.
Thank you for the reply.
I've reacessed the design of my user interface and found it lacking the level of quality I'm aiming for. Front-end development is definitly not my fort´┐Ż, so if anyone can point me to a website or other type of resource that has some visual examples of user interfaces I'd really appreciate it.
Wow, this thread blossomed into a fine debate
I followed a design in which, I too, use a static HashMap that is synchronized on when a client locks a given record. My remote implementation of the Data class implements the Unreferenced interface. Within the 'unreferenced()' method I perform a lookup against the HashMap to determine if the current instance of RemoteData is a key within it (Each element of the HashMap is identified as [RemoteData,RecordNumber]). If so, the key is removed and notifyAll() is called. In doing this, any lock that ClientA held before it died is released enabling ClientB to obtain the lock it was waiting for.
I can see using a WeakHashMap has the benefit of 'auto-magic' removal of 'dangling' key/value pairs when the GC runs. However, IMO using the Unreferenced interface provides control over exactly what happens when the remote object becomes invalid. I tested this with 50 or so threads all competing for the same lock on the same record. Intermittently, a thread would die without releasing the lock and the next thread would wait. It didn't take 20 minutes for unreferenced() to be called and the lock released.
[ June 20, 2003: Message edited by: Christian Garcia ]
Good points. I agree with the thought that doing more than implementing the unreferenced method isn't necessary. I was more or less trying to discover if there are ways around it. That's a moot point now.
The locking scenario I was using did indeed have a ClientA lock and have a ClientB try to lock on the same record. This was nothing more than a test. My implementation in the actual code will do as you described: lock, read, modify, unlock.
Thanks for your responses.
Regarding your points:

When you say, "all the while checking if HashMap.containsValue(recordNum)" I assume you mean that after this check, your thread uses wait() to allow the cpu do other things until another thread calls notifyAll(), right? ...

- Yes, the thread does use wait and calls notifyAll() once the existing lock has been released.

...I tested with unreferenced and it gets called after about fifteen minutes or so

- I let the app run for over 20 minutes and the lock still wasn't released. I'll review my code again to be certain there isn't an error.

How often will it happen that two clients want to lock the same record at nearly the same time and one of them crashes?

- It doesn't have to be simultaneous. If ClientA crashes after locking Record1 and unreferenced() hasn't been executed, ClientB could request the lock on Record1 15 minutes later and will still be waiting for the lock to be released.
Okay, the assumption is that unreferenced() will be called to perform code that unlocks the record that ClientA locked before crashing.
That said, my question then becomes how do you circumvent the dependency on the unreferenced method to perform the unlocking?
[ June 04, 2003: Message edited by: Christian Garcia ]
I'm working with this scenario:
RemoteData implements Unreferenced
ClientA requests and receives a reference to RemoteData
ClientA requests a lock on record 1
Record 1 is locked
ClientA crashes
ClientB requests and receives a reference to RemoteData
ClientB requests a lock on record 1
ClientB waits...
The question here is if the HashMap that stores the [ClientID|Record Number] is in a 'wait' state (because ClientA still has a "ghost reference" to record 1), all the while checking if HashMap.containsValue(recordNum), will the Garbage Collector still kick-in and clean up the dead reference to ClientA? If not, when will unreferenced() be called? There's no way the lock on record 1 can be resolved until unreferenced() is called which has code to release the lock.
This is certainly an important issue because a ClientB's request could essentially "hang" for an indeterminate about of time.
Has anyone figured this out?
[ June 03, 2003: Message edited by: Christian Garcia ]
I was just fishing for a "devil's advocate" response. The problems I've run into haven't all been figuring out how to get things to work, but figuring out "why" to implement functionality in particular way.
Thank you for your advice and for the patience. I'm doing my best at getting thru this challenge.
Good book. It's helped me out big-time.
Can you foresee any issues with this implementation?
So, what's happening is:
1. Each new instance of RemoteData is bound to the Registry at run-time.
2. ConnectionFactory returns the new instance of RemoteData.
Do I have this right?
So, given our explanation, only the ConnectionFactory is registered with the RMI Registry and returns instances of RemoteDataImpl.
Is this correct?
[ June 02, 2003: Message edited by: Christian Garcia ]