Reputation: 44074
I have two applications communicating via RMI, a slave server (of which there will be multiple) and a master server.
Following good abstract design, I'd like to implement the slave in a way that it doesn't know that when talking to the master, it's using RMI (so that the two apps can also be run inside the same JVM, for instance):
public static void main(String[] a) {
Master ms = magicGetTheMasterFromRMI();
new Slave(ms);
}
...
class Slave {
public Slave(Master m) {
m.registerSlave(this); // PROBLEM LINE
}
}
Problem: The line marked PROBLEM LINE
above doesn't work, because I cannot simply pass this (the Slave
itself is a Remote
which Master
will talk back to). I have to explicitly do a UnicastRemoteObject.exportObject(this, 0)
(or toStub(this)
if it's exported earlier) but that makes the Slave
class depend on RMI, breaking the design.
Additionally, registerSlave
forces me to catch RemoteException
, which also adds RMI dependency.
What would you suggest to solve these issues?
(It also bugs me that these classes must implement Remote, but I guess we can only go so far with abstraction)
Upvotes: 1
Views: 588
Reputation: 310840
RMI needs explicit exporting of objects
Only if they don't extend UnicastRemoteObject or Activatable. If they do they are auto-exported on construction.
I have to explicitly do a UnicastRemoteObject.exportObject(this, 0)
No, see above.
(or toStub(this) if it's exported earlier)
Whatever toStub() is. There's a RemoteObject.toStub(), but you can't be calling that, and if you are you're wasting your time.
But you don't have to do that at all. If 'this' is an exported remote object you can just pass it around as an RMI parameter or result. RMI will substitute the stub automatically.
Upvotes: 1
Reputation: 555
Some part of your Slave application is going to have to be prepared to receive call via RMI, and deal with RemoteException, etc. Why not introduce a proxy of some sort between the Slave and Master that mediates the communication and hides the RMI vs local issue. Eg, along the lines of:
public Slave(Master m)
{
new MasterConnection(m, this);
}
class MasterConnection implements Slave extends UnicastRemoteObject
{
Slave s;
public MasterConnection(Master m, Slave s) throws IOException
{
this.slave = s;
try {
exportObject(this);
} catch (RemoteException e){
throw new IOException("Could not communicate with Master etc....");
}
master.registerSlave(this);
}
public void callbackFromMaster(Object arg) // or whatever
{
s.callbackFromMaster(arg);
}
}
Upvotes: 0
Reputation: 44074
Well, I've done it like this:
interface Service {
void doIt();
}
class ServiceImpl implements Service {
public void doIt() { ... }
}
interface RemoteService extends Remote {
void proxyDoIt() throws RemoteException;
}
class RemoteServiceHost implements RemoteService {
public void proxyDoIt() throws RemoteException {
// serviceImpl is a field, defined in the constructor
serviceImpl.doIt();
}
}
class RemoteServiceClient implements Service {
public void doIt() {
try {
// remoteService is a field, defined in the constructor
remoteService.proxyDoIt();
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
}
Upvotes: 1
Reputation: 19225
I'd be wary of this abstraction - remotely served requests are different from locally served requests in many ways - latency, failure modes, retry semantics.
It could be that your abstraction is leaky because its not really valid.
Upvotes: 0