I'm doing the URLyBird assignment and the more I rethink my design the more I'm afraid that it is bad.
I do want to use RMI as my network protocol and as the interface provided by SUN doesn't allow to be called remotely, I wrote my own interface with the same methods as in SUN's interface but also allow throwing IOExceptions. This allows a local implementation and a remote implementation to be handled the same on the client side, which I assume is good. But the code, for example, to book a hotel room then is something like:
.which I believe is problematic. Clients holding a lock leads to problems on the server as the client never might call unlock on the record, making the record unavailable to other clients which definitely is bad.
I believe the proper way to handle this, is to lock the record from the server code. But then this would imply to have a completely different API to implement against. For example, the client calls a book-method which is available remotely and local as well. The implementation then would do the locking internally.
But this approach makes it "far away" from SUN's interface. The only commonality this approach would have is the internal working (Server implements MyConnectorInterface and has a Data object to access the database).
What are you're comments, hints, suggestions on this issue?
Hello Axel, it looks to me you are thinking too much in depth.
From what I understand from your post you are going towards something like have an exact copy of Sun's provided interface have it throw IOException or RemoteException. This is doable but no good.
Think of it from the user perpective higher up more abstract. What do clients really want to do? ... Search and book room. So to start off your interface can simply have searchRoom and bookRoom methods. Inside those call the Sun's interface find method for search or lock/update/unlock for book.
Again these methods you do need to cater for local and remote modes. What to pass in to the searchRoom and bookRoom methods is up to you. Suppose Sun's interface takes a array for find you can pass in array for searchRoom method.
Since I'm working on URLyBird also, I have a Room object and pass this object back and forth. On my server side, I get the need parms like name, location etc from that object's getXXX.
I know that I will quickly be drowned out by those who feel that just calling business methods remotely is "good enough", and I am in the minority of those who disagree, so I thought I'd jump in with my comments. If you are interested in seeing why I think that having the client call the lock() method directly is correct, take a look at Should lock methods be callable by the client
There are 2 easy solutions to your current dilemma without requiring you to change your entire architecture:
Use a factory on the server side to provide instances of your remote interface that also implement Unreferenced. When your client disconnects the unreferenced method will be called (once the distributed garbage collector runs) and you can perform some checks to see if the disconnected client holds a lock.
Declare the problem of crashed clients "out of scope" in your design document - you will thereby have indicated that you have though about the issue, and (especially if you make a comment about a solution) indicate that you know what to do. All without changing a single line of code.
I found Andrew's book excellent for giving a grounding in what is required for the SCJD certification and how to approach the daunting task .
What I did differently is I used a 'thin client' approach for networking. I had a 3-tier architecture (gui, services and business layer).
My Services layer called a getServices() method dependant on run mode entered. In STANDALONE mode my getServices
method gets the Data singleton directly. For network clients when SERVER mode is entered I get a Data singleton which is
passed to RemoteServices for use in NETWORK-CLIENT mode. My ServicesImpl class which implements the Services
interface wraps IOExceptions in a ServicesException that is passed back to the gui. The ServicesImpl class is the only route
to the persistence layer from the gui and all exceptions that can be thrown are passed back from the 'service layer'.
My RemoteServicesImpl class implements the Remote interface and extends UnicastRemoteObject and because the
my Services Interface throws throws IOException there is no problem throwing a RemoteException here. This makes
exception handling very easy, and also makes the network layer very small. My Search() and Book() methods just
delegate to the Data singleton instance passed in SERVER mode when I run the application in NETWORK-CLIENT
mode (no run mode entered). When I call RemoteServices via RMI from Services for the client I cast the this back to Services.
Because of the 'thin client' approach and delegating to the Data class when entering SERVER mode, my network code
(the RemoteServicesImpl class) compiles to 3.57k.