Which means that it uses sockets. You cannot use UnicastRemoteObject and still prevent RMI from opening server sockets. So you can't use this object in local mode, forcing you to do this:
Originally posted by xiao gao:
[...] I also make Data class extends UnicastRemoteObject [...]
Sounds like cut and paste galore to me... never a good way to get kudos in the OOP guru department.
At the same time I have another similar class called localData and ILocalData which haven't remote functions.
Bad move. Remember, you're coding for reuse. How many database-driven projects will only need one single database table?
I make the Data singleton instance.
How do you implement proper locking semantics if the locks only live on the client side?
In client side, I have a DataAccessObject [...] I also put lock book unlock process in that DataAccessObject.
Do the locks live on the server after all? Anyway, two things. First, a Vector is inappropriate for three reasons. (1) It is unnecessarily synchronized. (2) It is a misleading expression of the problem. A Vector, like any List, represents an ordered collection of possibly duplicate elements. Your locks are an unordered collection of unique elements, indicating a HashSet. (3) A Vector has O(n) performance for deletion and searching; a HashSet has O(1) performance, making it much more scalable. Second, there is in (my copy of) the assignment an requirement that duplicate calls to lock() should have no ill effect. How do you satisfy that without client ID?
I don't track user id, but just lock the record number in a vector within Data class.
The idea is fine, but (1) most prefer a class rather than an interface (2) if you use an interface, "public static final" is implicit and Sun's coding guidelines encourage you to omit it (I don't necessarily agree with this).
BTW, I have a interface called IndexOfFields, as it's name, I put all the index of fields array into that interface like "public static final int INDEX_ORIGIN = "3" [...] I wonder is this interface acceptable?
Consider implementing your networked RemoteData as either a subclass of, or a wrapper around, Data. Create an interface that both implement. The advantages are as follows. (1) One code base - no cut and paste. (2) A natural place to put the locking logic and everything else to do with multi-user handling: RemoteData. (3) The client can access both Data and RemoteData through the interface, probably without using any (I*Data) adapter objects.
Originally posted by xiao gao:
Yes, I just copy and paste the Remote Data to the local Data class, it really is not good way.
I can't remember the precise wording, but the instructions state that you are supposed to create an object that implements the same methods as Data. This doesn't make a lot of sense unless you interpret it as a requirement that the object implements the same interface as Data. Unless I'm misunderstanding your design, this element is missing.
DAO wrap public methods of IData and ILocalData with its own public methods, so it seems to use a proxy pattern. For example, if a client call the findCriteria in DAO, DAO will delegate findCriteria to IData or ILocalData findCriteria implementation depending on current network model.
Here I agree completely. As a consequence, the presence of lock() and unlock() in Data is nonsense if you are using an adapter anyway. Either scrap your adapter, or scrap the locking methods from Data. Can you justify the adapters?
though there is no need to keep locking in non-network model.
So the local implementation merely delegates (which is what you'd expect an adapter to do). The good thing about using adapters in networked mode is that you can satisfy the lock() and unlock() requirements without modifying the method signatures - either you delegate to server-side methods that take a client ID, or you filter out "erroneous" lock()/unlock() calls in the adapter. Either method is viable.
You can see, I put lock control client side but its real implementation is server side.
Three things. First, what happens if client #1 would call lock(10) again, even though it still has that lock? Similarly, what happens if client #2 would try calling unlock(10) even though it doesn't own the lock? Finally, be sure to take care over synchronizing this procedure. Keep your HashSet though - in this forum and elsewhere I have been recommending repeatedly to use unsynchronized collections because many developers are lured into thread-unsafe programming by the "safety" of Vector and Hashtable.
[...] another thread also want to book 10 flight, it will check Hashset , if find 10 in the HashSet, that thread will call wait until first thread finish booking and call notifyAll to waken waiting thread. So you see no need for client id.
Originally posted by xiao gao:
I have re-designed my project after studying the great advice from Peter den Haan.
Here is my design,
Server side, I just modified the Data class and put all its public methods in IData interface and make these method throw remoteException. I have another interface called IRemoteData which extends IData and Remote. I use a class called RemoteData implementing IRemoteData as remote server which wraps public methods in Data with its own public methods since two class share the same interface. I bind the RemoteData to the registry and make data transparant to the client. Simply, I still don't plan to track userId instead I use a Collections.synchronizedSet(new HashSet()) to return a thread-safe set to keep record number, if one client try to book flight 1, record number of flight 1 will be put into that set, if no same number in that set before, or this thread will wait until be notified.
I use a class called clientData which implements Idata interface. This class will use adapter pattern to get remote data reference or local data reference via different constructors. As to the local Data ,I user Data class directly by calling its public methods. clientData object will wrap remote or local data public methods with its own public methods for the same interface all of them.
As to the gui, I don't change a lot, still use GidBagLayout , combobox , JButton, JTable and JOptionPane. I use MVC pattern via AbstractTableModel to seperate the data from JTable, I also use Observer pattern to refresh both search and book data.
Above is my design , I am always looking forward to getting any great advice from all of you, Thank you again.