In this program there is a single remote server which performs miscellaneous work. Because there is only one server, it seems like a singleton pattern could fit well.
The problem that a distributed program introduces is that every client will have its own version of a static
variable. Therefore I have defined a basic ComputeInterface
which is implemented by two classes. ComputeServer
is the object which performs the work asked for by clients. ComputeService
is a class which performs an RMI lookup to the ComputeServer
and acts like singleton server in the client program.
The location information for the Server is maintained in a Configuration
class:
public class Configuration {
public static final int COMPUTE_REMOTE_PORT = 8900;
public static final String COMPUTE_REMOTE_ID = "COMPUTE_REMOTE_ID";
public static final String COMPUTE_REMOTE_HOST = "localhost";
}
A ComputeTask
implements Serializable
and is performed by the ComputeInterface
which implements Remote
. The ComputeInterface
contains an isConnected
method which is used for determining if there is connectivity to the server from the service:
public interface ComputeTask extends Serializable{
void doWork();
}
public interface ComputeInterface extends Remote {
ComputeTask compute(ComputeTask task) throws RemoteException;
boolean isConnected() throws RemoteException;
}
This is the implementation for the server:
final class ComputeServer extends UnicastRemoteObject implements ComputeInterface {
public static void main(String[] args) throws RemoteException, AlreadyBoundException {
ComputeServer computeServer = new ComputeServer();
Registry registry = LocateRegistry.createRegistry(Configuration.COMPUTE_REMOTE_PORT);
registry.bind(Configuration.COMPUTE_REMOTE_ID, computeServer);
System.out.println("Compute Server Running");
}
private ComputeServer() throws RemoteException {
super();
}
@Override
public synchronized ComputeTask compute(ComputeTask task) throws RemoteException{
task.doWork();
return task;
}
@Override
public synchronized boolean isConnected() throws RemoteException{
return true;
}
}
And here is the Service
. If the server has not yet been connected, the service will attempt to do a registry lookup on the server. If the registry lookup up was successful (or has already been performed), it will check the server connectivity before performing any tasks.
final public class ComputeService implements ComputeInterface{
public static final ComputeInterface service = new ComputeService();
private static ComputeInterface computeServer;
private ComputeService(){}
@Override
public synchronized boolean isConnected() throws RemoteException{
return ComputeService.connected();
}
private static boolean connected(){
if (computeServer == null) {
try{
Registry reg = LocateRegistry.getRegistry(Configuration.COMPUTE_REMOTE_HOST, Configuration.COMPUTE_REMOTE_PORT);
computeServer = (ComputeInterface) reg.lookup(Configuration.COMPUTE_REMOTE_ID);
System.out.println("New Connection to Compute Server");
}
catch (RemoteException | NotBoundException e){
System.err.print(e);
computeServer = null;
return false;
}
}
try{
return computeServer.isConnected();
}
catch (RemoteException e){
System.err.print(e);
computeServer = null;
}
return false;
}
@Override
public synchronized ComputeTask compute(ComputeTask task) throws RemoteException{
if(!ComputeService.connected()) return task;
return computeServer.compute(task);
}
}
Here is an example of how a client can use the service to perform work on the server:
CustomComputeTask task = new CustomComputeTask();
task = ComputeService.service.compute(task);