Design questions (with RMI)

I spent a lot of time with design and I am even not sure, that it is quite a good one. My Data class implements DataInterface (with all the public methods of Data class). I have an RemoteDataInterface which contains the public methods of Data class, but these throw RemoteException (or more). My RemoteDataImpl implemets RemoteDataInterface and adapt Data class methods to the interface methods. So, at the client side I will get RemoteDataInterface object which has no common interface (or ancestor) with Data class. Therefore, I use an adapter which implements DataInterface (the common interface) also to change back the methods with RemoteException to the local class methods. My factory class will produce an DataInterface type object independently it is working in local or remote mode. I did not listed here, but I plan to use Singleton for locking, also, but it fits to the adapter pattern.

My first problem that it seems to be a bit complicated and this double transformation (adapting)seems to me nonsense. The only reason why I use them, that I need a common interface...

I saw an another design, which has an RemoteDataIf (extends Remote and all public methods of Data). The LocalDataIf extends RemoteDataIf... What annoying me in this design that the local Data implements LocalDataIf and for this reason implements Remote... It is awkward for me that a local object without any networking implements Remote... (OK, I know it is just a marking interface without any need of method implementation.)

Could you please, comment these designs... and help me to find out its flaws.

Thank you all in advance. Ban

Andras Nemeth
Ranch Hand

Joined: Jul 31, 2001
Posts: 80

posted Aug 09, 2001 23:57:00

0

Hello, Isn't it enough clear or are these questions too specific? What is the reason of no answer? I do not want to write your design, just criticise mine. Thanks. Ban <email>nemeth_andras@yahoo.com</email>

Originally posted by Peter Gates:Why do you create an interface that have all the public methods of Data class? I didn't do that but extended the Data class. It works fine.

You can certainly get it to work, but it is misguided. On the client side, there are two different database components. One is, essentially, the Data class. It is a class to access a database contained in a local file. The other is a proxy to access a database exposed via the network. Remember that inheritance expresses an IS-A relationship. A "proxy to access a database exposed via the network" IS-A "class to access a database contained in a local file."? Well, no, obviously not. Apart from having the same interface (<blink><= keyword alert</blink>) they have absolutely nothing, zilch, zip, diddly squat in common. Inheritance is not the way to model them. That's the theoretical, object-modeling side of it. Now let's look at the practical side. Assume you're using RMI for remote database access. That means that you're having this server-side Remote object represented by a local proxy implementing the object's interface (<blink><= keyword alert</blink>). The design is kept as simple as possible[1] if the interface is kept as close to the Data interface (<blink><= keyword alert</blink>) as possible. In the most elegant designs, the two are identical -- in other words, the remote server object implements exactly the same interface as Data, and the RMI proxy class is directly interchangeable with Data. Obviously, this can only be done using interfaces as you have no control over the actual class of the RMI proxy. This is the practical expression of "the proxy" IS-NOT-A "local database class". With socket-based communication, you can extend Data and get it to work in a way that may not seem obviously wrong at first sight. But look again and you'll notice that every single method of Data is overridden in your proxy class, and no super.method() in sight. In other words, you're using none of the behaviour of the base class. This is a sure sign that there's something seriously wrong with the design, and again an expression of the fact that "the proxy" IS-NOT-A "local database class". Hope this makes sense. - Peter [1] Live by the XP mantra: do the simplest thing that can possibly work, but no simpler.

Peter den Haan
author
Ranch Hand

Joined: Apr 20, 2000
Posts: 3252

posted Aug 10, 2001 02:43:00

0

Originally posted by Andras Nemeth:I spent a lot of time with design and I am even not sure, that it is quite a good one.

Very good. Although I wasn't terribly familiar with either Swing or RMI when I started the assignment, I find afterwards that it's the time spent designing that was the most valuable of all.

My Data class implements DataInterface (with all the public methods of Data class). I have an RemoteDataInterface which contains the public methods of Data class, but these throw RemoteException (or more). My RemoteDataImpl implemets RemoteDataInterface and adapt Data class methods to the interface methods.

And you clearly feel there's something wrong with this. Trust your feeling. Take a step back. Forget everything about Data, RemoteDataImpl and all else. Forget implementation. You're writing a database client program, someone gives you a database interface to talk to, and tells you that there may be either a remote or a local database hiding behind it. What do you expect of this interface? For a start, you'd hope for methods to find, read and write database records. Much like the ones in Data, in fact. You would expect those methods to throw IOExceptions if there's something going wrong I/O wise, and database-specific exceptions if there's an internal problem. Much like the ones in Data... Also, because the database may be remote, you'd expect the methods to throw network communication related exceptions. If it's using RMI under the hood, that would be a RemoteException. Continuing the RMI line of thought, you take a look at the API and notice the Remote interface. You read the contract for Remote: The Remote interface serves to identify interfaces whose methods may be invoked from a non-local virtual machine. Clearly, if you'd be talking to a remote database, it might well be instanceof Remote; if you're talking to a local one, it certainly should not be instanceof Remote. Back to your design. You have a DataInterface that has the Data public methods, and a RemoteDataInterface that contains the same methods decorated by a throws RemoteException. Which of the two is the client application using to access the database? DataInterface. But that DataInterface does not meet the expectations outlined above; its methods cannot throw RemoteException. Instead of that, tthere's an adapter class that probably wraps any RemoteExceptions thrown into unchecked exceptions or exceptions that were already thrown by the Data class.

If RemoteExceptions are turned in unchecked exceptions, your application may ignore network communication problems, either intentionally or by accident. That's bad.

If you turn RemoteExceptions into exceptions already thrown by Data, you force yourself into an inconsistent approach that breaks down on the methods that don't throw an exception at all, like getRecordCount().

The adapter is cumbersome anyway.

I did not listed here, but I plan to use Singleton for locking, also, but it fits to the adapter pattern.

Don't. Remember that your remit is to build reusable classes. How many times have you seen a database with just one table?

I saw an another design, which has an RemoteDataIf (extends Remote and all public methods of Data). The LocalDataIf extends RemoteDataIf... What annoying me in this design that the local Data implements LocalDataIf and for this reason implements Remote...

Data implementing Remote is not just annoying, it's wrong; it's breaking Remote's contract, and in a larger application bugs may easily result because of this. But there's no reason to extend Remote! LocalDataIntf should not extend RemoteDataIntf, it should be the other way around. Look back at the list above describing the expectations you'd naively have of your database interface: it should define all the public Data methods, with a throws RemoteException clause, but not extending Remote. The Data class would implement this interface: Note that there is no reason for the getFieldInfo() implementation in Data to throw the full range of exceptions defined in the interface. It should not. After all, it's not a remote object, it shouldn't throw RemoteException. Then, if you're using RMI, you need a remote interface like: This will be implemented by the remote database. The above definitions meet all the expectations you'd have. The generic "remote-or-local" database interface may throw RemoteException, because its implementation may be remote. It does not extend Remote, because its implementation may also be local. The local implementation (Data) is essentially the unmodified Sun class and does not throw RemoteException at all. Hope this helps. - Peter

Hi, I have a design question in terms of handling exceptions. The various methods in the Data class throws DatabaseException. But, the Data class constructor alone does not throw DatabaseEXception, instead it throws IOException. Shall we change it to throw DatabaseException. And, We will be using wait() method somewhere in our Data class, mostly in lock() method. How are you guys handling the InterruptedException. Are you peoples handling it and throwing it as DatabaseException. And, what message are you guys placing as message string. Thanx. in Advance, Shankar S

Andras Nemeth
Ranch Hand

Joined: Jul 31, 2001
Posts: 80

posted Aug 10, 2001 04:05:00

0

Hello Peter, (den Haan) Thank you for the explanation of my bad feelings and precise explication of the your better solution. I am suprised that I missed this solution. I even thought all the aspect that you mentioned, but really put them together in wrong way. How do you ensure that you have only one lock object for that table? (I mean without Singleton pattern - it means it can be several instances, but only one for every single table.) I do appreciate your answer. Thanx again. Cheers, Ban

Peter den Haan
author
Ranch Hand

Joined: Apr 20, 2000
Posts: 3252

posted Aug 10, 2001 06:04:00

0

Originally posted by ShankarS:The various methods in the Data class throws DatabaseException. But, the Data class constructor alone does not throw DatabaseEXception, instead it throws IOException. Shall we change it to throw DatabaseException.

You could, if you think you've got a good reason for it. I didn't personally.

And, We will be using wait() method somewhere in our Data class, mostly in lock() method.

Well, locking calls for wait()/notify(). Whether that's in Data or somewhere else, that's up to you to decide.

How are you guys handling the InterruptedException. Are you peoples handling it and throwing it as DatabaseException. And, what message are you guys placing as message string.

You can make a case both for ignoring the InterruptedException ("don't support interrupting") and for propagating it back to the client as a DatabaseException ("something interrupted the lock, maybe a db shutdown process"). - Peter

Peter den Haan
author
Ranch Hand

Joined: Apr 20, 2000
Posts: 3252

posted Aug 10, 2001 06:08:00

0

Originally posted by Andras Nemeth:How do you ensure that you have only one lock object for that table? (I mean without Singleton pattern - it means it can be several instances, but only one for every single table.)

A 1:1 relationship isn't all that difficult to do. An instance variable would do the trick: Whether RemoteData is the appropriate place depends. In my design it wasn't as I had a separate RemoteData per client. In your design it may be fine. - Peter

Peter, etc..., if : interface RemoteDataInterface extends DataInterface, Remote { // that's all folks} and: class RemoteDatabase extends UnicastRemoteObject implements RemoteDatabaseInterface is that to say that the implementation of the public methods of DatabaseInterface in RemoteDatabase can be of the form : public publicMethodOfData() { return dta.publicMethodOfData(); } where dta is a local private instance variable of type Data Please let me know if I am way off line. Thanks in advance. Jon

Andras Nemeth
Ranch Hand

Joined: Jul 31, 2001
Posts: 80

posted Aug 10, 2001 12:30:00

0

Hello Jon, I guess you are on the right way. Anyway, it is called composition and delegation. Thank you all for clarifying the design. Chiao, Ban

Peter den Haan
author
Ranch Hand

Joined: Apr 20, 2000
Posts: 3252

posted Aug 10, 2001 15:40:00

0

Originally posted by Jon Trussler:is that to say that the implementation of the public methods of DatabaseInterface in RemoteDatabase can be of the form : public publicMethodOfData() { return dta.publicMethodOfData(); } where dta is a local private instance variable of type Data

Generally, yes, that'd be fine, although you would want to make exceptions. The locking code is the most obvious example, but you may also decide that, for example, if a networked client wants to write to a record but hasn't got a lock, you throw an exception or acquire a temporary lock behind the scenes. - Peter

This is great stuff. However, I am confused about one thing. We are supposed to write a DataClient that implements the very same methods as the DataObject. Logical thing is, class DataClient implements DataInterface { DataInterface dInterfacel public DataInterface(DataInterface d) { dInterface = d; } public void modify(DataInfo data) { dInterface.modify(data); } } Now, this modify method will delegate the call to the dataInterface it wraps. However, whats the point of this class when we can directly give the client a dataInterface to work with. This DataClient class cannot throw any new exceptions? What is the purpose of this DataClient if I used a factory to give me a DataInterface, isnt this just redundant? Please help.

Never be satisfied with anything less than the best and you will surely pass the test...

The Data class would implement this interface: Note that there is no reason for the getFieldInfo() implementation in Data to throw the full range of exceptions defined in the interface. It should not. After all, it's not a remote object, it shouldn't throw RemoteException. Then, if you're using RMI, you need a remote interface like:

Peter, maybe I'm missing something in this design approach but most of the Data methods are designed to throw DatabaseException or IOException !! Writing the following interface DataInterface { public FieldInfo [] getFieldInfo() throws RemoteException;

...won't work because the compiler will ask you to declare a throws clause for DatabaseException !! If you do so...it will complain again saying the method is redefined with a different throw clause !! I guess we have to pay the cost to write 2 different similar interface : one which throws DataBaseException or another one throwing RemoteException (for RMI implementation)

You don't need two similar interfaces. You can write the data access interface with specs like:

The Data class which implements this interface doesn't have to throw the RemoteException, but you want your remote data class to throw the remote exception. At least I believe that is the correct answer. Thanks, Todd Harney