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]
- Very large IO library (60+ classes)
- "Old"-style
stream
classes
- Deal with bytes; not suitable for 2-byte chars
- Not eliminated from use; still need for binary
- "New"-style
reader/writer
classes
- Deal with 2-byte characters; e.g. for text files
- Still need to interact with stream classes
Decorator/filter design pattern [3/29]
- Both old and new follow decorator pattern
- Also known as the filter design pattern
- Layers of functionality added
- Decorate or filter stream to get desired access
- e.g. <-
ZipInputStream
<- FileInputStream
<-
- Extremely flexible; worth the trouble? No!
- Mix and match to get exactly what’s needed
- Learn the common combinations for now
Intro to InputStream
classes [4/29]
InputStream
- byte input superclass
- One abstract method:
int read()
- DataInput interface for binary files
- Methods for reading all primitive Java types
DataInputStream
- reading input stream
BufferedInputStream
- Reading from an input stream using a buffer
FileInputStream
- reading from files
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]
Reader
- character input superclass
- Two abstract methods:
read(...)
, close()
InputStreamReader
- byte to character
- Reads characters from
InputStream
BufferedReader
- reading buffered input
FileReader
- reading chars from files
LineNumberReader
- keeps track of lines
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]
OutputStream
- byte output superclass
- One abstract method:
write(int)
DataOutput
interface for binary files
- Methods for writing all Java primitive types
DataOutputStream
- writing output stream
BufferedOutputStream
- buffered
FileOutputStream
- output to a file
Intro to Writer
classes [9/29]
Writer
- character output superclass
- Abstract methods:
write
, flush
, close
OutputStreamWriter
- character to byte
- Writes characters to
OutputStream
BufferedWriter
- writing using buffer
FileWriter
- writing chars to files
PrintWriter
- textual rep of values
- Using familiar
print
and println
methods
Random access I/O [10/29]
RandomAccessFile
- Implements both
DataInput
and DataOutput
- Constructor parameter for read or read/write
- Does not delete existing file when writing
- Using any other output steam will!
seek
moves file pointer to byte location
writeInt
, readInt
, etc., to write/read data
File compression I/O (java.util.zip
) [11/29]
GZIPInputStream
, GZIPOutputStream
- Reading from and writing to a GZIP file
ZipInputStream
, ZipOutputStream
- Reading and writing entries in a Zip file
ZipEntry
class to hold entry information
- {
get
,put
}NextEntry
then normal file I/O
Checked
{Input
,Output
}Stream
- Computes checksum for validating file I/O
File Management [12/29]
File
class encapsulates management ops
- Can represent either a file or a directory
- Getting info about file (date, size, path, etc.)
- Renaming or removing a file
- Getting all files in a directory (
list
methods)
FilenameFilter
selects certain files
accept
method used to accept/reject files
list
methods take FilenameFilter
object
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]
- Object serialization mechanism
- Automatically reads/write entire object
- As well as connected objects
- Tag class:
implements Serializable
- Use
Object
{Input
,Output
}Stream
writeObject
serializes object automatically
readObject
deserializes (Object returned)
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:
-
Easier for the programmer.
-
Reliability: if the host of an object fails, move the
object to another machine.
-
Performance: if the host of an object cannot support its
clients, replicate the object on several hosts.
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]
- Distributed objects only use interfaces to interact with one another.
- Distributed object systems use an Interface Definition Language (IDL) to describe interfaces.
- IDLs need not be part of a programming language, but they often look suspiciously like C with extra keywords.
- Java-RMI uses Java interfaces, which are part of the Java
programming language. This is possible because Java-RMI is intended to
be used only between Java systems.
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:
- Download the three files into your 'Calculator' directory
- (Unlike
the socket code)To really run a distributed RMI program takes some
trivial system programming which you can find in the tutorial,
localhost is fine for our purposes for now (No matter what it will
still run throught the TCP/IP stack, this is a legitimate RMI program.)
-
Open three DOS/Command Prompt windows, and change to the
directory with the source code in each window.
-
Compile the Java source code:
javac *.java
-
Generate the stub and skeleton code:
rmic CalculatorImpl
-
In the first window, start the Java-RMI registry:
rmiregistry
-
In the second window, create the
CalculatorImpl
remote object:
java CalculatorImpl
-
In the third window, run the
CalculatorClient
:
java CalculatorClient
- This is a good time to use .bat files!
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?
-
The RMI registry is listening on port 1099.
-
The
CalculatorImpl
object is listening for
requests on port 3408.
CalculatorClient
opened a connection from
port 3416 to the RMI registry to lookup the Calculator
service (implemented by the CalculatorImpl
object).
CalculatorClient
opened a connection from
port 3417 to the CalculatorImpl
object.
Under the hood [24/29]
The client host supports two objects:
-
An instance of the
CalculatorClient
class.
The programmer writes this class.
-
A stub or proxy, which is an instance of the
CalculatorImpl_Stub
class. This class is
generated automatically by the RMI compiler.
The server host supports two objects:
-
An instance of the
CalculatorImpl
class.
The programmer writes this class.
-
A skeleton, which is an instance of the
CalculatorImpl_Skel
class. This class is
generated automatically by the RMI compiler.
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:
-
Open four DOS/Command Prompt windows, and change to the
directory with the source code in each window.
-
Compile the Java source code:
javac *.java
-
Generate the stub and skeleton code:
rmic ChatReceiverImpl
-
Generate the stub and skeleton code:
rmic ChatServerImpl
-
In the first window, start the Java-RMI registry:
rmiregistry
-
In the second window, create the
ChatServerImpl
remote object:
java ChatServerImpl
-
In the third window, create a
ChatReceiverImpl
object:
java ChatReceiverImpl
-
Create another
ChatReceiverImpl
object in the fourth window:
java ChatReceiverImpl
-
Now type a message into the third window and press
return/enter. It should appear on the second, third, and
fourth windows. You can also type messages into the
fourth window.
Last Exam Next Week, Last homework given today! [29/29]
-Exam 9 Next Week
-Homework 9 Due Before Class