Click to See Complete Forum and Search --> : [RESOLVED] RMI Using different ports


ProgramThis
November 6th, 2009, 07:44 AM
Alright, I've answered enough questions to hopefully have one of my own answered :p

I am trying to write a distributed system in which I will have several servers (written in Java) that will all have the same data store on them. The goal of the application is to demonstrate data replication. So, if a client connects to a server (randomly chosen from a list of available servers), and he updates a piece of the data store (for simplicity I have chosen an int array), then the other servers need to notify each other that an update has been made and they must bring their data store up to the latest version.

I do not have access to multiple machines (as this needs to work on a single machine for testing / demonstration), so what I am trying to do is set up the servers simply to run on different ports on the rmiregistry.

Here is the interface message stub:
import java.rmi.Remote;
import java.rmi.RemoteException;

public interface MessageInterface extends Remote {
boolean sendUpdate(int index, int value) throws RemoteException;

boolean sendBroadcast(int data[]) throws RemoteException;

void broadcastInvalidate(int index) throws RemoteException;

int[] getDateStore() throws RemoteException;

int getSingleDataValue(int index) throws RemoteException;
}Here is the Server implementing the interface:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;

public class Server extends UnicastRemoteObject implements MessageInterface {

private static final long serialVersionUID = -4982932302868830173L;
private ArrayList<Server> servers = new ArrayList<Server>();
private ArrayList<Integer> data = new ArrayList<Integer>();
private String serverName;
private int port;

public Server() throws RemoteException { super(); }

public void broadcastInvalidate(int index) throws RemoteException {
for(Server s : servers) {

}
}

public int[] getDateStore() throws RemoteException {
int[] intArray = new int[data.size()];
for (int i = 0; i < data.size(); i++) {
intArray[i] = data.get(i);
}
return intArray;
}

public int getSingleDataValue(int index) throws RemoteException {
System.out.println("Client requested data at index: " + index);
return data.get(index);
}

public boolean sendBroadcast(int[] data) throws RemoteException {
return false;
}

public boolean sendUpdate(int index, int value) throws RemoteException {
return false;
}


public void setServerName(String name) {
this.serverName = name;
}
public String getServerName() {
return this.serverName;
}
public void displayStartupMessage() {
StringBuilder sb = new StringBuilder();
sb.append("***********************************************************");
sb.append("****************** Welcome to FooServer *****************");
sb.append("***********************************************************");
System.out.println(sb);
}

public void getPort(BufferedReader br) throws IOException {
System.out.println("**** Please enter a port for the Server to listen on ****");
boolean isInteger = false;
while(!isInteger) {
System.out.print("**** port: ");
String port = br.readLine();
try {
this.port = Integer.parseInt(port);
isInteger = true;
} catch(NumberFormatException ne) {
System.out.println("**** ERROR: You have entered an invalid port, please try again.");
}
}
}

public void testData() {
data.add(11);
data.add(12);
data.add(13);
data.add(14);
data.add(15);
data.add(16);
data.add(17);
data.add(18);
data.add(19);
}
}
I am using a Master Server in order to generate the servers (only 1 so far)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.rmi.AlreadyBoundException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;


public class MasterServer {
private int port;

public static void main(String[] args) {
try {
MasterServer ms = new MasterServer();
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
Server server = new Server();
server.testData();
server.displayStartupMessage();
server.getPort(br);
server.setServerName("Server"+ms.port);

UnicastRemoteObject.unexportObject(server, true);
MessageInterface stub = (MessageInterface) UnicastRemoteObject.exportObject(server,ms.port);
Registry registry = LocateRegistry.getRegistry();
try {
registry.bind(server.getServerName(), stub);
} catch(AlreadyBoundException ae) {
registry.rebind(server.getServerName(), stub);
}

} catch(IOException e) {
e.printStackTrace();
System.exit(1); // Force an exit because there might be other threads
} catch (Exception e) {
e.printStackTrace();
System.err.println("Usage: java [-Dbankname=<name>] RemoteBankServer");
System.exit(1); // Force an exit because there might be other threads
}

}
}
And of course, what would we do without a client:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.rmi.Naming;
import java.rmi.RMISecurityManager;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class Client {

private static void displayStartupMessage() {
StringBuilder sb = new StringBuilder();
sb.append("***********************************************************");
sb.append("****************** Welcome to FooClient *****************");
sb.append("***********************************************************");
System.out.println(sb);
}

public static void main(String[] args) {
try {
Client.displayStartupMessage();
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String input = "";
while(!input.equalsIgnoreCase("exit")) {
System.setSecurityManager(new RMISecurityManager());

String url = "rmi:///Server2000";

MessageInterface server = null;
Registry r = LocateRegistry.getRegistry(2000);

server = (MessageInterface)r.lookup("Server2000");
System.out.println("\nPlease enter a command from the following:\n");
System.out.println("sdv - Single data value followed by the index (ex 'sdv 4'");
System.out.println("exit - quit application");
input = br.readLine();

String values[] = input.split(" ");

if (values[0].equalsIgnoreCase("sdv") && values.length == 2) {
int x = Integer.parseInt(values[1]);
int n = server.getSingleDataValue(x);
System.out.println("Value at x = " + n);
}
else System.out.println("Unknown command");
}
} // Catch and display RMI exceptions
catch (RemoteException e) {
e.printStackTrace();
} // Other exceptions are probably user syntax errors, so show usage.
catch (Exception e) {
e.printStackTrace();
System.err.println("Usage: java [-Dbank=<url>] Bank$Client " +
"<cmd> <name> <password> [<amount>]");
System.err.println("where cmd is: open, close, deposit, " +
"withdraw, balance, history");
}
}
}I have read a few tutorials on using RMI. Most everything that I find says "why would you want to listen on a different port than 1099?" Well, I do :D. So I start up my rmiregistry as such:

>rmiregistry 2000

And then my Master server and I get:

C:\webapps\workspace\Replication\src>java -Djava.security.policy=c:\webapps\workspace\Replication\src\wideopen.policy MasterServer
***************************************************************************** Welcome to FooServer ********************************
**** Please enter a port for the Server to listen on ****
**** port: 2000
java.rmi.ConnectException: Connection refused to host: 192.168.114.107; nested exception is:
java.net.ConnectException: Connection refused: connect
at sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:601)
at sun.rmi.transport.tcp.TCPChannel.createConnection(TCPChannel.java:198)
at sun.rmi.transport.tcp.TCPChannel.newConnection(TCPChannel.java:184)
at sun.rmi.server.UnicastRef.newCall(UnicastRef.java:322)
at sun.rmi.registry.RegistryImpl_Stub.bind(Unknown Source)
at MasterServer.main(MasterServer.java:48)
Caused by: java.net.ConnectException: Connection refused: connect
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:333)
at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:195)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:182)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:366)
at java.net.Socket.connect(Socket.java:519)
at java.net.Socket.connect(Socket.java:469)
at java.net.Socket.<init>(Socket.java:366)
at java.net.Socket.<init>(Socket.java:180)
at sun.rmi.transport.proxy.RMIDirectSocketFactory.createSocket(RMIDirectSocketFactory.java:22)
at sun.rmi.transport.proxy.RMIMasterSocketFactory.createSocket(RMIMasterSocketFactory.java:128)
at sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:595)
... 5 more
Now, granted the port may be blocked here at work (I know, get back to work!), but at school I get security exceptions, at home the same (I will post them later when I get there).

I have several questions that I am not finding the answer to online.

First:
How do I set up the system to allow multiple servers? Can they all listen on the same port or do I need to bind them to different ports (different as far as I know, but maybe the registry will dish out requests based on name over the same port, this isn't clear from the documentation I have found)? If I need to bind each server to a different port, how do I get the rmiregistry to listen on multiple ports when it only takes one argument? Java RMI (http://java.sun.com/j2se/1.3/docs/tooldocs/solaris/rmiregistry.html)

Secondly:
When I was getting the security exception, this was at the school, with no ports being blocked. Am I doing something wrong in the client when I try to locate the registry? From all of the examples I have seen online, this is the way to do it, I just don't see what I am doing wrong.

Thanks,

Deliverance
November 6th, 2009, 08:14 AM
Just to back you up for a second here, is RMI really the best choice for doing this? It seems to me that a multi-cast protocol would work very nicely to notify all of the serves. Incidentally, there is a great library to use for such a task: http://www.jgroups.org/

The other option is to use a message queue (apache activeMQ, or sun's implementation). anyone listening to that queue and topic can automatically read and process this message.

Martin O
November 6th, 2009, 09:04 AM
Just a couple of suggestions (not that technical though).

Re: not having access to multiple machines:
You could use VMWare or Virtual PC (VMWare Server & Virtual PC are free).

And for your RMI problems, I'd suggest making something much more SSCCE-ish, till you work things out. For instance reduce the methods in your Remote interface from 5 methods to 1 simple 'testRmi' method.

ProgramThis
November 6th, 2009, 09:27 AM
I appreciate the suggestions.

As for having 5 methods and testing one, I am only testing one of them, even though there are 5 stubs.

As for libraries, I would LOVE to use one ;) However, this is an assignment and we are required to reinvent the wheel, as most assignments go :rolleyes:. We are supposed to be doing this at a very low level, and he wants us using either RMI in Java, or RPC in C.

ProgramThis
November 6th, 2009, 10:18 AM
Alright, after much frustration and trials I have found the solution :thumb:

If you are performing the LocateRegistry.createRegistry(port) and binding it, you do not need to perform the command line rmiregistry. That was confusing the system. So all I have to do now is start my server and then my clients and it all works nicely :).

Here is the code change for the master server in case anybody is interested.

public class MasterServer {

public void displayStartupMessage() {
StringBuilder sb = new StringBuilder();
sb.append("***********************************************************");
sb.append("****************** Welcome to FooServer *****************");
sb.append("***********************************************************");
System.out.println(sb);
}

public int getPort(BufferedReader br) throws IOException {
System.out.println("**** Please enter a port for the Server to listen on, enter 0 to finish ****");
int port = 0;
while (true) {
System.out.print("**** port: ");
String ports = br.readLine();
System.out.println("You entered port: " + ports + "\n");
try {
port = Integer.parseInt(ports);
break;
} catch (NumberFormatException ne) {
System.out.println("**** ERROR: You have entered an invalid port, please try again.");
}
}
return port;
}

private void createServers() throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int port = 0;
displayStartupMessage();
while(true) {
port = getPort(br);
if(port==0)break;
Server server = new Server();
server.testData();
server.setServerName("Server" + port);
server.setPort(port);
UnicastRemoteObject.unexportObject(server, true);
MessageInterface stub = (MessageInterface) UnicastRemoteObject
.exportObject(server, port);
Registry registry = LocateRegistry.createRegistry(port);

try {
registry.bind(server.getServerName(), stub);
} catch (AlreadyBoundException ae) {
registry.rebind(server.getServerName(), stub);
}
System.out.println("Listening on port: " + port + " to server: "
+ server.getServerName());
}
}

public static void main(String[] args) {
try {
MasterServer ms = new MasterServer();
ms.createServers();
} catch (IOException e) {
e.printStackTrace();
System.exit(1); // Force an exit because there might be other threads
} catch (Exception e) {
e.printStackTrace();
System.exit(1); // Force an exit because there might be other threads
}
}
}