aspose file tools*
The moose likes Developer Certification (SCJD/OCMJD) and the fly likes My design...getting cloudier Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


Win a copy of Soft Skills this week in the Jobs Discussion forum!
JavaRanch » Java Forums » Certification » Developer Certification (SCJD/OCMJD)
Bookmark "My design...getting cloudier" Watch "My design...getting cloudier" New topic
Author

My design...getting cloudier

Mikael Månsson
Greenhorn

Joined: Sep 20, 2009
Posts: 17
Hi!

I am working on the B&S assignment and I thought that I had made a clean design...until i started to change things..now I think I need to consult on what is a recommended java approach to the design.

I have a Database interface that is the interface to the database. There are 4 methods.

book
findByName
findByCity
listAll

I have a RemoteDatabase inteface that is the interface to the database using a remote client. It has exactly the same signature as Database, except for that all methods throw RemoteException.

book throws RemoteException
findByName throws RemoteException
findByCity throws RemoteException
listAll throws RemoteException

Then I have a Client interface that is the common client interface for the Controller to use (my model in mvc). Implementing classes of Client are RemoteClient and DirectClient, and client has also the exact signature as Database has. This is good, because the controller only needs to know that its working with a Client, but can be unaware of that it is a Direct or Remote client.

Now... what I earlier did is that the RemoteClient caught the RemoteExceptions and displayed an error to the customer, and did not propagegate them further, since that would break the Client interface (with expections). Then I figured... I think it is more correct to propage these errors all the way to the GUI, wrap them as ControllerExceptions in the Controller and catch and show in the GUI itself.

So... then I need the RemoteClient to actually follow the RemoteDatabase interface exactly...but the DirectClient to follow the Database (without the remoteexceptions). Now Client can no longer be an interface to these two...and I cannot use it nicely in my Controller class anymore.

Also I seem to have so many interfaces and classes that have the same setup of methods...

Client, RemoteClient, DirectClient, Database, RemoteDatabase, DatabaseImpl, RemoteDatabaseImpl.

Maybe I'm not completely off, but I feel a bit like i'm painted in a corner here.

What would be a proper java way of using the interfaces and classes?
Roel De Nijs
Bartender

Joined: Jul 19, 2004
Posts: 5594
    
  15

Hi Mickael,

Sounds to me your design is becoming really complex with a whole nuch of interfaces and classes.

In this thread you'll can find my approach. Maybe it could be of some inspiration.

Kind regards,
Roel


SCJA, SCJP (1.4 | 5.0 | 6.0), SCJD
http://www.javaroe.be/
Mikael Månsson
Greenhorn

Joined: Sep 20, 2009
Posts: 17
Hi Roel, thanks for you blazing fast answer

I see that you are into using only one client for both. I also want to keep it that way.

You had to make both businesservices throw remoteexceptions..even the local one, and your client has to catch them, even though you will never get one from the localbusinessservice. I guess there is no way out of this, if you want to keep your client unaware of what type it is.

May I ask..did you wrap these RemoteExceptions in your client to some other expections and propagated them up to the GUI, or did you handle them in your Client?
Roel De Nijs
Bartender

Joined: Jul 19, 2004
Posts: 5594
    
  15

Hi Mikael,

I guess there is no way out of this, if you want to keep your client unaware of what type it is.
That's true. And that you have to catch the RemoteException is quiet normal, because that's a consequence of not knowing which mode you are running your application in.

Some more info about how I designed my client, you'll can find here.

Kind regards,
Roel
Roberto Perillo
Bartender

Joined: Dec 28, 2007
Posts: 2268
    
    3

Howdy, Mikael!

Champion, just like you, in my assignment, I created a thick client (which, in my opinion, complicates things a little bit). But right now, I'm writing an article to a brazilian magazine and I'm proposing this approach. It's pretty similar with the approach proposed by Roel, with small variations. For instance, with the methods you listed, here's how you could organize your business layer:



The Services interface offers the methods to be consumed by the presentation layer. These methods are to be used in the TableModel or in ActionListeners, so the class that represents the main window needs to have a constructor that expects the Services interface and pass it to the ActionListeners and to the TableModel, so the services can be consumed there. An instance of your Database interface may be created with the parameters provided in each properties window, when the application is started, according to each property provided by the user. This way, your Services classes have constructor that expect the Database interface, and the main window has a constructor that expects the Services interface. This is dependency injection, and eases the reuse of these components in frameworks that offer this feature, such as Spring.


Cheers, Bob "John Lennon" Perillo
SCJP, SCWCD, SCJD, SCBCD - Daileon: A Tool for Enabling Domain Annotations
Andriy Pererva
Ranch Hand

Joined: Jul 19, 2009
Posts: 73
Hi, Roberto!

Thank you for your clear example of how to make remote services.

My question is about this piece of code:



In this example your RMIRemoteServices extends DefaultServices, not UnicastRemoteObject, as it is recommended in Andrew's book.
If I understand correctly, you are using UnicastRemoteObject.exportObject() method instead of inheritance?


SCJP 6.0(95%), SCWCD 5(94%), SCJD (working on B&S v.2.3.1)
Roel De Nijs
Bartender

Joined: Jul 19, 2004
Posts: 5594
    
  15

Andriy Pererva wrote:If I understand correctly, you are using UnicastRemoteObject.exportObject() method instead of inheritance?
I'm not Roberto, but I also didn't extend from UnicastRemoteObject (I even didn't extend from anything, just an implements) and just used the exportObject-method.

Kind regards,
Roel
Roberto Perillo
Bartender

Joined: Dec 28, 2007
Posts: 2268
    
    3

Roel De Nijs wrote:...and just used the exportObject-method.


That's the trick, Andriy! Just so you can have an idea, the implementation of the method startServer(int) could have the following lines:



This method is called when the application starts in server mode, and binds the RMIRemoteServices object to the Registry, so it can receive requests on the port passed as parameter to the method.
Mikael Månsson
Greenhorn

Joined: Sep 20, 2009
Posts: 17
Thank you for all your help. I think I've cleaned it up just a little.
Rodrigo R. Branas
Greenhorn

Joined: May 13, 2009
Posts: 9
Roberto,

Do you really think that is a good idea handle actions like start server inside the RemoteServiceImpl that should provide only business related actions like search and book contractors? I'm a little bit worried about the cohesion of that.

I created a class ServerManager that provides a method called startServer that create the registry and bind the RemoteService using a factory.

[]s


SCJA, SCJP, SCJD, SCWCD and SCBCD Certified.
Roel De Nijs
Bartender

Joined: Jul 19, 2004
Posts: 5594
    
  15

Hi Rodrigo,

I also had a seperate class for creating the registry and binding the implementation of the RemoteService. I think this is the better approach (high cohesion).

Kind regards,
Roel
Roberto Perillo
Bartender

Joined: Dec 28, 2007
Posts: 2268
    
    3

Hum... I guess this is one point where there is no right or wrong. At the same time I think it is OK to have a separate class to start the server, for instance, I also think that it is ok to have this logic in the class that implements the business logic. So, whatever you choose, it's ok. But, to keep it simple, I would prefer to have everything in the same class, just like the design I proposed above.
Kenneth Logan
Greenhorn

Joined: Dec 22, 2010
Posts: 21
I am working on my RMI connection code similar to this:

Roberto Perillo wrote:Howdy, Mikael!




I am ultimately wanting to eliminate duplicate code for both the RMI implementation and Local implementation. From my understanding, the DefaultServices IS the local implementation of Services and each method in the DefaultServices class throws the same exceptions as in the Services methods (ServiceException and RemoteException). However, is it okay that the DefaultServices methods throw RemoteException even though for a Local-connection, the RemoteExceptions will never be used?

Roberto Perillo
Bartender

Joined: Dec 28, 2007
Posts: 2268
    
    3

Howdy, Kenneth! In the name of the JavaRanch family, I'd like to give you a warm welcome!

I am ultimately wanting to eliminate duplicate code for both the RMI implementation and Local implementation. From my understanding, the DefaultServices IS the local implementation of Services and each method in the DefaultServices class throws the same exceptions as in the Services methods (ServiceException and RemoteException).


Well champ, there is a little trick there. The thing is that the DefaultServices can be used either locally or remotelly, so the DefaultServices class can be either local or remote, depending on the way the application is executed. This is an efficient way to avoid duplication of code. The way I proposed also promotes abstraction, since the window that receives the Services object does not know whether the application is being executed locally or remotelly.

However, is it okay that the DefaultServices methods throw RemoteException even though for a Local-connection, the RemoteExceptions will never be used?


Well champ, when executed locally, the RemoteException will never happen. This is just for us to be able to successfully implement an interface that extends Remote. Note that if the exception is in a method's signature in an interface, it doesn't mean that the concrete method has to throw it, but that it can throw that exception.
Kenneth Logan
Greenhorn

Joined: Dec 22, 2010
Posts: 21
Thank you for the warm welcome! You're right, just because it declares that it throws an exception, does not mean that it necessarily "has" to. And the DefaultServices is being used directly when in local mode (that is the concrete class) and then indirectly using the RMI-connection (because RemoteServices just inherits all of DefaultServices' methods). So this makes sense and I totally like this design.

Now, are there any tests that I can use to verify that my RMI connection works? I ran my server on my laptop, and the client on a desktop machine and did a few tests.
Test 1: Connect client to server using valid IP and valid Port number - seems as if it works fine (Client Window appears and JTable is populated).
Test 2: Connect client to server using invalid IP address, but valid Port number - "Failed to connect to the database" appears as expected.
Test 3: Connect client to server using vaild IP but invalid port number. - Client Window does NOT appear and the program is still running. Not what I expected-- I was expecting a "Failed to connect" message as in test case #2.

Roberto Perillo
Bartender

Joined: Dec 28, 2007
Posts: 2268
    
    3

Howdy, Kenneth!

Now, are there any tests that I can use to verify that my RMI connection works? I ran my server on my laptop, and the client on a desktop machine and did a few tests.
Test 1: Connect client to server using valid IP and valid Port number - seems as if it works fine (Client Window appears and JTable is populated).
Test 2: Connect client to server using invalid IP address, but valid Port number - "Failed to connect to the database" appears as expected.
Test 3: Connect client to server using vaild IP but invalid port number. - Client Window does NOT appear and the program is still running. Not what I expected-- I was expecting a "Failed to connect" message as in test case #2.


Well champ, the good thing is that if a client that is in the same LAN is able to connect to your server, then that's pretty much it. But how did you run these tests? How did you connect to your server with an invalid IP address? I think that you could do just one simple test that, if it passes, then everything is fine. It follows the design proposed a few posts above:



The essence of this test is to verify only if the server is started and if you are able to get it from the registry. If it passes, then I'd say that it is pretty much it (regarding server connection). Eventually, if you have some code in your server that stops it, then you can include the code that stops it in an @After method.
Kenneth Logan
Greenhorn

Joined: Dec 22, 2010
Posts: 21
Roberto Perillo wrote:Howdy, Kenneth!

How did you connect to your server with an invalid IP address?



Er, I was NOT able to connect with an INVALID IP address (this is my intended behavior). I just wanted to make sure entering an invalid ip address would pop up a message to the user saying something along the lines of it "failing the connection."

Thanks for the test case! I will try this soon!
Norbert Lebenthal
Ranch Hand

Joined: Sep 23, 2010
Posts: 74
Roberto Perillo wrote:



Hum, I've a different implementation of the RMIRemoteServices. Indeed, here you extend DefaultServices, which then requires its content to be serializable. It's quite a pity though because it's not strictly required so: indeed, from what I understood from RMI, the stub will be created through a proxy and only the proxy will be given away. This proxy then delegates, after transport, to the actual instance.

As such, in the proxy, carrying a proper Database object and all its content is a bit too much IMHO (requires Database to be serializable and potentially carries around way too much).

What about such an implementation:


As such, the Database isn't required to be Serializable and won't be transported around when a proxy is made. Sounds all good isn't it ? Maybe I'm missing something, but up to now I don't see that.

Side note: on my side I made this RMIRemoteServices extending UnicastRemoteObject, since one instance can be used through all clients. And on top of that I've no factory or the like: just the instance is put in the RMI registry, the clients directly go at it.

Roberto Perillo wrote:
The Services interface offers the methods to be consumed by the presentation layer. These methods are to be used in the TableModel or in ActionListeners


Isn't it rather the controller which should use them ?

EDIT: side question: what do you do if/when the configuration isn't proper. Error message and then stop or something cleverer with a new edit dialog being displayed as long as config wasn't good?
Roel De Nijs
Bartender

Joined: Jul 19, 2004
Posts: 5594
    
  15

My remote service implementation also has a reference to the Data class. This reference is not marked transient, nor does my Data class implement the Serializable interface. Simply because RMI works with stubs and skeletons, so not with the actual classes, the MyRemoteServiceImpl instance is not transported to the client.

When I take a look at the source code of the stub generated by rmic, I see a class which implements my remote service interface (2 methods implemented), but no reference at all of the Data class. Each method in this stub has a lot of hard-to-read code (we are fortunate that we don't have to write this code ourselves). Just do the same like me and generate the stub and skeleton classes (and keep the sources, use "-keep" option of rmic) and convince yourself

Norbert Lebenthal wrote:Isn't it rather the controller which should use them ?

Of course! Your controller will invoke the methods on your Services instance. The presentation layer will have a reference to the controller.
Roberto Perillo
Bartender

Joined: Dec 28, 2007
Posts: 2268
    
    3

Norbert Lebenthal wrote:Hum, I've a different implementation of the RMIRemoteServices. Indeed, here you extend DefaultServices, which then requires its content to be serializable. It's quite a pity though because it's not strictly required so: indeed, from what I understood from RMI, the stub will be created through a proxy and only the proxy will be given away. This proxy then delegates, after transport, to the actual instance.


Champion, only the objects that are transferred over the network have to be serializable. For the approach I proposed above, the Data class does not have to be Serializable, since it won't be transffered over the network. In this case, only the objects that are parameters or return types of the methods of your remote interface have to be Serializable. You don't have to mark the Data object in your Services class to be transient.

Norbert Lebenthal wrote:Side note: on my side I made this RMIRemoteServices extending UnicastRemoteObject, since one instance can be used through all clients. And on top of that I've no factory or the like: just the instance is put in the RMI registry, the clients directly go at it.


Well, it would also work this way if you didn't extend UnicastRemoteObject and did something like this.

Norbert Lebenthal wrote:Isn't it rather the controller which should use them ?

Well, today I'd say that a more appropriate approach would be to provide to the TableModel only the data that is displayed on the JTable, and not provide the Services object to it, just like I've shown in this other thread (which was started by you). Other than that, an ActionListener is a Controller. So, it can have a reference to the Services object. For instance, the listener of the Book Room button can have a reference to the Services object and, after verifying the data provided by the user, invoke the bookRoom method from the Services object.

Norbert Lebenthal wrote:EDIT: side question: what do you do if/when the configuration isn't proper. Error message and then stop or something cleverer with a new edit dialog being displayed as long as config wasn't good?


Well, you can just show a message saying what is wrong about the configuration and keep the dialog open for the user to provide the configuration data again...
Norbert Lebenthal
Ranch Hand

Joined: Sep 23, 2010
Posts: 74
I hope I wasn't appearing presumptuous :$

Back to the topic. In fact on the way to "explore" RMI I had exception due to my Service class not being serializable. Thanks to your post I've realized that it wasn't needed anymore.

I hadn't seen this bit:


thanks for the tip. However, I feel it a bit magic, esp. if the class remoted is called RemoteService (since I would look into it to figure out its usage). But that's quite personal I guess.


Well, today I'd say that a more appropriate approach would be to provide to the TableModel only the data that is displayed on the JTable, and not provide the Services object to it, just like I've shown in this other thread (which was started by you). Other than that, an ActionListener is a Controller. So, it can have a reference to the Services object. For instance, the listener of the Book Room button can have a reference to the Services object and, after verifying the data provided by the user, invoke the bookRoom method from the Services object.


Well, I wasn't aware that one could consider an ActionListener a Controller. Up to reading you I was under the impression that the controller should be a well defined class lol (which Roel looks like agreeing). Anyway, no big deal, thanks again for your interaction.

best
norbert
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: My design...getting cloudier