CSC 224 Lecture Notes


Week 9: I/O and RMI



Questions on last week's quiz or homework? [1/29]
Java I/O (input/output) Menagerie [2/29]
Decorator/filter design pattern [3/29]
Intro to InputStream classes [4/29]
Example: BinaryIOTest.java [5/29]
Intro to Reader classes [6/29]
How do I get the lines numbered? [7/29]
Intro to OutputStream classes [8/29]
Intro to Writer classes [9/29]
Random access I/O [10/29]
File compression I/O (java.util.zip) [11/29]
File Management [12/29]
The long awaited cin [13/29]
Object I/O [14/29]
Distributed Object Systems [15/29]
Location Transparency [16/29]
Remote Method Invocation [17/29]
Interfaces [18/29]
Stubs and Skeletons [19/29]
Naming/Lookup [20/29]
The Server and Client Code [21/29]
Running the code [22/29]
Who's listening where? [23/29]
Under the hood [24/29]
Don't Call Me, I'll Call You [25/29]
Remote Object References [26/29]
RMI Chat Example [27/29]
Run it [28/29]
Last Exam Next Week, Last homework given today! [29/29]

Questions on last week's quiz or homework? [1/29]



Today is the last day to post your class review with the University.

Let's look at the homework...

Let's take this week's quiz...

Distance Learning Students: The final will be week 11; on Friday the 18th, from 7:00pm - 8:30pm, Computer Sci & Tech Center 222.
Only one student has requested a proctored exam at this point.

Java I/O (input/output) Menagerie [2/29]



Decorator/filter design pattern [3/29]



Intro to InputStream classes [4/29]



Example: BinaryIOTest.java [5/29]


1: import java.io.*; 2: 3: public class BinaryIOTest{ 4: public static void main(String[] args){ 5: double d; 6: int i; 7: boolean b; 8: String s; 9: try{ 10: DataOutputStream out = new DataOutputStream( 11: new BufferedOutputStream( 12: new FileOutputStream("file"))); 13: out.writeDouble(3.14); 14: out.writeInt(17); 15: out.writeBoolean(true); 16: out.writeUTF("This is a string."); 17: out.close(); 18: System.out.println("Successful output."); 19: 20: DataInputStream in = new DataInputStream( 21: new BufferedInputStream( 22: new FileInputStream("file"))); 23: d = in.readDouble(); 24: i = in.readInt(); 25: b = in.readBoolean(); 26: s = in.readUTF(); 27: in.close(); 28: System.out.println("Read:\n" + d + "\n" + i + "\n" + b + "\n" + s); 29: }catch (FileNotFoundException e) 30: { System.err.println(e.getMessage());} 31: catch (IOException e) 32: { System.err.println(e.getMessage());} 33: } 34: } BinaryIOTest.java
biot.bat
file

Intro to Reader classes [6/29]



How do I get the lines numbered? [7/29]


import java.io.*; public class LineNumberTest { public static void main(String[] args) { try { FileWriter out = new FileWriter(args[0] + ".html"); LineNumberReader in = // includes buffering already new LineNumberReader( new FileReader(args[0])); String line; // read lines and output numbered lines out.write(...);//really i have html tags in there out.flush(); while ((line = in.readLine())!=null){ out.write(in.getLineNumber() + ": " + line + "\n"); out.flush(); } out.write(...);//more html, download the code file to see out.flush(); out.close(); }catch (FileNotFoundException e) { System.err.println("Couldn't find LineNumberTest.java"); } catch (IOException e) { System.err.println("Problem reading from file"); } } } LineNumberTest.java

Intro to OutputStream classes [8/29]



Intro to Writer classes [9/29]



Random access I/O [10/29]



File compression I/O (java.util.zip) [11/29]



File Management [12/29]


1: import java.io.*; 2: 3: public class DirectoryLister { 4: public static void main(String args[]) { 5: File entry; 6: if (args.length == 0) { 7: System.err.println("Please specify a directory name."); 8: return; 9: } 10: entry = new File(args[0]); // user specified 11: 12: listDirectory(entry); 13: } 14: 15: public static void listDirectory(File entry) { 16: try { 17: if (!entry.exists()) { 18: System.out.println(entry.getName() + " not found."); 19: return; 20: } 21: 22: if (entry.isFile()) { 23: // Write the pathname of the file 24: System.out.println(entry.getCanonicalPath()); 25: } else if (entry.isDirectory()) { 26: // Create list of entries for this directory 27: String[] fileName = entry.list(); 28: if (fileName == null) return; 29: for (int i = 0; i<fileName.length; i++) { 30: // Create a File object for the entry 31: File item = new File(entry.getPath(), fileName[i]); 32: // List it by a recursive call. 33: listDirectory(item); 34: } 35: } 36: } catch(IOException e) { System.out.println("Error: " + e); } 37: } 38: } DirectoryLister.java dl.bat

The long awaited cin [13/29]


Just to do cin (at least one way to do it): ... BufferedReader br = new BufferedReader (new InputStreamReader (System.in)); String s = br.readLine(); ... We'll see it in action below


There is nothing you can't do with Java IO, but it get's ugly and it is beyond the scope of this class, here's a tutorial on the basic stuff

Object I/O [14/29]



Distributed Object Systems [15/29]


Many current programming languages are object-oriented.

There is a mismatch between traditional sockets and RPC and object-oriented languages.

A distributed object system allows objects that are distributed across multiple hosts to interact with one another.

Examples:


Location Transparency [16/29]


Distributed objects should (mostly) provide location transparency.

Location transparency in the context of distributed objects means that we do not know or care where a distributed object is located.

Advantages of location transparency:

Of course, things are not quite that easy...


Remote Method Invocation [17/29]


See tutorial

Distributed objects interact via Remote Method Invocation (RMI), just as normal objects interact via method invocation.

Terminology warning: RMI is used to name the concepts and the implementations.

At run-time on a particular system, some objects will be local and others will be remote.

If a method is invoked on a remote object, then a remote procedure call takes place.

The location of the remote object determines the destination of the remote procedure call.


Interfaces [18/29]


import java.rmi.Remote; import java.rmi.RemoteException; public interface Calculator extends Remote{ public int add (int x, int y) throws RemoteException; public int sub (int x, int y) throws RemoteException; public int mul (int x, int y) throws RemoteException; public int div (int x, int y) throws RemoteException; } Calculator.java

Stubs and Skeletons [19/29]


The interface is used to automatically generate code.

The stub code runs on the client and turns the local method invocation into a remote method invocation. The stub is also known as a proxy.

The skeleton code runs on the server. It receives a remote method invocation from the stub code, and forwards it to the real object.

With Java-RMI, the stub and skeleton code is generated from an interface using the RMI compiler (rmic).


Naming/Lookup [20/29]


How do distributed objects find out about one another?

A naming or lookup service is a separate server that records which distributed objects are available and where they can be found.

A server registers its objects with the naming service.

A client looks up a remote object in the naming service.

With Java-RMI, the RMI registry (rmiregistry) performs naming services for distributed objects.

CORBA has a similar mechanism: the CORBA Naming Service.


The Server and Client Code [21/29]


SERVER: import java.net.MalformedURLException; import java.rmi.Naming; import java.rmi.Remote; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; public class CalculatorImpl extends UnicastRemoteObject implements Calculator{ public static void main (String[] args) { try { new CalculatorImpl().go(); } catch (Exception e) { System.err.println (e); } } private void go () throws MalformedURLException, RemoteException{ Naming.rebind ("rmi://127.0.0.1/Calculator", this); } public CalculatorImpl () throws RemoteException{} public int add (int x, int y) throws RemoteException{ return x + y; } public int sub (int x, int y) throws RemoteException{ return x - y; } public int mul (int x, int y) throws RemoteException{ return x * y; } public int div (int x, int y) throws RemoteException{ return x / y; } } CLIENT: import java.net.MalformedURLException; import java.rmi.Naming; import java.rmi.NotBoundException; import java.rmi.RemoteException; public class CalculatorClient{ public static void main (String[] args){ try { new CalculatorClient ().go (); } catch (Exception e) { System.err.println (e); } } private void go () throws ArithmeticException, MalformedURLException, NotBoundException, RemoteException { Calculator calc = (Calculator) Naming.lookup ("rmi://127.0.0.1/Calculator"); System.out.println ("12 + 6 = " + calc.add (12, 6)); System.out.println ("12 - 6 = " + calc.sub (12, 6)); System.out.println ("12 * 6 = " + calc.mul (12, 6)); System.out.println ("12 / 6 = " + calc.div (12, 6)); } } CalculatorImpl.java
CalculatorClient.java

Running the code [22/29]


Instructions:

Warning: if your network is misconfigured, you may get strange errors when you try Java-RMI examples. Try running it in the Software Exploration labs in the CS&T building instead. It does work there.


Who's listening where? [23/29]


On a trial run:

$ netstat -a --inet
Proto Local Address           Foreign Address         State
tcp   *:1099                  *:*                     LISTEN
tcp   *:3408                  *:*                     LISTEN
tcp   localhost:3416          localhost:1099          TIME_WAIT
tcp   livy.cs.depaul.edu:3417 livy.cs.depaul.edu:3408 TIME_WAIT

What does this mean?


Under the hood [24/29]


The client host supports two objects:

The server host supports two objects:

The classes CalculatorImpl_Stub and CalculatorImpl implement the interface Calculator.

For example, the add method is invoked by the instances of CalculatorClient and CalculatorImpl_Skel. This is regular method invocation.

The RMI registry only needs to be contacted once, not once for every remote method invocation.

Provides the illusion of location transparency.


Don't Call Me, I'll Call You [25/29]


How can a server interact with a client?

In the calculator example, a client performs a remote method invocation, and the server returns some data.

But what if the server wants to initiate an interaction?

For example, a chat server needs to be able to notify all clients that one client has sent a message.

A generic solution is for the client to poll the server:

Child -> Parent: Are we nearly there yet?
Parent -> Child: No.
Child -> Parent: Are we nearly there yet?
Parent -> Child: No!
Child -> Parent: Are we nearly there yet?
Parent -> Child: No!!
Child -> Parent: Are we nearly there yet?
Parent -> Child: Yes.
        

For the chat server, each client repeatedly asks the server whether it has any new messages. The client may pause between questions.

Polling is not restricted to RMI, nor even to distributed systems.

Sometimes polling is the only solution, e.g., if the client is behind a firewall.

Polling can be an inefficient use of resources (network traffic, processor time).

The alternative is for the server to initiate an interaction with the client:

Child -> Parent: Please tell me when we have arrived.
...time passes...
Parent -> Child: We are there now.
        

This is a callback.

Callbacks are often used in non-distributed systems, e.g., graphical user interface programming, parsing XML documents.

The client (who wants to be called back) must tell the server (who will call back) how to contact the client.

The server will need to know the client's IP address and a port number.

Callbacks are straightforward with RMI. The client passes a remote object reference to the server, and the remote object reference knows how to contact the client on behalf of the server.

With Java-RMI, the RMI registry is not involved when the client passes a remote object reference to the server.


Remote Object References [26/29]


Primitive data types such as booleans, integers, and floating point numbers are marshalled and transmitted.

Sometimes objects are marshalled and transmitted in their entirety. This leads to duplication and call-by-value parameter passing.

Sometimes a remote object reference is sent instead of the object.

How can we tell for Java-RMI?

If an object's class implements the java.rmi.Remote interface, then a remote object reference is sent instead of a copy of the object.

If the client sends a remote object reference to the server, then the server can call back to the client by remote method invocations on the remote object reference. The client becomes a server!


RMI Chat Example [27/29]


CLIENT INTERFACE: import java.rmi.Remote; import java.rmi.RemoteException; public interface ChatReceiver extends Remote{ public void receive (String message) throws RemoteException; } CLIENT IMPLEMENTATION: import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.IOException; import java.net.MalformedURLException; import java.rmi.Naming; import java.rmi.NotBoundException; import java.rmi.Remote; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; public class ChatReceiverImpl extends UnicastRemoteObject implements ChatReceiver{ public static void main (String[] args){ try { new ChatReceiverImpl ().go (); } catch (Exception e) {System.err.println (e);} } private void go () throws IOException, MalformedURLException, NotBoundException, RemoteException{ ChatServer cs = (ChatServer) Naming.lookup ("rmi://127.0.0.1/ChatService"); cs.register (this); BufferedReader br = new BufferedReader (new InputStreamReader (System.in)); String message; while ( (message = br.readLine ()) != null) { cs.send (message); } } public ChatReceiverImpl () throws RemoteException{} public void receive (String message) throws RemoteException{ System.out.println (message); } } SERVER INTERFACE: import java.rmi.Remote; import java.rmi.RemoteException; public interface ChatServer extends Remote{ public void register (ChatReceiver cr) throws RemoteException; public void send (String message) throws RemoteException; } SERVER IMPLEMENTATION: import java.net.MalformedURLException; import java.rmi.Naming; import java.rmi.Remote; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; import java.util.ArrayList; import java.util.List; import java.util.Iterator; public class ChatServerImpl extends UnicastRemoteObject implements ChatServer{ public static void main (String[] args){ try { new ChatServerImpl ().go (); } catch (Exception e) { System.err.println (e);} } private void go () throws MalformedURLException, RemoteException{ Naming.rebind ("rmi://127.0.0.1/ChatService", this); } private List receivers; public ChatServerImpl () throws RemoteException{ receivers = new ArrayList (10); } public void register (ChatReceiver cr) throws RemoteException{ receivers.add (cr); } public void send (String message) throws RemoteException{ System.out.println ("ChatServerImpl received: " + message); Iterator iter = receivers.iterator (); while (iter.hasNext ()) { ChatReceiver cr = (ChatReceiver) iter.next (); cr.receive (message); } } } ChatReceiver.java
ChatReceiverImpl.java
ChatServer.java
ChatServerImpl.java

Run it [28/29]


Instructions:


Last Exam Next Week, Last homework given today! [29/29]


-Exam 9 Next Week
-Homework 9 Due Before Class