How to remotely construct a remote object with RMI

  • Thread starter Screamin Lord Byron
  • Start date
S

Screamin Lord Byron

Hello,

I am learning RMI and I ran into this difficulty.

Let's say we have:

import java.rmi.*;

public interface Foo extends Remote {
public Bar constructBar(String name);
// other stuff
}

public interface Bar extends Remote {
public void mutate();
}

---------

import java.rmi.*;
import java.rmi.server.*;

public class BarImpl extends UnicastRemoteObject
implements Bar {
String name;
public BarImpl(String name) throws RemoteException {
this.name = name;
}

public void mutate() throws RemoteException {
name = "some new name";
}
}

---------

import...

public class FooImpl extends UnicastRemoteObject
implements Foo {

public FooImpl() throws RemoteException {
// some init
}

public Bar constructBar(String name) throws RemoteException {
return new BarImpl(name);
}

// other stuff

}


----------

Now I have some server and rmi initialization in the main class

----------

import...

public class Server {
public static void main(String[] args) {

// Construct Foo implementation, no problem
FooImpl myFoo = new FooImpl();

// Construct Bar Implementation
BarImpl myBar = new BarImpl(// oops, don't have the name,
// the client has it
);

// So I can bind Foo,
new InitialContext().bind("rmi:my_foo", myFoo);

// But can't bind Bar

}
}

I don't even know how many Bars there will be, and I can't just
serialize Bar and transfer a copy to the client, because I need all Bars
to be remote.

However, if I try to bind in constructBar() method of the Foo class I
get something like this on the client:

java.lang.IllegalArgumentException: illegal remote method encountered:
public abstract void Bar.mutate()

I suppose it's because there is no Bar in the RMI registry yet.

What can I do? I'm sorry if I'm asking nonsense, but how to make a stub
object for this Bar class? Am I doing it completely the wrong way?

I have no problem invoking methods of remote objects instantiated by the
server. That is to say -- if I remove this Bar business, everything else
runs as expected.

I've just started with RMI so (obviously) I don't know anything about it
yet. Can you please tell me is maybe Remote Object Activation right way
to look?

Thanks!
 
M

markspace

Screamin said:
I don't even know how many Bars there will be, and I can't just
serialize Bar and transfer a copy to the client, because I need all Bars
to be remote.

I don't know much about RMI either, although I might have some time to
give this some thought and make some test code.

Could you elaborate on the "I don't know how many Bars there will be"
part? What does that mean? Types of Bars, or different Bars with
different data but are all the same type? I think it makes a difference.

Couple of solutions occur to me, like a Proxy Pattern or a Type Object,
but I'd have to know a little more about the problem details.
 
T

Tom Anderson

public class FooImpl extends UnicastRemoteObject
implements Foo {

public Bar constructBar(String name) throws RemoteException {
return new BarImpl(name);

I have a vague idea you have to say something like:

return UnicastRemoteObject.exportObject(new BarImpl(name));

here. But i could be wrong.

tom
 
S

Screamin Lord Byron

I don't know much about RMI either, although I might have some time to
give this some thought and make some test code.

Could you elaborate on the "I don't know how many Bars there will be"
part? What does that mean? Types of Bars, or different Bars with
different data but are all the same type? I think it makes a difference.

There will only be instances of BarImpl class, and only methods declared
in the Bar interface will be used.

Couple of solutions occur to me, like a Proxy Pattern or a Type Object,
but I'd have to know a little more about the problem details.

I am making a little card playing game to do some practice with Java.
The game logic is done, and now it's time to make the server which will
host the game and the client for the players. I chose RMI just to get
myself more familiar with it (although I admit it may not be the optimal
choice).

This is the game. It's quite popular in some parts of my country (not
that it really matters for this discussion :) )

http://en.wikipedia.org/wiki/Belot

What matters is that players can't just play any card they like (they
must follow suit, etc.). The Player class has the hand field which
contains 8 cards (that class is what the BarImpl represents in my example).

The dealing part is done on the server. The idea is this. At the
begining of the game, all clients (one for each player) must send the
name of the player (and possibly some ID) by invoking some construction
code (something like constructBar() in my example), and it receives a
Player remote object from the server (which already contains his cards).

When player chooses a card to play, the server must check if it's a
valid move. Client uses a chooseCard(Card c) method of the Player class
which marks the chosen card and enables the game logic on the server to
check it and proceed if it's ok (remove it from hand). What I want to do
is to call player.chooseCard(card) from the client. Of course, this
means that the client's player object must not be a mere copy of the
server's player object, but it must be a remote object.

Well, that's the idea, and also the problem. :) Of course, it would be
easier to do it without RMI with some simple quickly-made-up
communication protocol, but I'm just curios is it possible to make it
work as I imagined with RMI.

The issues like how to notify all clients that the card is played and
such are not important right now. I have some idea on how to handle that.

And yes. Thank you for reading all this. :)
 
S

Screamin Lord Byron

I have a vague idea you have to say something like:

return UnicastRemoteObject.exportObject(new BarImpl(name));

here. But i could be wrong.

Thanks. I'll give it a shot, although I'm not sure how to tell the
client it's a remote object.
 
M

markspace

Screamin said:
What matters is that players can't just play any card they like (they
must follow suit, etc.). The Player class has the hand field which
contains 8 cards (that class is what the BarImpl represents in my example).

The dealing part is done on the server. The idea is this. At the
begining of the game, all clients (one for each player) must send the
name of the player (and possibly some ID) by invoking some construction
code (something like constructBar() in my example), and it receives a
Player remote object from the server (which already contains his cards).

When player chooses a card to play, the server must check if it's a
valid move. Client uses a chooseCard(Card c) method of the Player class
which marks the chosen card and enables the game logic on the server to
check it and proceed if it's ok (remove it from hand). What I want to do
is to call player.chooseCard(card) from the client. Of course, this
means that the client's player object must not be a mere copy of the
server's player object, but it must be a remote object.


The first thing that occurs to me is something like this:

interface BelotRmi {
Player getPlayer( String name );
void playCard( Card card );
}

where Player's chooseCard() method invokes the playCard method above,
which may or may not be a great idea. Let me think about this some more...
 
E

Esmond Pitt

public interface Foo extends Remote {
public Bar constructBar(String name);
// other stuff
}

This is a remote factory pattern, and Foo is the factory. You need to
register that in the Registry.
public interface Bar extends Remote {
public void mutate();
}

You don't need to create *any* implementations of BarImpl until somebody
calls constructBar(), and you never need to bind them to the Registry.

Done.

@Tom, you only need to call UnicastRemoteObject.exportObject() if the
remote object doesn't extend UnicastRemoteObject or Activatable.
 
M

Michal Kleczek

Screamin said:
Hello,

I am learning RMI and I ran into this difficulty.

Let's say we have:

import java.rmi.*;

public interface Foo extends Remote {
public Bar constructBar(String name);
// other stuff
}

public interface Bar extends Remote {
public void mutate();

add "throws RemoteException" here otherwise...
 
S

Screamin Lord Byron

This is a remote factory pattern, and Foo is the factory. You need to
register that in the Registry.


Yes. That's exactly what I need, but I haven't find a way how to do it
(register Foo as a factory). Is this done with some additional
parameters to the Context object?

For now I have something like:

Context initialContext = new InitialContext();
initialContext.bind("rmi:my_foo", myFoo);

I see that there is a constant InitialContext.INITIAL_CONTEXT_FACTORY.
Has that something to do with what I need to do? I suppose it hasn't, as
InitialContext() is just a factory for contexts.

Sorry if I'm asking dumb questions, but I only just started with RMI
yesterday, so there's still quite a mess inside my head.

Could you please give me some pointers on how to register Foo as a
factory in the Registry. I couldn't find any examples of it on Oracle's
site or my books (Core Java I and II - Horstmann/Cornell) either.

Some short example of the registration would be very helpful and
appreciated.

Thank you.


You don't need to create *any* implementations of BarImpl until somebody
calls constructBar(), and you never need to bind them to the Registry.

Good news. :)
Thanks.
 
S

Screamin Lord Byron

add "throws RemoteException" here otherwise...

:) Ha. It's the simple things. Thank you, Michal. That was in fact the
problem. I just didn't know how to read the exception I got.

Now it works. I get this for a newly constructed object on the client:

Proxy[RemotePlayer,RemoteObjectInvocationHandler[UnicastRef [liveRef:
[endpoint:[127.0.1.1:37325](remote),objID:[-9ab963d:129feecc4a1:-7ffc,
-7563478605164479550]]]]]

I suppose that is indeed a true remote object, and I didn't change
anything in the code apart from adding that throws clause.

Thanks once again.
 
S

Screamin Lord Byron

Some short example of the registration would be very helpful and
appreciated.

I guess this is not needed anymore, as it turns out it works like this
by default.


This remains a very helpful piece of information, as I'm now assured
it's safe to remove all of that unneeded Bar rmi registration nonsense I
wrote.

Anyways, thanks to all for clearing this up for me.

Cheers!
 
M

Michal Kleczek

Screamin said:
What matters is that players can't just play any card they like (they
must follow suit, etc.). The Player class has the hand field which
contains 8 cards (that class is what the BarImpl represents in my
example).n

The dealing part is done on the server. The idea is this. At the
begining of the game, all clients (one for each player) must send the
name of the player (and possibly some ID) by invoking some construction
code (something like constructBar() in my example), and it receives a
Player remote object from the server (which already contains his cards).

How do you handle client crashes? Do you want to rely on RMI DGC? Or maybe
you want to handle it explicitly.
When player chooses a card to play, the server must check if it's a
valid move. Client uses a chooseCard(Card c) method of the Player class
which marks the chosen card and enables the game logic on the server to
check it and proceed if it's ok (remove it from hand). What I want to do
is to call player.chooseCard(card) from the client. Of course, this
means that the client's player object must not be a mere copy of the
server's player object, but it must be a remote object.

Well, that's the idea, and also the problem. :) Of course, it would be
easier to do it without RMI with some simple quickly-made-up
communication protocol, but I'm just curios is it possible to make it
work as I imagined with RMI.

RMI is a pretty good choice IMO. But Jini is better :)
The issues like how to notify all clients that the card is played and
such are not important right now. I have some idea on how to handle that.

Is the client going to export some listener object and hand it to the server
so that it can use it for notification?

I think you should look at Jini (Apache River now). It has all the basic
infrastructure to handle distributed applications like this - namely:
leasing and remote events. Even better - your game would be a good candidate
to use a JavaSpace.
 
S

Screamin Lord Byron

How do you handle client crashes? Do you want to rely on RMI DGC? Or maybe
you want to handle it explicitly.

I'm not sure I understand what you mean by client crashes -- didn't get
to that point yet. :) I will rethrow all checked exceptions to the
client, and let client handle them. For example, I have one Game object
for each game (involving 4 clients), and it's a state machine. The state
dictates which methods are allowed to be called (for example, it's
illegal to call throwCard() if the state is "bidding", and so on)

I'm yet to discover what happens when connection breaks. It's a part of
the learning process. :) But it's a simple program and the clients are
dumb (all of the heavy lifting is done on the server), so I shouldn't be
in too much trouble and it should be fairly easy to debug.

Is the client going to export some listener object and hand it to the server
so that it can use it for notification?

Well, that would require the client to act as server (what I want to
avoid), but if nothing else will work then that's the way it will be. I
was actually thinking of blocking the call of one client/player until
the next makes its move, and then return that next player's move, and so
on. How would that work?

Something like:
while(!g.trickFinished()) {
if (g.isOnTurn())
if(!g.playCard(Card c))
continue; // bad card
else
played = c; // good card
else
played = g.checkPlayedCard(); // blocks until card from
// some other player is played
trick.add(played);
...
}

Server will make sure to report checkPlayedCard() 3 times for each
player not on turn per turn and it will be done in correct turn order
(possibly even return an ordered list or queue of all cards played in
the current trick).

This could also check for exceptions in case of client crashes. I'm just
not sure are remote calls that could block for a long time a problem for
RMI (will some kind of timeout occur?).

I think you should look at Jini (Apache River now). It has all the basic
infrastructure to handle distributed applications like this - namely:
leasing and remote events. Even better - your game would be a good candidate
to use a JavaSpace.

Thanks for the suggestion. I will certainly take a closer look at Jini
for some real projects, but I believe it could be a slight overkill for
this particular application.
 
E

Esmond Pitt

initialContext.bind("rmi:my_foo", myFoo);

initialContext.bind("rmi://localhost:1099/my_foo", myFoo);

You don't need the 1099 unless it isn't 1099 for some reason, but you do
need the hostname. However as for an RMI bind it must be localhost there
is no point in specifying anything else ;-) e.g. a real hostname.
 
S

Screamin Lord Byron

initialContext.bind("rmi://localhost:1099/my_foo", myFoo);

You don't need the 1099 unless it isn't 1099 for some reason, but you do
need the hostname.

If you don't specify the hostname I suppose it defaults to localhost, as
"rmi:my_foo" works just fine in my case.

However as for an RMI bind it must be localhost there
is no point in specifying anything else ;-) e.g. a real hostname.

That's if you're running the rmiregistry service that is part of the
JDK, as that service only allows binding calls from the same host.
However, RMI architecture allows for a more general RMI registry
implementation that supports multiple servers (Cay S. Horstman, Gary
Cornell: Core Java, Volume II - Advanced Features, 8th edition, page 853)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,902
Latest member
Elena68X5

Latest Threads

Top