Second Summer 2004 |
Here's a powerpoint presentation and a copy of the paper (dated May 2004). (Also the initial motivation dated Jan 2004).
Part One. Deriving the pattern.
There will be a server, implemented as follows:
There will be many clients, implemented as follows:class ServerImplementation { }
The server has a business card:class ClientImplementation { }
And it implements it:public interface Server { }
The client also has a business cardclass ServerImplementation implements Server { }
And, like the server, it makes a promise to fulfil it:public interface Client { }
The server keeps track of clients:class ClientImplementation implements Client { }
(Theclass ServerImplementation implements Server { Client[] clients = new Client[100]; int index = -1; }
index
of the most recently allocated cell is -1
if the array is empty). Notice that
What else does the server do?
It allows clients to register with it, whatever that means.
Although that in this case amounts to:public interface Server { public void register(Client client); }
So we see that, apparently, a client:class ServerImplementation implements Server { Client[] clients = new Client[100]; int index = -1; synchronized public void register(Client client) { clients[++this.index] = client; client.setID(new Integer(this.index)); for (int i = 0; i < this.index; i++) { clients[i].register(new Integer(this.index), client); } } }
Let's see how these promises can be fulfiled.public interface Client { public void setID(Integer index); public void register(Integer index, Client client); }
First off, every client will have an own numeric id
:
Secondly, the world of peers initially registered with the server is replicated in each client:class ClientImplementation implements Client { int id; synchronized public void setID(Integer index) { this.id = index.intValue(); } }
Let's go back to the server.class ClientImplementation implements Client { int id; synchronized public void setID(Integer index) { this.id = index.intValue(); } Client[] peer = new Client[100]; int index = -1; synchronized public void register(Integer index, Client client) { this.index = index.intValue(); this.peer[this.index] = client; } }
How does it get started and what does it do?
The server has no constructor.
The server is implemented as a thread.
In its run()
method the server constantly broadcasts to clients.
Broadcasting amounts to the following:class ServerImplementation implements Server, Runnable { Client[] clients = new Client[100]; int index = -1; synchronized public void register(Client client) { clients[++this.index] = client; client.setID(new Integer(this.index)); for (int i = 0; i < this.index; i++) { clients[i].register(new Integer(this.index), client); } } public void run() { while (true) { try { Thread.sleep((int)(Math.random() * 5000 + 5000)); this.broadcast(); } catch (Exception e) { } } } }
During broadcasting:class ServerImplementation implements Server, Runnable { Client[] clients = new Client[100]; int index = -1; synchronized public void register(Client client) { clients[++this.index] = client; client.setID(new Integer(this.index)); for (int i = 0; i < this.index; i++) { clients[i].register(new Integer(this.index), client); } } public void run() { while (true) { try { Thread.sleep((int)(Math.random() * 5000 + 5000)); this.broadcast(); } catch (Exception e) { } } } synchronized public void broadcast() { for (int i = 0; i <= this.index; i++) clients[i].setAvailable(new Boolean(false)); for (int i = 0, check = 0; i <= this.index; i++) check += clients[i].getBalance(); for (int i = 0; i <= this.index; i++) clients[i].setAvailable(new Boolean(true)); } }
Here's how we accomplish these steps.
Setting the availability and returning the current balance are advertised by the clients:
The implementation of these two functions is immediate:public interface Client { public void setID(Integer index); public void register(Integer index, Client client); public void setAvailable(Boolean availability); public int getBalance(); }
What else does the server do?class ClientImplementation implements Client { int id; synchronized public void setID(Integer index) { this.id = index.intValue(); } Client[] peer = new Client[100]; int index = -1; synchronized public void register(Integer index, Client client) { this.index = index.intValue(); this.peer[this.index] = client; } Boolean available = new Boolean(true); synchronized public void setAvailable(Boolean availability) { this.available = availability; } int balance; public int getBalance() { return this.balance; } }
Broadcasting is a just little bit more complicated but other than that, that's it.
Clients need to provide the added level of functionality:class ServerImplementation implements Server, Runnable { Client[] clients = new Client[100]; int index = -1; synchronized public void register(Client client) { clients[++this.index] = client; client.setID(new Integer(this.index)); for (int i = 0; i < this.index; i++) { clients[i].register(new Integer(this.index), client); } } public void run() { while (true) { try { Thread.sleep((int)(Math.random() * 5000 + 5000)); this.broadcast(); } catch (Exception e) { } } } synchronized public void broadcast() { for (int i = 0; i <= this.index; i++) clients[i].setAvailable(new Boolean(false)); String report = ""; String calculation = ""; int check = 0; for (int i = 0; i <= this.index; i++) { report += clients[i].report() + "\n"; calculation += "(" + clients[i].getBalance() + ").."; check += clients[i].getBalance(); } System.out.print("Server report indicates: \n" + report); System.out.println(calculation + " ---> " + check); for (int i = 0; i <= this.index; i++) clients[i].setAvailable(new Boolean(true)); } }
Implementation for this extra feature is immediate, as well:public interface Client { public void setID(Integer index); public void register(Integer index, Client client); public void setAvailable(Boolean availability); public int getBalance(); public String report(); }
How does one start the server?class ClientImplementation implements Client { int id; synchronized public void setID(Integer index) { this.id = index.intValue(); } Client[] peer = new Client[100]; int index = -1; synchronized public void register(Integer index, Client client) { this.index = index.intValue(); this.peer[this.index] = client; } Boolean available = new Boolean(true); synchronized public void setAvailable(Boolean availability) { this.available = availability; } int balance; public int getBalance() { return this.balance; } String name; public String report() { return this.name + ": " + this.balance; } }
The server provides a method for that:
If we were to try this program out we'd do something like this:class ServerImplementation implements Server, Runnable { Client[] clients = new Client[100]; int index = -1; synchronized public void register(Client client) { clients[++this.index] = client; client.setID(new Integer(this.index)); for (int i = 0; i < this.index; i++) { clients[i].register(new Integer(this.index), client); } } public void run() { while (true) { try { Thread.sleep((int)(Math.random() * 5000 + 5000)); this.broadcast(); } catch (Exception e) { } } } synchronized public void broadcast() { for (int i = 0; i <= this.index; i++) clients[i].setAvailable(new Boolean(false)); String report = ""; String calculation = ""; int check = 0; for (int i = 0; i <= this.index; i++) { report += clients[i].report() + "\n"; calculation += "(" + clients[i].getBalance() + ").."; check += clients[i].getBalance(); } System.out.print("Server report indicates: \n" + report); System.out.println(calculation + " ---> " + check); for (int i = 0; i <= this.index; i++) clients[i].setAvailable(new Boolean(true)); } public void startAsLocalServer() { new Thread(this).start(); } }
A server needs to be created, then started:class LocalSetup { public static void main(String[] args) { } }
We need some clients, too.class LocalSetup { public static void main(String[] args) { ServerImplementation server = new ServerImplementation(); server.startAsLocalServer(); } }
But how does one create a client?
And, if started, what do clients do?
So we can create a few clients now:class ClientImplementation implements Client { int id; synchronized public void setID(Integer index) { this.id = index.intValue(); } Client[] peer = new Client[100]; int index = -1; synchronized public void register(Integer index, Client client) { this.index = index.intValue(); this.peer[this.index] = client; } Boolean available = new Boolean(true); synchronized public void setAvailable(Boolean availability) { this.available = availability; } int balance; public int getBalance() { return this.balance; } String name; public String report() { return this.name + ": " + this.balance; } public ClientImplementation(String name) { this.name = name; } }
In describing how we start the clients we acknowledge for the first time the distributed nature of our goal.class LocalSetup { public static void main(String[] args) { ServerImplementation server = new ServerImplementation(); server.startAsLocalServer(); for (int i = 0; i < 6; i++) { ClientImplementation dealer = new ClientImplementation("Dealer_" + i); dealer.startAsClientOf(server); } } }
This addition however has significant ramifications.class ClientImplementation implements Client { int id; synchronized public void setID(Integer index) { this.id = index.intValue(); } Client[] peer = new Client[100]; int index = -1; synchronized public void register(Integer index, Client client) { this.index = index.intValue(); this.peer[this.index] = client; } Boolean available = new Boolean(true); synchronized public void setAvailable(Boolean availability) { this.available = availability; } int balance; public int getBalance() { return this.balance; } String name; public String report() { return this.name + ": " + this.balance; } public ClientImplementation(String name) { this.name = name; } public void startAsClientOf(java.rmi.Remote peer) { Server server = (Server)peer; server.register(this); this.server = server; new Thread(this).start(); } Server server; }
First the server needs to match the type:
In addition to that clients need to be described in their active behaviour:public interface Server extends java.rmi.Remote { public void register(Client client); }
So that's what clients typically do: wait, then initiate a transfer.class ClientImplementation implements Client, Runnable { int id; synchronized public void setID(Integer index) { this.id = index.intValue(); } Client[] peer = new Client[100]; int index = -1; synchronized public void register(Integer index, Client client) { this.index = index.intValue(); this.peer[this.index] = client; } Boolean available = new Boolean(true); synchronized public void setAvailable(Boolean availability) { this.available = availability; } int balance; public int getBalance() { return this.balance; } String name; public String report() { return this.name + ": " + this.balance; } public ClientImplementation(String name) { this.name = name; } public void startAsClientOf(java.rmi.Remote peer) { Server server = (Server)peer; server.register(this); this.server = server; new Thread(this).start(); } Server server; public void run() { while (true) { try { Thread.sleep((int) Math.random() * 1000 + 1000); if (this.available.booleanValue()) this.initiateTransfer(); } catch (Exception e) { } } } }
Initiating a transfer has the following description:
A description ofclass ClientImplementation implements Client, Runnable { int id; synchronized public void setID(Integer index) { this.id = index.intValue(); } Client[] peer = new Client[100]; int index = -1; synchronized public void register(Integer index, Client client) { this.index = index.intValue(); this.peer[this.index] = client; } Boolean available = new Boolean(true); synchronized public void setAvailable(Boolean availability) { this.available = availability; } int balance; public int getBalance() { return this.balance; } String name; public String report() { return this.name + ": " + this.balance; } public ClientImplementation(String name) { this.name = name; } public void startAsClientOf(java.rmi.Remote peer) { Server server = (Server)peer; server.register(this); this.server = server; new Thread(this).start(); } Server server; public void run() { while (true) { try { Thread.sleep((int) Math.random() * 1000 + 1000); if (this.available.booleanValue()) this.initiateTransfer(); } catch (Exception e) { } } } synchronized private void initiateTransfer() { if (this.index > 0) { int chosen = (int) (Math.random() * (this.index + 1)); if (chosen == this.id // always deal with someone else || peer[chosen] == null) return; this.balance += peer[chosen].process( new Transaction( this.name, this.id, (int)(Math.random() * 10 + 1), Math.random() > 0.5 ? "sent" : "requested")); } } }
process
and of Transaction
follows:
Transactions are passive, but by the end of this tutorial they will become visitors.public class Transaction { String initiatorName; int initiatorID; int amount; String direction; public Transaction(String name, int id, int amount, String direction) { this.initiatorName = name; this.initiatorID = id; this.amount = amount; this.direction = direction; } }
Processing is not very complicated:
Andclass ClientImplementation implements Client, Runnable { int id; synchronized public void setID(Integer index) { this.id = index.intValue(); } Client[] peer = new Client[100]; int index = -1; synchronized public void register(Integer index, Client client) { this.index = index.intValue(); this.peer[this.index] = client; } Boolean available = new Boolean(true); synchronized public void setAvailable(Boolean availability) { this.available = availability; } int balance; public int getBalance() { return this.balance; } String name; public String report() { return this.name + ": " + this.balance; } public ClientImplementation(String name) { this.name = name; } public void startAsClientOf(java.rmi.Remote peer) { Server server = (Server)peer; server.register(this); this.server = server; new Thread(this).start(); } Server server; public void run() { while (true) { try { Thread.sleep((int) Math.random() * 1000 + 1000); if (this.available.booleanValue()) this.initiateTransfer(); } catch (Exception e) { } } } synchronized private void initiateTransfer() { if (this.index > 0) { int chosen = (int) (Math.random() * (this.index + 1)); if (chosen == this.id // always deal with someone else || peer[chosen] == null) return; this.balance += peer[chosen].process( new Transaction( this.name, this.id, (int)(Math.random() * 10 + 1), Math.random() > 0.5 ? "sent" : "requested")); } } synchronized public int process(Transaction transaction) { if (this.available.booleanValue()) if (transaction.direction.equals("sent")) { this.balance += transaction.amount; return - (transaction.amount); } else { this.balance -= transaction.amount; return (transaction.amount); } else return 0; // object unavailable: method idempotent } }
process
must be added to Client
's business card:
So our application has been developed now:public interface Client { public void setID(Integer index); public void register(Integer index, Client client); public void setAvailable(Boolean availability); public int getBalance(); public String report(); public int process(Transaction transaction); }
Is it possible to have avoided usingburrowww.cs.indiana.edu% javac *.java burrowww.cs.indiana.edu% java LocalSetup Server report indicates: Dealer_0: -7 Dealer_1: 15 Dealer_2: -12 Dealer_3: -3 Dealer_4: 13 Dealer_5: -6 (-7)..(15)..(-12)..(-3)..(13)..(-6).. ---> 0 Server report indicates: Dealer_0: -16 Dealer_1: 24 Dealer_2: -24 Dealer_3: -26 Dealer_4: 15 Dealer_5: 27 (-16)..(24)..(-24)..(-26)..(15)..(27).. ---> 0 Server report indicates: Dealer_0: -23 Dealer_1: 44 Dealer_2: -16 Dealer_3: -23 Dealer_4: 5 Dealer_5: 13 (-23)..(44)..(-16)..(-23)..(5)..(13).. ---> 0 Server report indicates: Dealer_0: -39 Dealer_1: 28 Dealer_2: -6 Dealer_3: -14 Dealer_4: 9 Dealer_5: 22 (-39)..(28)..(-6)..(-14)..(9)..(22).. ---> 0 ^Cburrowww.cs.indiana.edu%
java.rmi.Remote
at all? Perhaps so, but this is a bit more general, since we could have more than one kind of server.
That is, we could easily rewrite startAsClientOf
as follows:
Now we will show how we distribute the program without touching the application logic developed thus far.public void startAsClientOf(java.rmi.Remote peer) { if (peer instanceof Server) { Server server = (Server)peer; server.register(this); this.server = server; new Thread(this).start(); } else { // throw an exception or something... } }
Let's introduce this abstraction:
This abstraction encapsulates and provides everything else that was needed.public abstract class NetworkPeer implements java.rmi.Remote { public void exportMethods() throws java.rmi.RemoteException { java.rmi.server.UnicastRemoteObject.exportObject(this); } public java.rmi.Remote locatePeer(String peerHost, int peerPort, String peerName) throws Exception { return java.rmi.Naming.lookup("rmi://" + peerHost + ":" + peerPort + "/" + peerName); } public void startAsNetworkClientOf(String peerHost, int peerPort, String peerName) throws Exception { this.exportMethods(); java.rmi.Remote peer = this.locatePeer(peerHost, peerPort, peerName); this.startAsClientOf(peer); // see below ... } public abstract void startAsClientOf(java.rmi.Remote peer) throws java.rmi.RemoteException; public void startAsNetworkServer(String name, int port) { System.setSecurityManager(new java.rmi.RMISecurityManager()); try { this.exportMethods(); java.rmi.registry.Registry registry = java.rmi.registry.LocateRegistry.createRegistry(port); registry.bind(name, this); this.startAsLocalServer(); // see below ... System.out.println("Server is ready ... "); } catch (Exception e) { System.out.println("Server error: " + e + " ... "); } } public abstract void startAsLocalServer(); // perhaps a better name would be startAsServer... }
First, for the server:
The client implementation also includes the ability to start a client as a standalone application:class ServerImplementation extends NetworkPeer implements Server, Runnable { Client[] clients = new Client[100]; int index = -1; synchronized public void register(Client client) { clients[++this.index] = client; client.setID(new Integer(this.index)); for (int i = 0; i < this.index; i++) { clients[i].register(new Integer(this.index), client); } } public void run() { while (true) { try { Thread.sleep((int)(Math.random() * 5000 + 5000)); this.broadcast(); } catch (Exception e) { } } } synchronized public void broadcast() { for (int i = 0; i <= this.index; i++) clients[i].setAvailable(new Boolean(false)); String report = ""; String calculation = ""; int check = 0; for (int i = 0; i <= this.index; i++) { report += clients[i].report() + "\n"; calculation += "(" + clients[i].getBalance() + ").."; check += clients[i].getBalance(); } System.out.print("Server report indicates: \n" + report); System.out.println(calculation + " ---> " + check); for (int i = 0; i <= this.index; i++) clients[i].setAvailable(new Boolean(true)); } public void startAsLocalServer() { new Thread(this).start(); } public static void main(String[] args) { String portNumber = args[0], ownName = args[1]; ServerImplementation here = new ServerImplementation(); here.startAsNetworkServer(ownName, Integer.parseInt(portNumber)); // startAsLocalServer implicitly called ... } public void startAsClientOf(java.rmi.Remote peer) { // not needed, shows why a server is a client that has a public address (home, host) } }
And there's one last change we need to make:class ClientImplementation extends NetworkPeer implements Client, Runnable { int id; synchronized public void setID(Integer index) { this.id = index.intValue(); } Client[] peer = new Client[100]; int index = -1; synchronized public void register(Integer index, Client client) { this.index = index.intValue(); this.peer[this.index] = client; } Boolean available = new Boolean(true); synchronized public void setAvailable(Boolean availability) { this.available = availability; } int balance; public int getBalance() { return this.balance; } String name; public String report() { return this.name + ": " + this.balance; } public ClientImplementation(String name) { this.name = name; } public void startAsClientOf(java.rmi.Remote peer) { Server server = (Server)peer; server.register(this); this.server = server; new Thread(this).start(); } Server server; public void run() { while (true) { try { Thread.sleep((int) Math.random() * 1000 + 1000); if (this.available.booleanValue()) this.initiateTransfer(); } catch (Exception e) { } } } synchronized private void initiateTransfer() { if (this.index > 0) { int chosen = (int) (Math.random() * (this.index + 1)); if (chosen == this.id // always deal with someone else || peer[chosen] == null) return; this.balance += peer[chosen].process( new Transaction( this.name, this.id, (int)(Math.random() * 10 + 1), Math.random() > 0.5 ? "sent" : "requested")); } } synchronized public int process(Transaction transaction) { if (this.available.booleanValue()) if (transaction.direction.equals("sent")) { this.balance += transaction.amount; return - (transaction.amount); } else { this.balance -= transaction.amount; return (transaction.amount); } else return 0; // object unavailable: method idempotent } public static void main(String[] args) throws Exception { String ownName = args[0], serverHostName = args[1], serverPortNumber = args[2], serverName = args[3]; ClientImplementation client = new ClientImplementation(ownName); client.startAsNetworkClientOf(serverHostName, Integer.parseInt(serverPortNumber), serverName); // startAsClientOf(...) will be called implicitly ... } public void startAsLocalServer() { // not needed, a client is an anonymous figure, a guest without a permanent address ... } }
But being able to perform as a network peer requires additional decoration:public class Transaction implements java.io.Serializable { String initiatorName; int initiatorID; int amount; String direction; public Transaction(String name, int id, int amount, String direction) { this.initiatorName = name; this.initiatorID = id; this.amount = amount; this.direction = direction; } }
That means we need to create stubs for both theburrowww.cs.indiana.edu% pwd /nfs/paca/san/r1a0l1/dgerman/pasadena burrowww.cs.indiana.edu% ls -ld * -rw-r--r-- 1 dgerman faculty 290 Jul 2 14:26 Client.java -rw-r--r-- 1 dgerman faculty 2666 Jul 3 01:15 ClientImplementation.java -rw-r--r-- 1 dgerman faculty 321 Jul 2 13:45 LocalSetup.java -rw-r--r-- 1 dgerman faculty 1378 Jul 3 00:13 NetworkPeer.java -rw-r--r-- 1 dgerman faculty 96 Jul 2 13:57 Server.java -rw-r--r-- 1 dgerman faculty 1683 Jul 3 01:34 ServerImplementation.java -rw-r--r-- 1 dgerman faculty 338 Jul 3 01:28 Transaction.java burrowww.cs.indiana.edu% javac *.java burrowww.cs.indiana.edu% rmic NetworkPeer burrowww.cs.indiana.edu% java ServerImplementation 36091 theServer Server error: java.rmi.StubNotFoundException: Stub class not found: ServerImplementation_Stub; nested exception is: java.lang.ClassNotFoundException: ServerImplementation_Stub ... burrowww.cs.indiana.edu%
ServerImplementation
class (a first round of changes)
So now we canclass ServerImplementation extends NetworkPeer implements Server, Runnable, java.rmi.Remote { // need stub(s) ... Client[] clients = new Client[100]; int index = -1; synchronized public void register(Client client) throws java.rmi.RemoteException { // rmic imposes this on Server ... clients[++this.index] = client; client.setID(new Integer(this.index)); for (int i = 0; i < this.index; i++) { clients[i].register(new Integer(this.index), client); } } public void run() { while (true) { try { Thread.sleep((int)(Math.random() * 5000 + 5000)); this.broadcast(); } catch (Exception e) { } } } synchronized public void broadcast() { for (int i = 0; i <= this.index; i++) clients[i].setAvailable(new Boolean(false)); String report = ""; String calculation = ""; int check = 0; for (int i = 0; i <= this.index; i++) { report += clients[i].report() + "\n"; calculation += "(" + clients[i].getBalance() + ").."; check += clients[i].getBalance(); } System.out.print("Server report indicates: \n" + report); System.out.println(calculation + " ---> " + check); for (int i = 0; i <= this.index; i++) clients[i].setAvailable(new Boolean(true)); } public void startAsLocalServer() { new Thread(this).start(); } public static void main(String[] args) { String portNumber = args[0], ownName = args[1]; ServerImplementation here = new ServerImplementation(); here.startAsNetworkServer(ownName, Integer.parseInt(portNumber)); // startAsLocalServer implicitly called ... } public void startAsClientOf(java.rmi.Remote peer) { // not needed, shows why a server is a client that has a public address (home, host) } } public interface Server extends java.rmi.Remote { public void register(Client client) throws java.rmi.RemoteException; // rmic requires it ... (Server/ClientImplementations comply) } class ClientImplementation extends NetworkPeer implements Client, Runnable { int id; synchronized public void setID(Integer index) { this.id = index.intValue(); } Client[] peer = new Client[100]; int index = -1; synchronized public void register(Integer index, Client client) { this.index = index.intValue(); this.peer[this.index] = client; } Boolean available = new Boolean(true); synchronized public void setAvailable(Boolean availability) { this.available = availability; } int balance; public int getBalance() { return this.balance; } String name; public String report() { return this.name + ": " + this.balance; } public ClientImplementation(String name) { this.name = name; } public void startAsClientOf(java.rmi.Remote peer) throws java.rmi.RemoteException { // because of register(...) ... Server server = (Server)peer; server.register(this); this.server = server; new Thread(this).start(); } Server server; public void run() { while (true) { try { Thread.sleep((int) Math.random() * 1000 + 1000); if (this.available.booleanValue()) this.initiateTransfer(); } catch (Exception e) { } } } synchronized private void initiateTransfer() { if (this.index > 0) { int chosen = (int) (Math.random() * (this.index + 1)); if (chosen == this.id // always deal with someone else || peer[chosen] == null) return; this.balance += peer[chosen].process( new Transaction( this.name, this.id, (int)(Math.random() * 10 + 1), Math.random() > 0.5 ? "sent" : "requested")); } } synchronized public int process(Transaction transaction) { if (this.available.booleanValue()) if (transaction.direction.equals("sent")) { this.balance += transaction.amount; return - (transaction.amount); } else { this.balance -= transaction.amount; return (transaction.amount); } else return 0; // object unavailable: method idempotent } public static void main(String[] args) throws Exception { String ownName = args[0], serverHostName = args[1], serverPortNumber = args[2], serverName = args[3]; ClientImplementation client = new ClientImplementation(ownName); client.startAsNetworkClientOf(serverHostName, Integer.parseInt(serverPortNumber), serverName); // startAsClientOf(...) will be called implicitly ... } public void startAsLocalServer() { // not needed, a client is an anonymous figure, a guest without a permanent address ... } } class LocalSetup { public static void main(String[] args) throws /*java.rmi.Remote*/Exception { // startAsClientOf(...)'s influence ... ServerImplementation server = new ServerImplementation(); server.startAsLocalServer(); for (int i = 0; i < 6; i++) { ClientImplementation dealer = new ClientImplementation("Dealer_" + i); dealer.startAsClientOf(server); } } }
javac
everything and rmic ServerImplementation
.
And we need to take care of the ClientImplementation
as well:
So, as we can see, the code we developed originally has not been touched, and is being used.public interface Client extends java.rmi.Remote { public void setID(Integer index) throws java.rmi.RemoteException; public void register(Integer index, Client client) throws java.rmi.RemoteException; public void setAvailable(Boolean availability) throws java.rmi.RemoteException; public int getBalance() throws java.rmi.RemoteException; public String report() throws java.rmi.RemoteException; public int process(Transaction transaction) throws java.rmi.RemoteException; } class ClientImplementation extends NetworkPeer implements Client, Runnable { int id; synchronized public void setID(Integer index) { this.id = index.intValue(); } Client[] peer = new Client[100]; int index = -1; synchronized public void register(Integer index, Client client) { this.index = index.intValue(); this.peer[this.index] = client; } Boolean available = new Boolean(true); synchronized public void setAvailable(Boolean availability) { this.available = availability; } int balance; public int getBalance() { return this.balance; } String name; public String report() { return this.name + ": " + this.balance; } public ClientImplementation(String name) { this.name = name; } public void startAsClientOf(java.rmi.Remote peer) throws java.rmi.RemoteException { Server server = (Server)peer; server.register(this); this.server = server; new Thread(this).start(); } Server server; public void run() { while (true) { try { Thread.sleep((int) Math.random() * 1000 + 1000); if (this.available.booleanValue()) this.initiateTransfer(); } catch (Exception e) { } } } synchronized private void initiateTransfer() throws java.rmi.RemoteException { if (this.index > 0) { int chosen = (int) (Math.random() * (this.index + 1)); if (chosen == this.id // always deal with someone else || peer[chosen] == null) return; this.balance += peer[chosen].process( new Transaction( this.name, this.id, (int)(Math.random() * 10 + 1), Math.random() > 0.5 ? "sent" : "requested")); } } synchronized public int process(Transaction transaction) { if (this.available.booleanValue()) if (transaction.direction.equals("sent")) { this.balance += transaction.amount; return - (transaction.amount); } else { this.balance -= transaction.amount; return (transaction.amount); } else return 0; // object unavailable: method idempotent } public static void main(String[] args) throws Exception { String ownName = args[0], serverHostName = args[1], serverPortNumber = args[2], serverName = args[3]; ClientImplementation client = new ClientImplementation(ownName); client.startAsNetworkClientOf(serverHostName, Integer.parseInt(serverPortNumber), serverName); // startAsClientOf(...) will be called implicitly ... } public void startAsLocalServer() { // not needed, a client is an anonymous figure, a guest without a permanent address ... } } class ServerImplementation extends NetworkPeer implements Server, Runnable, java.rmi.Remote { Client[] clients = new Client[100]; int index = -1; synchronized public void register(Client client) throws java.rmi.RemoteException { clients[++this.index] = client; client.setID(new Integer(this.index)); for (int i = 0; i < this.index; i++) { clients[i].register(new Integer(this.index), client); } } public void run() { while (true) { try { Thread.sleep((int)(Math.random() * 5000 + 5000)); this.broadcast(); } catch (Exception e) { } } } synchronized public void broadcast() throws java.rmi.RemoteException { for (int i = 0; i <= this.index; i++) clients[i].setAvailable(new Boolean(false)); String report = ""; String calculation = ""; int check = 0; for (int i = 0; i <= this.index; i++) { report += clients[i].report() + "\n"; calculation += "(" + clients[i].getBalance() + ").."; check += clients[i].getBalance(); } System.out.print("Server report indicates: \n" + report); System.out.println(calculation + " ---> " + check); for (int i = 0; i <= this.index; i++) clients[i].setAvailable(new Boolean(true)); } public void startAsLocalServer() { new Thread(this).start(); } public static void main(String[] args) { String portNumber = args[0], ownName = args[1]; ServerImplementation here = new ServerImplementation(); here.startAsNetworkServer(ownName, Integer.parseInt(portNumber)); // startAsLocalServer implicitly called ... } public void startAsClientOf(java.rmi.Remote peer) { // not needed, shows why a server is a client that has a public address (home, host) } }
Now we can start the programs in distributed fashion.
On tucotuco.cs.indiana.edu
we start theServer
as a server on port 36091
:
This, of course, is done after compiling everything and runningtucotuco.cs.indiana.edu% java ServerImplementation 36091 theServer Server is ready ... Server report indicates: ---> 0 Server report indicates: ---> 0 Server report indicates: ---> 0 Server report indicates: larry: 0 (0).. ---> 0 Server report indicates: larry: 0 (0).. ---> 0 Server report indicates: larry: 0 (0).. ---> 0 Server report indicates: larry: 0 (0).. ---> 0 Server report indicates: larry: 0 (0).. ---> 0 Server report indicates: larry: 0 (0).. ---> 0 Server report indicates: larry: 17 michael: -17 (17)..(-17).. ---> 0 Server report indicates: larry: 18 michael: -18 (18)..(-18).. ---> 0 Server report indicates: larry: 30 michael: -30 (30)..(-30).. ---> 0 Server report indicates: larry: 40 michael: -40 (40)..(-40).. ---> 0 Server report indicates: larry: 26 michael: -26 (26)..(-26).. ---> 0 Server report indicates: larry: 15 michael: -15 (15)..(-15).. ---> 0 Server report indicates: larry: 38 michael: -51 toni: 13 (38)..(-51)..(13).. ---> 0 Server report indicates: larry: 40 michael: -51 toni: 11 (40)..(-51)..(11).. ---> 0 Server report indicates: larry: 39 michael: -54 toni: 15 (39)..(-54)..(15).. ---> 0 Server report indicates: larry: 41 michael: -66 toni: 25 (41)..(-66)..(25).. ---> 0 Server report indicates: larry: 72 michael: -111 toni: 39 (72)..(-111)..(39).. ---> 0 Server report indicates: larry: 82 michael: -89 toni: 7 (82)..(-89)..(7).. ---> 0 Server report indicates: larry: 70 michael: -85 toni: 15 (70)..(-85)..(15).. ---> 0 Server report indicates: larry: 74 michael: -85 toni: 11 (74)..(-85)..(11).. ---> 0 Server report indicates: larry: 67 michael: -71 toni: 4 (67)..(-71)..(4).. ---> 0 Server report indicates: larry: 69 michael: -69 toni: 12 richard: -12 (69)..(-69)..(12)..(-12).. ---> 0 Server report indicates: larry: 64 michael: -69 toni: 11 richard: -6 (64)..(-69)..(11)..(-6).. ---> 0 Server report indicates: larry: 71 michael: -62 toni: 9 richard: -18 (71)..(-62)..(9)..(-18).. ---> 0 Server report indicates: larry: 74 michael: -56 toni: 4 richard: -22 (74)..(-56)..(4)..(-22).. ---> 0 Server report indicates: larry: 81 michael: -53 toni: 3 richard: -31 (81)..(-53)..(3)..(-31).. ---> 0 Server report indicates: larry: 41 michael: -45 toni: 19 richard: -15 (41)..(-45)..(19)..(-15).. ---> 0 ^Ctucotuco.cs.indiana.edu%
rmic
on the two implementations. The server output listed above is a consequence of starting the following clients, in sequence.
On burrowww.cs.indiana.edu
:
Onburrowww.cs.indiana.edu% java ClientImplementation larry tucotuco.cs.indiana.edu 36091 theServer
blesmol.cs.indiana.edu
:
Onblesmol.cs.indiana.edu% java ClientImplementation michael tucotuco.cs.indiana.edu 36091 theServer
bobac.cs.indiana.edu
:
And finally, onbobac.cs.indiana.edu% java ClientImplementation toni tucotuco.cs.indiana.edu 36091 theServer
molerat.cs.indiana.edu
:
And the local setup still works as before.molerat.cs.indiana.edu% java ClientImplementation richard tucotuco.cs.indiana.edu 36091 theServer
Now we need to show how we one could simply use this pattern from the outset.
OOP is really about distributed programming.
That's why the pattern above says nothing about (and cannot make use of) static
members.
Part Two. Transactions as visitors.
We do exactly what we did earlier, changing only one thing.
But the development is without exploration: we know what we want and we simply go for it.
So there's a NetworkPeer
abstraction one needs to have in mind (and start from).
It can be compiled, packaged. Let's keep moving.
There is a server (with an interface and a server implementation).
There's also a type of client (of which we have many instances).
Every client implements the client interface in a specific (same) way.
The transaction process has changed a bit (in client implementation) and the transaction object is now an agent (visitor).
That's basically it. The local set up is just for testing (although it's extremely important). So now we try things out:
Let's see the local setup in action first:frilled.cs.indiana.edu%pwd /nfs/grouchy/home/user2/www/classes/a348-dger/sum2004/pasadena/partTwo frilled.cs.indiana.edu%ls -ld * -rw-r--r-- 1 dgerman faculty 506 Jul 3 23:41 Client.java -rw-r--r-- 1 dgerman faculty 2683 Jul 4 00:07 ClientImplementation.java -rw-r--r-- 1 dgerman faculty 358 Jul 4 00:01 LocalSetup.java -rw-r--r-- 1 dgerman faculty 1575 Jul 3 23:11 NetworkPeer.java -rw-r--r-- 1 dgerman faculty 128 Jul 3 23:36 Server.java -rw-r--r-- 1 dgerman faculty 1720 Jul 3 23:37 ServerImplementation.java -rw-r--r-- 1 dgerman faculty 882 Jul 4 00:06 Transaction.java frilled.cs.indiana.edu%javac *.java frilled.cs.indiana.edu%rmic ClientImplementation frilled.cs.indiana.edu%rmic ServerImplementation frilled.cs.indiana.edu%ls -ld * -rw------- 1 dgerman faculty 464 Jul 4 00:10 Client.class -rw-r--r-- 1 dgerman faculty 506 Jul 3 23:41 Client.java -rw------- 1 dgerman faculty 2728 Jul 4 00:10 ClientImplementation.class -rw-r--r-- 1 dgerman faculty 2683 Jul 4 00:07 ClientImplementation.java -rw------- 1 dgerman faculty 2968 Jul 4 00:10 ClientImplementation_Skel.class -rw------- 1 dgerman faculty 5381 Jul 4 00:10 ClientImplementation_Stub.class -rw------- 1 dgerman faculty 764 Jul 4 00:10 LocalSetup.class -rw-r--r-- 1 dgerman faculty 358 Jul 4 00:01 LocalSetup.java -rw------- 1 dgerman faculty 1943 Jul 4 00:10 NetworkPeer.class -rw-r--r-- 1 dgerman faculty 1575 Jul 3 23:11 NetworkPeer.java -rw------- 1 dgerman faculty 202 Jul 4 00:10 Server.class -rw-r--r-- 1 dgerman faculty 128 Jul 3 23:36 Server.java -rw------- 1 dgerman faculty 2275 Jul 4 00:10 ServerImplementation.class -rw-r--r-- 1 dgerman faculty 1720 Jul 3 23:37 ServerImplementation.java -rw------- 1 dgerman faculty 1625 Jul 4 00:10 ServerImplementation_Skel.class -rw------- 1 dgerman faculty 2906 Jul 4 00:10 ServerImplementation_Stub.class -rw------- 1 dgerman faculty 1254 Jul 4 00:10 Transaction.class -rw-r--r-- 1 dgerman faculty 882 Jul 4 00:06 Transaction.java frilled.cs.indiana.edu%
Same thing should happen if we start things in a distributed fashion.frilled.cs.indiana.edu%java LocalSetup 0 chooses 2 1 chooses 3 Visiting Dealer_3: 0 sent by 1 10 points sent Transaction now coming through... Visiting Dealer_2: 0 sent by 0 3 points requested Transaction now coming through... 3 chooses 2 Visiting Dealer_2: -3 sent by 3 6 points requested Transaction now coming through... 4 chooses 0 Visiting Dealer_0: 3 sent by 4 9 points sent Transaction now coming through... 5 chooses 2 Visiting Dealer_2: -9 sent by 5 10 points sent Transaction now coming through... 1 chooses 4 Visiting Dealer_4: -9 sent by 1 10 points requested Transaction now coming through... 4 chooses 0 Visiting Dealer_0: 12 sent by 4 10 points requested Transaction now coming through... 0 chooses 4 Visiting Dealer_4: -9 sent by 0 8 points requested Transaction now coming through... 2 chooses 1 Visiting Dealer_1: 0 sent by 2 7 points sent Transaction now coming through... 5 chooses 4 Visiting Dealer_4: -17 sent by 5 7 points sent Transaction now coming through... 3 chooses 0 Visiting Dealer_0: 10 sent by 3 2 points sent Transaction now coming through... 1 chooses 5 Visiting Dealer_5: -17 sent by 1 2 points requested Transaction now coming through... 0 chooses 3 Visiting Dealer_3: 14 sent by 0 1 points sent Transaction now coming through... 4 chooses 2 Visiting Dealer_2: -6 sent by 4 2 points sent Transaction now coming through... 2 chooses 5 Visiting Dealer_5: -19 sent by 2 7 points sent Transaction now coming through... 5 chooses 0 Visiting Dealer_0: 11 sent by 5 10 points sent Transaction now coming through... 3 chooses 0 Visiting Dealer_0: 21 sent by 3 8 points sent Transaction now coming through... 1 chooses 0 Visiting Dealer_0: 29 sent by 1 9 points requested Transaction now coming through... 4 chooses 3 Visiting Dealer_3: 7 sent by 4 7 points requested Transaction now coming through... 0 chooses 4 Visiting Dealer_4: -5 sent by 0 6 points sent Transaction now coming through... 5 chooses 0 Visiting Dealer_0: 14 sent by 5 1 points requested Transaction now coming through... 1 chooses 3 Visiting Dealer_3: 0 sent by 1 7 points requested Transaction now coming through... 2 chooses 4 Visiting Dealer_4: 1 sent by 2 9 points requested Transaction now coming through... 4 chooses 0 Visiting Dealer_0: 13 sent by 4 9 points requested Transaction now coming through... 5 chooses 1 Visiting Dealer_1: 25 sent by 5 8 points sent Transaction now coming through... 3 chooses 4 Visiting Dealer_4: 1 sent by 3 5 points requested Transaction now coming through... 0 chooses 5 Visiting Dealer_5: -29 sent by 0 4 points sent Transaction now coming through... 4 chooses 0 2 chooses 5 Visiting Dealer_5: -25 sent by 2 7 points requested Transaction now coming through... Visiting Dealer_0: 0 sent by 4 7 points requested Transaction now coming through... 5 chooses 3 Visiting Dealer_3: -2 sent by 5 9 points requested Transaction now coming through... 3 chooses 4 Visiting Dealer_4: 3 sent by 3 5 points requested Transaction now coming through... 0 chooses 1 Visiting Dealer_1: 33 sent by 0 7 points requested Transaction now coming through... 1 chooses 0 Visiting Dealer_0: 0 sent by 1 3 points requested Transaction now coming through... 2 chooses 3 Visiting Dealer_3: -6 sent by 2 2 points requested Transaction now coming through... 5 chooses 1 Visiting Dealer_1: 29 sent by 5 5 points requested Transaction now coming through... 3 chooses 5 Visiting Dealer_5: -18 sent by 3 8 points sent Transaction now coming through... Server report indicates: Dealer_0: -3 Dealer_1: 24 Dealer_2: 7 Dealer_3: -16 Dealer_4: -2 Dealer_5: -10 (-3)..(24)..(7)..(-16)..(-2)..(-10).. ---> 0 1 chooses 2 Visiting Dealer_2: 7 sent by 1 4 points sent Transaction now coming through... 4 chooses 2 Visiting Dealer_2: 11 sent by 4 3 points requested Transaction now coming through... 5 chooses 4 Visiting Dealer_4: 1 sent by 5 3 points sent Transaction now coming through... 3 chooses 4 Visiting Dealer_4: 4 sent by 3 2 points sent Transaction now coming through... 2 chooses 1 Visiting Dealer_1: 20 sent by 2 3 points sent Transaction now coming through... 4 chooses 0 Visiting Dealer_0: -3 sent by 4 6 points sent Transaction now coming through... 5 chooses 0 Visiting Dealer_0: 3 sent by 5 7 points sent Transaction now coming through... 3 chooses 2 Visiting Dealer_2: 5 sent by 3 9 points sent Transaction now coming through... 0 chooses 3 Visiting Dealer_3: -27 sent by 0 10 points requested Transaction now coming through... 1 chooses 2 Visiting Dealer_2: 14 sent by 1 7 points sent Transaction now coming through... 2 chooses 5 Visiting Dealer_5: -20 sent by 2 2 points requested Transaction now coming through... 4 chooses 2 Visiting Dealer_2: 23 sent by 4 1 points requested Transaction now coming through... 5 chooses 4 Visiting Dealer_4: 1 sent by 5 3 points requested Transaction now coming through... 3 chooses 0 Visiting Dealer_0: 20 sent by 3 1 points sent Transaction now coming through... 0 chooses 2 Visiting Dealer_2: 22 sent by 0 8 points requested Transaction now coming through... 2 chooses 4 Visiting Dealer_4: -2 sent by 2 4 points requested Transaction now coming through... 1 chooses 4 Visiting Dealer_4: -6 sent by 1 1 points sent Transaction now coming through... 4 chooses 5 Visiting Dealer_5: -19 sent by 4 1 points sent Transaction now coming through... 5 chooses 3 Visiting Dealer_3: -38 sent by 5 3 points sent Transaction now coming through... 3 chooses 5 Visiting Dealer_5: -21 sent by 3 10 points sent Transaction now coming through... 0 chooses 1 Visiting Dealer_1: 15 sent by 0 1 points requested Transaction now coming through... 1 chooses 4 Visiting Dealer_4: -6 sent by 1 1 points sent Transaction now coming through... 2 chooses 0 Visiting Dealer_0: 30 sent by 2 5 points requested Transaction now coming through... 4 chooses 0 Visiting Dealer_0: 25 sent by 4 9 points requested Transaction now coming through... 5 chooses 4 Visiting Dealer_4: 4 sent by 5 8 points requested Transaction now coming through... 3 chooses 4 Visiting Dealer_4: -4 sent by 3 6 points requested Transaction now coming through... 0 chooses 4 Visiting Dealer_4: -10 sent by 0 5 points sent Transaction now coming through... 1 chooses 0 Visiting Dealer_0: 11 sent by 1 8 points sent Transaction now coming through... 4 chooses 1 Visiting Dealer_1: 5 sent by 4 8 points requested Transaction now coming through... 5 chooses 1 Visiting Dealer_1: -3 sent by 5 2 points sent Transaction now coming through... 0 chooses 3 Visiting Dealer_3: -39 sent by 0 3 points sent Transaction now coming through... 2 chooses 5 Visiting Dealer_5: -5 sent by 2 6 points sent Transaction now coming through... 1 chooses 2 Visiting Dealer_2: 17 sent by 1 5 points requested Transaction now coming through... 4 chooses 0 Visiting Dealer_0: 16 sent by 4 5 points sent Transaction now coming through... 3 chooses 2 Visiting Dealer_2: 12 sent by 3 10 points sent Transaction now coming through... 5 chooses 1 Visiting Dealer_1: 4 sent by 5 3 points requested Transaction now coming through... 0 chooses 5 Visiting Dealer_5: 4 sent by 0 10 points sent Transaction now coming through... 2 chooses 1 Visiting Dealer_1: 1 sent by 2 3 points requested Transaction now coming through... 4 chooses 5 Visiting Dealer_5: 14 sent by 4 7 points sent Transaction now coming through... 3 chooses 1 Visiting Dealer_1: -2 sent by 3 1 points sent Transaction now coming through... 5 chooses 2 Visiting Dealer_2: 25 sent by 5 1 points sent Transaction now coming through... Server report indicates: Dealer_0: 11 Dealer_1: -1 Dealer_2: 26 Dealer_3: -47 Dealer_4: -9 Dealer_5: 20 (11)..(-1)..(26)..(-47)..(-9)..(20).. ---> 0 0 chooses 4 Visiting Dealer_4: -9 sent by 0 1 points sent Transaction now coming through... 1 chooses 0 Visiting Dealer_0: 10 sent by 1 4 points sent Transaction now coming through... 2 chooses 1 Visiting Dealer_1: -5 sent by 2 4 points requested Transaction now coming through... 3 chooses 1 Visiting Dealer_1: -9 sent by 3 8 points requested Transaction now coming through... 5 chooses 1 Visiting Dealer_1: -17 sent by 5 6 points requested Transaction now coming through... 0 chooses 1 Visiting Dealer_1: -23 sent by 0 10 points sent Transaction now coming through... 2 chooses 3 Visiting Dealer_3: -39 sent by 2 10 points sent Transaction now coming through... 4 chooses 1 Visiting Dealer_1: -13 sent by 4 10 points sent Transaction now coming through... 3 chooses 0 Visiting Dealer_0: 4 sent by 3 7 points sent Transaction now coming through... 5 chooses 0 Visiting Dealer_0: 11 sent by 5 3 points requested Transaction now coming through... ^Cfrilled.cs.indiana.edu%
This is how you start the server (dave):
This is how you start the first client (larry)frilled.cs.indiana.edu%java ServerImplementation 36091 dave
This is how you start the second client (michael):burrowww.cs.indiana.edu% java ClientImplementation larry frilled.cs.indiana.edu 36091 dave
This is how you start a third client (toni):tucotuco.cs.indiana.edu% java ClientImplementation michael frilled.cs.indiana.edu 36091 dave
And the four entities do work as expected.blesmol.cs.indiana.edu% java ClientImplementation toni frilled.cs.indiana.edu 36091 dave
Part Three. A simple automated chat system.
Take this as a potential exercise, although a solution will be presented.
Here's the local version of a (simple, automated) chat system:
Server.java
(the server interface)
ServerImplementation.java
(the server implementation)
Client.java
(the client interface)
ClientImplementation.java
(the client implementation)
Update.java
(the information passed around)
LocalSetup
(the local setup, test program)
Here's how this works:
This was just an example.frilled.cs.indiana.edu%javac *.java frilled.cs.indiana.edu%java LocalSetup Server being initialized ... clients: larry, michael, toni larry receives: ***(toni says: Howdy!)*** toni michael receives: ***(toni says: Howdy!)*** toni receives: ***(toni says: Howdy!)*** larry receives: ***(michael says: Howdy!)*** michael michael receives: ***(michael says: Howdy!)*** toni receives: ***(michael says: Howdy!)*** larry receives: ***(larry says: Howdy!)*** larry michael receives: ***(larry says: Howdy!)*** toni receives: ***(larry says: Howdy!)*** larry receives: ***(toni says: Howdy!)*** toni michael receives: ***(toni says: Howdy!)*** toni receives: ***(toni says: Howdy!)*** larry receives: ***(michael says: Howdy!)*** michael michael receives: ***(michael says: Howdy!)*** toni receives: ***(michael says: Howdy!)*** larry receives: ***(larry says: Howdy!)*** larry michael receives: ***(larry says: Howdy!)*** toni receives: ***(larry says: Howdy!)*** larry receives: ***(toni says: Howdy!)*** toni michael receives: ***(toni says: Howdy!)*** toni receives: ***(toni says: Howdy!)*** larry receives: ***(michael says: Howdy!)*** michael michael receives: ***(michael says: Howdy!)*** toni receives: ***(michael says: Howdy!)*** larry receives: ***(larry says: Howdy!)*** larry michael receives: ***(larry says: Howdy!)*** toni receives: ***(larry says: Howdy!)*** larry receives: ***(toni says: Howdy!)*** toni michael receives: ***(toni says: Howdy!)*** toni receives: ***(toni says: Howdy!)*** ^Cfrilled.cs.indiana.edu%
It doesn't always go in this order.
Now let's see what it takes to make this application distributed.
Although the modified source code is available the reader should take the above as a decisive hint.frilled.cs.indiana.edu%pwd /nfs/grouchy/home/user2/www/classes/a348-dger/sum2004/pasadena/partThree/two frilled.cs.indiana.edu%ls -ld *.java -rw------- 1 dgerman faculty 121 Jul 4 17:20 Client.java -rw------- 1 dgerman faculty 1295 Jul 4 17:25 ClientImplementation.java -rw------- 1 dgerman faculty 407 Jul 4 17:26 LocalSetup.java -rw------- 1 dgerman faculty 1575 Jul 4 17:21 NetworkPeer.java -rw------- 1 dgerman faculty 195 Jul 4 17:20 Server.java -rw------- 1 dgerman faculty 967 Jul 4 17:26 ServerImplementation.java -rw------- 1 dgerman faculty 189 Jul 4 17:01 Update.java frilled.cs.indiana.edu%diff Client.java ../one/Server.java 1,2c1,3 < public interface Client extends java.rmi.Remote { < public void update(Update event) throws java.rmi.RemoteException; --- > public interface Server { > public int register(Client client); > public void broadcast(Update event); frilled.cs.indiana.edu%clear frilled.cs.indiana.edu%pwd /nfs/grouchy/home/user2/www/classes/a348-dger/sum2004/pasadena/partThree/two frilled.cs.indiana.edu%ls -ld *.java -rw------- 1 dgerman faculty 121 Jul 4 17:20 Client.java -rw------- 1 dgerman faculty 1295 Jul 4 17:25 ClientImplementation.java -rw------- 1 dgerman faculty 407 Jul 4 17:26 LocalSetup.java -rw------- 1 dgerman faculty 1575 Jul 4 17:21 NetworkPeer.java -rw------- 1 dgerman faculty 195 Jul 4 17:20 Server.java -rw------- 1 dgerman faculty 967 Jul 4 17:26 ServerImplementation.java -rw------- 1 dgerman faculty 189 Jul 4 17:01 Update.java frilled.cs.indiana.edu%diff Server.java ../one/Server.java 1,3c1,3 < public interface Server extends java.rmi.Remote { < public int register(Client client) throws java.rmi.RemoteException; < public void broadcast(Update event) throws java.rmi.RemoteException; --- > public interface Server { > public int register(Client client); > public void broadcast(Update event); frilled.cs.indiana.edu%diff ServerImplementation.java ../one/ServerImplementation.java 1c1 < public class ServerImplementation extends NetworkPeer implements Server, java.rmi.Remote { --- > public class ServerImplementation implements Server { 4c4 < synchronized public int register(Client client) throws java.rmi.RemoteException { --- > synchronized public int register(Client client) { 8c8 < synchronized public void broadcast(Update event) throws java.rmi.RemoteException { --- > synchronized public void broadcast(Update event) { 20,30d19 < public static void main(String[] args) { < String < portNumber = args[0], < ownName = args[1]; < ServerImplementation here = new ServerImplementation(ownName); < here.startAsNetworkServer(ownName, < Integer.parseInt(portNumber)); < } < public void startAsClientOf(java.rmi.Remote peer) { < < } frilled.cs.indiana.edu%diff Client.java ../one/Client.java 1,2c1,2 < public interface Client extends java.rmi.Remote { < public void update(Update event) throws java.rmi.RemoteException; --- > public interface Client { > public void update(Update event); frilled.cs.indiana.edu%diff ClientImplementation.java ../one/ClientImplementation.java 1c1 < public class ClientImplementation extends NetworkPeer implements Runnable, Client { --- > public class ClientImplementation extends Thread implements Client { 8c8 < public void update(Update event) throws java.rmi.RemoteException { --- > public void update(Update event) { 14c14 < Thread.sleep((int)(Math.random() * 6000 + 10000)); --- > sleep((int)(Math.random() * 6000 + 10000)); 19,22c19,22 < public void startAsClientOf(java.rmi.Remote server) throws java.rmi.RemoteException { < this.id = ((Server)server).register(this); < this.server = (Server)server; < (new Thread(this)).start(); --- > public void startAsClientOf(Server server) { > this.id = server.register(this); > this.server = server; > this.start(); 24,36d23 < public static void main(String[] args) throws Exception { < String < ownName = args[0], < serverHostName = args[1], < serverPortNumber = args[2], < serverName = args[3]; < < ClientImplementation client = new ClientImplementation(ownName); < client.startAsNetworkClientOf(serverHostName, < Integer.parseInt(serverPortNumber), < serverName); // calls startAsClientOf < } < public void startAsLocalServer() { } frilled.cs.indiana.edu%diff Update.java ../one/Update.java 1c1 < public class Update implements java.io.Serializable { --- > public class Update { frilled.cs.indiana.edu%diff LocalSetup.java ../one/LocalSetup.java 2c2 < public static void main(String[] args) throws /*Remote*/Exception { --- > public static void main(String[] args) { frilled.cs.indiana.edu%
And build the distributed application as an exercise.
Part Five. A simple distributed whiteboard.
The structure of this application is similar to the one just presented.
Clients are a bit more complicated, but not from the network point of view.
There's a little bit of GUI in them, and that's all the extra complexity.
Here's the code we start from:
One should compile all this and run Simulation
.
Question is: how do we turn this into a distributed application?
Easy. We just apply our pattern.
(I am tempted to leave this as an exercise to the reader, because it is too easy.)
But I might also add the code soon, just as well.
Epilogue. The essence of it all.
The minimal possible exercise: odd/even
, the old chestnut.
Consider the following class of objects:
It implements half of an equation.class Alpha { Beta other; public boolean odd(int n) { if (n == 0) return false; else return other.even(n-1); } }
The other half is implemented by:
How do we put the two halves in motion?class Beta { Alpha other; public boolean even(int n) { if (n == 0) return true; else return other.odd(n-1); } }
First off, one of the two will start as a server, the other as a client.
The symmetry is perfect though, as we will see.
Each one of them would advertise a number of services:
Now we also need to start them up:interface AlphaServices { boolean odd(int n); } interface BetaServices { boolean even(int n); }
class Beta implements BetaServices { Alpha other; public boolean even(int n) { if (n == 0) return false; else return other.odd(n-1); } void startAsLocalServer() { } void startAsClientOf(java.rmi.Remote peer) { ((Alpha)peer).register(this); } void register(Alpha alpha) { this.other = alpha; } }
Alpha
has the exact same attitude:
The local setup now looks as follows:class Alpha implements AlphaServices { Beta other; public boolean odd(int n) { if (n == 0) return true; else return other.even(n-1); } void startAsLocalServer() { } void startAsClientOf(java.rmi.Remote peer) { ((Beta)peer).register(this); } void register(Beta beta) { this.other = beta; } }
Of course, for this to work we need he interfaces to match the generic type:class LocalSetup { public static void main(String[] args) { Beta beta = new Beta(); beta.startAsLocalServer(); Alpha alpha = new Alpha(); alpha.startAsLocalServer(); beta.startAsClientOf(alpha); alpha.startAsClientOf(beta); // start is a bit too much to say System.out.println(alpha.odd(5)); System.out.println(beta.even(5)); } }
And here's the end of the experiment:interface AlphaServices extends java.rmi.Remote { boolean odd(int n); } interface BetaServices extends java.rmi.Remote { boolean even(int n); }
Now we make this a distributed application:frilled.cs.indiana.edu%ls -ld *.java -rw------- 1 dgerman faculty 335 Jul 5 14:16 Alpha.java -rw------- 1 dgerman faculty 76 Jul 5 14:07 AlphaServices.java -rw------- 1 dgerman faculty 337 Jul 5 14:15 Beta.java -rw------- 1 dgerman faculty 75 Jul 5 14:08 BetaServices.java -rw------- 1 dgerman faculty 386 Jul 5 14:14 LocalSetup.java frilled.cs.indiana.edu%more *.java :::::::::::::: Alpha.java :::::::::::::: class Alpha implements AlphaServices { Beta other; public boolean odd(int n) { if (n == 0) return false; else return other.even(n-1); } void startAsLocalServer() { } void startAsClientOf(java.rmi.Remote peer) { ((Beta)peer).register(this); } void register(Beta beta) { this.other = beta; } } :::::::::::::: AlphaServices.java :::::::::::::: interface AlphaServices extends java.rmi.Remote { boolean odd(int n); } :::::::::::::: Beta.java :::::::::::::: class Beta implements BetaServices { Alpha other; public boolean even(int n) { if (n == 0) return true; else return other.odd(n-1); } void startAsLocalServer() { } void startAsClientOf(java.rmi.Remote peer) { ((Alpha)peer).register(this); } void register(Alpha alpha) { this.other = alpha; } } :::::::::::::: BetaServices.java :::::::::::::: interface BetaServices extends java.rmi.Remote { boolean even(int n); } :::::::::::::: LocalSetup.java :::::::::::::: class LocalSetup { public static void main(String[] args) { Beta beta = new Beta(); beta.startAsLocalServer(); Alpha alpha = new Alpha(); alpha.startAsLocalServer(); beta.startAsClientOf(alpha); alpha.startAsClientOf(beta); // start is a bit too much to say System.out.println(alpha.odd(5)); System.out.println(beta.even(5)); } } frilled.cs.indiana.edu%javac *.java frilled.cs.indiana.edu%java LocalSetup true false frilled.cs.indiana.edu%
We leave as an exercise for the reader to determine the differences with the local version.frilled.cs.indiana.edu%pwd /nfs/grouchy/home/user2/www/classes/a348-dger/sum2004/pasadena/lastPart/two frilled.cs.indiana.edu%ls -ld * -rw------- 1 dgerman faculty 1647 Jul 5 21:31 Alpha.java -rw------- 1 dgerman faculty 178 Jul 5 18:06 AlphaServices.java -rw------- 1 dgerman faculty 1702 Jul 5 21:34 Beta.java -rw------- 1 dgerman faculty 178 Jul 5 18:06 BetaServices.java -rw------- 1 dgerman faculty 465 Jul 5 21:18 LocalSetup.java -rw------- 1 dgerman faculty 1575 Jul 5 17:14 NetworkPeer.java frilled.cs.indiana.edu%javac *.java frilled.cs.indiana.edu%rmic Beta frilled.cs.indiana.edu%rmic Alpha frilled.cs.indiana.edu%
Here's how the distributed version runs:
One startsburrowww.cs.indiana.edu% java Alpha 36091 larry bobac.cs.indiana.edu 36092 michael Server is ready ... Press a key if the other server is up and running... Press a key when you want this to end ... Beta_Stub[RemoteStub [ref: [endpoint:[129.79.245.103:54554](remote),objID:[7c6768:fd925a2d2c:-8000, 0]]]] is registering into Alpha@1430b5c You are asking if 4 is odd... You are asking if 2 is odd... You are asking if 0 is odd... You are asking if 5 is odd... You are asking if 3 is odd... You are asking if 1 is odd... ^Cburrowww.cs.indiana.edu% bobac.cs.indiana.edu% java Beta 36092 michael burrowww.cs.indiana.edu 36091 larry Server is ready ... Press a key if the other server is up and running... You are asking if 5 is even... You are asking if 3 is even... You are asking if 1 is even... Is 5 even? The answer is: false You are asking if 4 is even... You are asking if 2 is even... You are asking if 0 is even... Is 5 odd? The answer is: true Press a key when you want this to end ... ^Cbobac.cs.indiana.edu% bobac.cs.indiana.edu%
Alpha
then Beta
.
And then keys are being pressed into Alpha
, and then in Beta
.
Beta
then prints the output, then one can close them (^-C
) in any order.