CIS6930: Java Remote Method Invocation
Download
Report
Transcript CIS6930: Java Remote Method Invocation
CIS 5930-04 – Spring 2001
Part 5: Java RMI—Remote Method
Invocation
http://aspen.csit.fsu.edu/it1spring01
Instructors: Geoffrey Fox , Bryan Carpenter
Computational Science and Information Technology
Florida State University
Acknowledgements: Nancy McCracken
Syracuse University
[email protected]
1
Remote Method Invocation
Java RMI is a mechanism that allows a Java program
running on one computer (e.g., the client) to apply a
method to an object on a different computer (e.g., the
server).
In itself, the syntax of the remote invocation looks exactly
like an ordinary Java method invocation. The remote
method call can be passed arguments computed in the
context of the local machine. It can return arbitrary values
computed in the context of the remote machine. The RMI
system transparently forwards these arguments and
results.
RMI is an implementation of the of the Distributed Object
programming model—similar to CORBA, but simpler, and
specialized to the Java language
[email protected]
2
Example
Assume code running in the local machine holds a remote
reference to an object obj on a remote machine:
obj
res = obj.meth(arg) ;
ResType meth(ArgType arg) {
...
return new ResImpl(. . .) ;
}
Local Machine
Remote Machine
[email protected]
3
Central Components of Java RMI
Remote objects—these are normal Java objects, but
their class extends some RMI library class that
incorporates support for remote invocation.
Remote references—object references that effectively
refer to remote objects, typically on a different computer.
Remote interfaces—normal Java interfaces, that specify
the “API” of a remote object. They should extend the
marker interface, java.rmi.Remote. The remote
interface must be known to both the local and remote
code.
[email protected]
4
Some Supporting Technologies
Registries—places where the local machine initially
looks to find a reference to a remote object.
Serialization—reduction of Java objects to a
representation that can be communicated as a byte
stream (for arguments and results).
Dynamic class loading—needed in various places.
One example is when a remote method returns an object
whose class (e.g. ResImpl) was not previously known on
the calling machine. Also used for stubs—see later.
Security manager—used to control the behavior of code
loaded from a remote host.
[email protected]
5
Related Approaches to Networking
Sockets
– Traditionally quite hard to program, although the java.net
package makes it relatively easy to establish a socket network
connection to another host. Communication takes place via
streams, but you must define the detailed protocols for message
exchange yourself.
– High-level compared with Unix sockets, but low-level compared
to RMI.
Remote Procedure Call (RPC)
– An earlier UNIX protocol to allow calling remote procedures.
– Programmers register their application with a host port mapper.
– Protocols for parameter-passing support a limited number of
types.
CORBA
– See next semester’s course. . .
[email protected]
6
References
Core Java 2, Volume II, Chapter 5: “Remote
Objects”.
Java RMI, Troy Bryan Downing, IDG books, 1998.
“Getting Started Using RMI”, and other
documents, at:
http://java.sun.com/products/jdk/rmi/
[email protected]
7
Getting Started
[email protected]
8
The Remote Interface
In RMI, a common remote interface is the minimum
amount of information that must be shared in advance
between “client” and “server” machines. It defines a
high-level “protocol” through which the machines will
communicate.
A remote interface is an ordinary Java interface, which
must extent the marker interface java.rmi.Remote.
All methods in a remote interface must be declared to
throw the java.rmi.RemoteException exception.
[email protected]
9
A Simple Example
A file MessageWriter.java contains the interface
definition:
import java.rmi.* ;
public interface MessageWriter extends Remote {
void writeMessage(String s) throws RemoteException
;
}
This interface defines a single remote method,
writeMessage().
[email protected]
10
java.rmi.Remote
The interface java.rmi.Remote is a marker interface.
It declares no methods or fields; however, extending it
tells the RMI system to treat the interface concerned as
a remote interface.
In particular we will see that the rmic compiler
generates extra code for classes that implement remote
interfaces. This code allows their methods to be called
remotely.
[email protected]
11
java.rmi.RemoteException
Requiring all remote methods be declared to throw
RemoteException was a philosophical choice by the
designers of RMI.
RMI makes remote invocations look syntactically like
local invocation. In practice, though, it cannot defend
from problems unique to distributed computing—
unexpected failure of the network or remote machine.
Forcing the programmer to handle remote exceptions
helps to encourage thinking about how these partial
failures should be dealt with.
See the influential essay: “A Note on Distributed
Computing” by Waldo et al, republished in The Jini
Specification:
http://java.sun.com/docs/books/jini
[email protected]
12
The Remote Object
A remote object is an instance of a class that
implements a remote interface.
Most often this class also extends the library class
java.rmi.server.UnicastRemoteObject. This class
includes a constructor that exports the object to the RMI
system when it is created, thus making the object visible
to the outside world.
Usually you will not have to deal with this class
explicitly—your remote object classes just have to
extend it.
One fairly common convention is to name the class of
the remote object after the name of the remote interface
it implements, but append “Impl” to the end.
[email protected]
13
A Remote Object Implementation Class
The file MessageWriterImpl.java contains the class
declaration:
import java.rmi.* ;
import java.rmi.server.* ;
public class MessageWriterImpl extends UnicastRemoteObject
implements MessageWriter {
public MessageWriterImpl() throws RemoteException {
}
public void writeMessage(String s) throws RemoteException {
System.out.println(s) ;
}
}
[email protected]
14
Remarks
The constructor MessageWriterImpl() has an empty
body. But recall that if there is no explicit constructor
invocation in the body of a subclass constructor, it
implicitly invokes super().
Hence the vital constructor of UnicastRemoteObject is
called.
This constructor is declared to throw
RemoteException. The MessageWriterImpl()
constructor must be declared to throw this exception in
turn, otherwise there will be a compiler error message.
Of course the class must also define all the methods of
the remote interface MessageWriter, which it
implements.
[email protected]
15
Compiling the Remote Object Class
To compile classes that implement Remote, you must
use the rmic compiler. The reasons will be discussed
later. For example:
sirah$ rmic MessageWriterImpl
[email protected]
16
Client and Server Programs
We have completed the Java files for the remote object
class itself, but we still need the actual client and server
programs that use this class.
In general there are some pieces of administrivia one
has to deal with—publishing class files and installing
security managers.
To minimize distractions, we initially make the
simplifying assumption that both client and server have
copies of all class files for MessageWriter (e.g., they
may share access through shared NFS directories).
Then we also don’t need a security manager, because
all code is “local”, and therefore trusted.
[email protected]
17
A Server Program
We assume the file HelloServer.java contains the class
declaration:
import java.rmi.* ;
public class HelloServer {
public static void main(String [] args) throws Exception {
MessageWriter server = new MessageWriterImpl() ;
Naming.rebind(“messageservice”, server) ;
}
}
[email protected]
18
Remarks
To avoid cluttering this illustrative code with try-catch
statements, we simply have the main() method throw all
exceptions (not good practice in general).
This program does two things:
– It creates a remote object with local name server.
– It publishes a remote reference to that object with
external name “MessageWriter”.
The call to Naming.rebind() places a reference to
server in an RMI registry running on the local host (i.e.,
the host where the HelloServer program is run).
Client programs can obtain a reference to the remote
object by looking it up in this registry.
[email protected]
19
A Client Program
We assume the file HelloClient.java contains the class
declaration:
import java.rmi.* ;
public class HelloClient {
public static void main(String [] args) throws Exception {
MessageWriter server =
(MessageWriter) Naming.lookup(
“rmi://sirah.csit.fsu.edu/messageservice”) ;
server.writeMessage(“Hello, other world”) ;
}
}
[email protected]
20
Remarks
Again the program does two things:
– It looks up a reference to a remote object with
external name “MessageWriter”, and stores the
returned reference with local name server.
– Finally (!), it invokes the remote method,
writeMessage(), on server.
The call to Naming.lookup() searches in a remote RMI
registry. Its argument is a URL, with protocol tag “rmi”.
This example assumes the remote object lives on the
host “sirah”, and has been registered in the default RMI
registry (which happens to listen on port 1099) on that
machine.
[email protected]
21
Compiling and Running the Example
Compile HelloServer and HelloClient on their
respective hosts, e.g.:
sirah$ javac HelloServer
merlot$ javac HelloClient
Either ensure client and server share the current
directory, or copy all files with names of the form
MessageWriter * .class to the client’s current directory.
Then. . .
[email protected]
22
Running HelloClient/HelloServer
[email protected]
23
Running HelloClient/HelloServer
[email protected]
24
Running HelloClient/HelloServer
[email protected]
25
Running HelloClient/HelloServer
[email protected]
26
Running HelloClient/HelloServer
[email protected]
27
Remark on Using the RMI Registry
In this example we ran the RMI registry on its default port.
In general this is probably a bad idea, especially if the server
is used by many people, because there is no mechanism to
prevent interference.
It is better to start a registry a non-default port number of your
own choice, e.g.:
sirah$ rmiregistry 4956 &
The Naming calls become, e.g.:
Naming.rebind(“rmi://sirah.csit.fsu.edu:4956/messageservice”,
server) ;
Naming.lookup(“rmi://sirah.csit.fsu.edu:4956/messageservice”) ;
[email protected]
28
The Mechanics of Remote Method
Invocation
[email protected]
29
Is RMI a Language Extension?
Invocation of a method on a remote object reproduces
the “look and feel” of local invocation amazingly well.
Yet the internal mechanics of remote invocation are
much more complex than local invocation:
– Arguments—which may be objects of arbitrary complexity—are
somehow collected together into messages suitable for shipping
across the Internet.
– Results (or exceptions) are similarly shipped back.
Perhaps surprisingly, RMI involves essentially no
fundamental modification to the Java language,
compiler, or virtual machine.
The illusion of remote invocation is achieved by clever
libraries, plus one relatively simple “post-processor” tool
(rmic).
[email protected]
30
Exchanging Remote References
A good feature of RMI is that references to other remote
objects can be passed as arguments to, and returned as
results from, remote methods.
Starting with one remote object reference (presumably
obtained from an RMI registry) a client can, for example,
obtain references to additional remote objects—returned
by methods on the first one.
[email protected]
31
Example: a Printer Directory
Perhaps more relevant on LAN than the Internet, but it
illustrates the idea:
public interface Printer extends Remote {
void print(String document) throws RemoteException ;
}
public interface PrinterHub extends Remote {
Printer getPrinter(int dpi, boolean isColor)
throws RemoteException ;
}
A client might initially obtain a PrinterHub reference
from the RMI registry. The remote object contains some
table of printers on the network.
An individual Printer interface is returned to the client,
according to specifications given in getPrinter().
[email protected]
32
Remote References have Interface
Type
This is a powerful feature, but there is one interesting
restriction:
– If a particular argument or result of a remote method
itself implements Remote, the type appearing in the
method declaration must be a remote interface. The
declared type cannot be a remote implementation
class.
We have also seen earlier that the remote object
reference returned by Naming.lookup() can be cast to
the expected remote interface type.
– However, this reference cannot be cast it to the
implementation class of the remote object! A
ClassCastException will occur if you try.
[email protected]
33
Stubs
What this tells us is that, however they are obtained—
and however they look—remote references are not, in
reality, Java references to remote objects. They are
Java references to local objects that happen to
implement the same remote interfaces as the remote
objects concerned.
The local Java object referenced is actually an instance
of a stub class.
[email protected]
34
Some Important Parts of RMI
Stubs.
– Each remote object class has an associated stub class, which
implements the same remote interfaces. An instance of the stub
class is needed on each client. Client-side remote invocations
are “actually” local invocations on the stub class.
Serialization.
– Arguments and results have to be “marshaled”—converted to a
representation that can be sent over the Net. In general this is a
highly non-trivial transformation for Java objects. Serialization is
also used for distributing stubs.
The Server-side “Run-time System”.
– This is responsible for listening for invocation requests on
suitable IP ports, and dispatching them to the proper, locally
resident remote object.
[email protected]
35
Architecture
Internet
Client
Call stub method
locally
Client
Code
Call remote object
method locally
Send marshaled
arguments
Stub
Return value
or throw exception
Server
Send marshaled
result or
exception
RMI
“Run-time”
System
[email protected]
Remote
Object
Return value
or throw exception
36
The Role of rmic
The only “compiler” technology peculiar to RMI is the
rmic stub generator.
The input to rmic is a remote implementation class,
compiled in the normal way with javac (for example).
The stub generator outputs a new class that implements
the same remote interfaces as the input class.
The methods of the new class contain code to send
arguments to, and receive results from, a remote object,
whose Internet address is stored in the stub instance.
[email protected]
37
Example Operation of rmic
An earlier example of a remote implementation class:
public class MessageWriterImpl extends UnicastRemoteObject
implements MessageWriter {
...
public void writeMessage(String s) throws RemoteException
{
...
}
}
We issue the command:
rmic –v1.2 –keep MessageWriterImpl
– The flag –v1.2 avoids generation of unnecessary code, needed
only for backward compatibility (including “skeleton classes”).
– The flag –keep causes the intermediate Java source to be
retained.
Output files will be MessageWriterImpl_Stub.java and
[email protected]
38
MessageWriterImpl_Stub.class.
The Generated Stub Class
public final class MessageWriterImpl_Stub
extends java.rmi.server.RemoteStub
implements MessageWriter, java.rmi.Remote {
...
public MessageWriterImpl_Stub(java.rmi.server.RemoteRef ref) {
super(ref);
}
public void writeMessage(java.lang.String $param_String_1)
throws java.rmi.RemoteException {
try {
ref.invoke(this, $method_writeMessage_0,
new java.lang.Object[] {$param_String_1},
4572190098528430103L);
} ...
}
}
[email protected]
39
Remarks on the Stub Class
The stub class includes an inherited field ref, of type
RemoteRef.
Essentially the stub class is just a wrapper for this
remote reference.
Remote methods are dispatched through the invoke()
method on ref.
This is passed an array of Objects holding the original
arguments (in general it also returns an Object).
It is also passed arguments to identify the particular
method to be invoked on the server.
Essentially the stub wrapper is providing compile-time
type safety. The actual work is done in library classes
that don’t know the compile-time type in advance.
[email protected]
40
Marshalling of Arguments
Objects passed as arguments to invoke() must be
marshaled for transmission over the network.
The representation of a Java object inside the Java
Virtual Machine is complex:
– An object includes references to all its fields, which may
themselves be objects. These may in turn reference other
objects.
– An object will reference its class object, which contains runtime
information about the object’s type.
This internal representation is also not standardized—
different vendors implementation of the JVM will
certainly use different representations.
If objects are to be exchanged between JVMs, we need
a standardized way to encode all the information.
[email protected]
41
Object Serialization
Java has a general framework for converting objects
(and groups of objects) to an external representation
that can later be read back into an arbitrary JVM.
This framework is called Object Serialization.
Object serialization is very important to RMI, but it has
other applications as well.
For example, a running program can use object
serialization to dump the current state of a particular
object to a file. Much later, another program can read
the file and reincarnate an exact replica of the original
object. This is a mechanism for object persistence.
[email protected]
42
I/O Streams
The technology for serializing and deserializing objects is
found in a pair of the many I/O stream classes of Java.
In general an output stream (for example) can be
associated with various targets:
– a file, an Internet socket connection, an internal Java array of
bytes to which one is writing externally formatted data, etc.
The abstract superclass OutputStream provides lowlevel write methods like:
public void write(byte [] buffer) throws IOException {. . .}
Subclasses may override the implementation for a
particular output target.
Subclasses may also add extra methods that take more
general data, convert them to a byte array, then invoke
write() to do the final output.
[email protected]
43
Object Streams
ObjectOutputStream is a subclass that adds methods
including:
public void writeInt(int val) throws IOException {. . .}
public void writeFloat(float val) throws IOException {. . .}
etc, and most interestingly:
public void writeObject(Object obj) throws IOException, . . . {. . .}
Similarly ObjectInputStream extends InputStream and
adds:
public int readInt() throws IOException {. . .}
etc, and:
public Object readObject() throws IOException, . . . {. . .}
[email protected]
44
Using Object Streams
We can use the writeObject() method of an
ObjectOutputStream to write an object to a file, an
Internet socket connection, etc.
Later we use the readObject() method of an
ObjectInputStream to read an object from the same
file, the other end of the socket connection, etc.
When deserialization occurs, a new object is created in
the second JVM. As far as possible this is a perfect
replica of the the original object.
[email protected]
45
Serialization Preserves Object Graphs
Consider this binary tree node class:
class Node implements Serializable {
Node() {}
Node(Node left, Node right) {
this.left = left ;
this.right = right ;
}
private Node left, right ;
}
We create a small tree, d, by:
Node a = new Node(), b = new Node() ; // Leaves
Node c = new Node(a, b) ;
Node d = new Node(c, null) ;
[email protected]
46
Serializing and Deserializing a Tree
a
b
Write out the root of the tree:
out.writeObject(d) ;
c
d
a’
Read a node later by:
Node e = (Node) in.readObject() ;
b’
c’
e
The whole of the original tree is reproduced. Copies
a’, b’, c’ of the original sub-nodes are recreated
along with e. The pattern of references is preserved.
[email protected]
47
Referential Integrity is Preserved
This behavior is not limited to
trees.
a
In this example both b and c
reference a single object a.
Again the pattern of links is
preserved. When the root
object is reconstructed from its
serialized form, a single a’,
referenced twice, is also
created.
Generally referential integrity is
preserved amongst all objects
written to a single
ObjectOutputStream.
[email protected]
c
b
d
a’
c’
b’
e
48
The Serializable Interface
Serializable is another marker interface. An object’s
class must implement Serializable if it is to be passed
to writeObject(). If it doesn’t, a
NotSerializableException will be thrown.
Implementing Serializable doesn’t appear to affect the
way the Java compiler and JVM treat the class in
general—it seems to be simply a safety feature in
ObjectOutputStream.
[email protected]
49
Argument Passing in RMI
In general any object-valued argument or result of a
remote method must either implement Remote or
Serializable.
If the argument or result implements Remote, it is
effectively passed by (remote) reference.
If it implements Serializable, it is passed by serialization
and copying. Referential integrity is preserved within
the limits of the arguments of a single invocation, as
described above.
[email protected]
50
Passing by Remote Reference
The serialization stream used by RemoteRef.invoke()
is actually a subclass of ObjectOutputStream, which
has been customized to make certain substitutions in
the output stream.
In particular this stream recognizes the class of a
remote object passed to it, and if necessary replaces it
with a RemoteStub object in the output stream.
– It gets the stub from an implementation class using the
RemoteObject.toStub().
So even if a remote object implementation is passed as
an argument (by the host that holds the object), what will
be received is a stub.
Stub classes themselves implement Serializable. So
once references have been converted to stubs, they just
get passed from host to host by value.
[email protected]
51
Argument Passing Examples
To end this section we work through two examples that
are interesting in their own right, and also demonstrate
the two fundamental ways of passing arguments in RMI.
[email protected]
52
Example: a File Reading Service
The earlier “Hello” example involved a very trivial
remote I/O service, whereby a client could write a string
to the console of a server.
Here we generalize to a more useful service that reads
a file on the server and returns its contents to a client.
In particular it will illustrate how RMI implicitly uses
serialization for a java.util class, returned as a remote
method result.
[email protected]
53
The Remote Interface
The file FileSource.java:
import java.util.* ;
import java.io.* ;
import java.rmi.* ;
public interface FileSource extends Remote {
public Vector readFile(String File)
throws RemoteException, IOException ;
}
[email protected]
54
The Remote Object Implementation
The definition of FileSourceImpl:
public class FileSourceImpl extends UnicastRemoteObject
implements FileSource {
public FileSourceImpl() throws RemoteException {}
public Vector readFile(String file)
throws RemoteException, IOException {
Vector lines = new Vector() ;
BufferReader in = new BufferReader(new FileReader(file)) ;
while(true) {
String line = in.readLine() ;
if(line == null) break ;
lines.addElement(line) ;
}
return lines ;
}
}
[email protected]
55
The Server Program
The definition of the class FileServer:
public class FileServer {
public static void main(String [] args) throws Exception {
FileSource server = new FileSourceImpl() ;
Naming.rebind(
“rmi://sirah.csit.fsu.edu:4965/fileservice”,
server) ;
}
}
(We chose the registry port as a random number between
1024 and 64K.)
[email protected]
56
The Client Program
The definition of the class FileClient:
public class FileClient {
public static void main(String [] args) throws Exception {
FileSource server = (FileSource) Naming.lookup(
“rmi://sirah.csit.fsu.edu:4965/fileservice”) ;
Vector lines = server.readFile(“students”) ;
// The remote invocation
for(int i = 0 ; i < lines.size() ; i++)
System.out.println((String) lines.get()) ;
}
}
[email protected]
57
Compiling
On the server:
rmic FileSourceImpl
javac FileServer
– (Note rmic will automatically invoke javac if
FileSourceImpl.java has not previous been compiled. javac, in
turn, will notice a dependency on FileSource.java, and compile
that file if necessary.)
Copy the files FileSource.class and
FileSourceImpl_Stub.class from server to client.
– Later we will see how to use dynamic class loading to avoid
copying the stub class to the client manually.
On the client:
javac FileClient
[email protected]
58
Running FileClient/FileServer
[email protected]
59
Running FileClient/FileServer
[email protected]
60
Remarks
The file students lives on the server. It is not directly
accessible to the client.
The FileSource object reads the file, and saves its lines
in a Vector.
The Vector is returned as a result of the remote method
readFile(). Vector, and the String class of the included
elements, both implement Serializable. The return
value is transparently serialized in the server and
deserialized on the client.
We could also have used an array of String objects—an
array type is serializable if its component type is.
[email protected]
61
Example: The RMI Registry
The RMI registry is a process that normally runs on the
server.
At first sight the registry seems to have a privileged role
in RMI. Actually it is “just another” remote object.
[email protected]
62
The RMI Registry
Client
Server
Request
Reference
Client
Code
Registry Store
Reference
Remote
Object
[email protected]
63
The Registry Remote Interface
Instead of interacting with the registry indirectly through
the Naming class, it is possible to obtain a direct remote
reference to the registry object.
Its remote interface is defined in the package
java.rmi.registry. The interface includes:
public interface Registry extends Remote {
public Remote lookup(String name) throws . . . ;
public bind(String name, Remote obj) throws . . . ;
public rebind(String name, Remote obj) throws . . . ;
...
}
[email protected]
64
The LocateRegistry Class
This class constructs a stub for an existing Registry
object. (It can also create a new registry implementation
object, running in the current JVM.)
The interface includes:
public final class LocateRegistry {
public static Registry getRegistry(String host, int port)
throws . . . {. .
.}
public static Registry createRegistry(int port)
throws . . . {. .
.}
...
}
[email protected]
65
Using the Registry Interface Directly
An alternative definition of the class FileServer:
public class FileServer {
public static void main(String [] args) throws Exception {
FileSource server = new FileSourceImpl() ;
Registry reg =
LocateRegistry.getRegistry(“sirah.csit.fsu.edu”,
4965) ;
reg.rebind(“fileservice”, server) ;
//A remote method invocation
}
}
We are particularly interested in this example because it
involves passing a remote object implementation, server,
as an argument to a remote method, rebind().
[email protected]
66
Passing a Remote Object Argument
The server argument of rebind() is a true Java reference
to a remote object implementation.
When the invoke() call is made (on the remote reference
embedded in reg) it uses a customized object-stream
class. As mentioned earlier, this tests for instances of
remote objects among the arguments, and treats them
specially.
In the output stream, it will replace the implementation
object with a stub obtained by applying the
RemoteObject.toStub() method to server.
Thus the registry receives a stub.
This is a general mechanism that applies whenever a
locally resident remote object is passed as an argument to
a remote method.
[email protected]
67
Summary
In principle most of the features that have been
discussed in this section are hidden inside the
implementation of RMI. In an ideal world you would not
have to know about them.
In practice, to successfully deploy an RMI-based
application, you will probably need to at least be aware
of some fundamental issues.
You need to be aware of the existence of stub objects,
and the basic working of object serialization.
You should be aware that references to remote objects
are normally produced by creating a stub object on the
server, then passing this stub to registry and clients in
serialized form.
[email protected]
68
Dynamic Class Loading
[email protected]
69
Byte Code Instructions for Stubs?
As we have seen: before any client can use an RMI
remote object, it must receive a serialized stub object.
The serialized stub contains a remote reference. Data
fields of the reference may include information like:
– The name of the host where the remote object lives,
– Some port number on that host, where the RMI run-time system
is listening for invocation requests.
– Any other information needed to uniquely identify the remote
object within its host.
One thing serialized objects do not contain is the actual
JVM instructions (the byte codes), that implement
methods on the local object (e.g. the code for the
wrapper methods that dispatch invoke()).
[email protected]
70
Serialization Only Saves the Data
The Java serialization process stores all data fields from
the original object.
It does not store any representation of the code
associated with the methods in the object’s class.
When an object is deserialized (e.g. on some client), the
client JVM must have some way of loading a class file
that does contain this information.
If it cannot find a suitable class file, the deserialization
process will fail. You will see a
java.rmi.UnmarshalException thrown, with a nested
java.lang.ClassNotFoundException.
When you are doing development using RMI, you will
probably see this exception a lot!
[email protected]
71
Copying Stub Class Files
In RMI, there are at least two ways to get the class files
to the client.
The straightforward approach is to manually copy class
files for all stub classes to the client: either put them in
the current directory on the client, or in some directory
on the client’s CLASSPATH.
This approach is reliable, easy to understand, and
perhaps the best approach for initial experiments with
RMI.
Eventually you may find it is too limiting. One of the
benefits of the OO approach is supposed to be that the
user code (here the client) doesn’t need need to know
the exact implementation class in advance—only the
interface. But stubs are associated with the
implementation class.
[email protected]
72
Dynamic Class Loading
A more general approach is to publish implementation
class files that may be needed by clients on a Web
Server.
Although the serialized representation of an object does
not contain the actual information from the class file, the
representation can be annotated with a URL. This
specifies a Web Server directory from which the class
file can be downloaded.
When the object is deserialized, the client Java Virtual
Machine transparently downloads the byte codes from
the Web Server specified in the annotation. On the
client side, this process happens automatically.
[email protected]
73
Dynamic Class Loading
Serialized stub,
annotated with code-base:
http://myWWW/download/
Remote Object
(MyImpl instance)
Client
JVM
Server
Client
Request stub
class file
Web
Server
html/
download/
MyImpl_Stub.class
[email protected]
Server
(myWWW)
74
Remarks
In simple examples, the serialized stub will probably be
obtained through an RMI registry running on the server
(the same server where the remote object is running).
The two servers—the server where the remote object is
running, and the Web Server publishing the class files—
may, of course, be physically the same machine.
[email protected]
75
The java.rmi.server.codebase
Property
We need a way to cause serialized object
representations to be annotated with suitably chosen
URLs.
In principle this is straightforward. We set a property
called java.rmi.server.codebase in the JVM where the
stub (or serialized object in general) originates.
The value of this property is a code-base URL.
The RMI serialization classes read the code-base
property, and embed the URL they find there in the
serialized representation of arguments or results.
– Unless this JVM itself downloaded the class file for the object
from a Web server, in which case they embed the URL from
which the class was originally loaded.
[email protected]
76
Properties
java.rmi.server.codebase is one of the JVM default
system properties (see documentation for class
System).
Values of properties in general, and
java.rmi.server.codebase in particular, can be set on
the java command-line, or through “system calls”.
On the command-line, you can set the value of a
property with name prop to value val by using the –D
option, as in:
java –Dprop=val MyClass
Within a running program, you can call the static method
setProperty(), as in:
System.setProperty(prop, val) ;
Here both prop and val must be String-valued
expressions. (There [email protected]
a related getProperty()
method.)
77
Setting the Code-base
For example, our original HelloServer example might be run
as follows:
java –Djava.rmi.server.codebase=http://sirah.csit.fsu.edu/users/dbc/
HelloServer
This sets the java.rmi.server.codebase property to:
http://sirah.csit.fsu.edu/users/dbc/
This URL gets embedded in serialization streams created by
the HelloServer program.
If an object is subsequently recreated by deserialization in a
different JVM (and that JVM cannot find a local copy of the
associated class file) it will automatically request it from the
Web server sirah, looking in the document directory
users/dbc/.
[email protected]
78
Recap: Marshalling and the Codebase
When an object is marshaled for an RMI invocation, the
serialized object may be annotated with a URL:
– If the class of the object was originally loaded from the local
CLASSPATH, the embedded URL will be the value of the local
java.rmi.server.codebase property.
– If the class of the object was originally loaded dynamically from
some Web Server directory, the embedded URL will be the URL
for that directory.
[email protected]
79
Recap: Unmarshalling and Loading
When an object is unmarshaled after an RMI invocation,
if the class of the object is not already loaded into the
JVM, then:
– If the class can be found on the local CLASSPATH, it is loaded
from there.
– Otherwise, the class is loaded dynamically from a Web Server
directory specified in the URL embedded in the serialized
object.
If the loaded class uses further classes, not already
loaded into the JVM:
– If the used class can be found on the local CLASSPATH, it is
loaded from there.
– Otherwise, the class is loaded dynamically from the same URL
as the first class.
This rule applies recursively.
[email protected]
80
Security Managers
There is one more thing we need to worry about.
Before a Java application is allowed to download code
dynamically, a suitable security manager must be set.
This means a security policy must also be defined.
In general this is a complicated topic. We won’t go into
any detail: just give a recipe you can follow.
[email protected]
81
Setting the Security Manager
In an RMI application, if no security manager is set,
stubs and classes can only be loaded from the local
CLASSPATH.
To enable dynamic loading, issue the command:
System.setSecurityManager(new RMISecurityManager()) ;
at the start of the program.
You should do this in any application that may have to
download code—in the simple examples considered so
far this means RMI clients that need to download stubs.
This isn’t the end of the story. You also have to define a
new property: the java.security.policy property.
– In simple cases this property is needed for clients, whereas
java.rmi.server.codebase is needed for servers.
[email protected]
82
Defining a Security Policy
The simplest security policy you can define is a plain text file
with contents:
grant {
permission java.security.AllPermission “”, “” ;
};
This policy allows downloaded code to do essentially
anything the current user has privileges to do:
– Read, write and delete arbitrary files; open, read and write to
arbitrary Internet sockets; execute arbitrary UNIX/Windows
commands on the local machine, etc.
It is a dangerous policy if there is any chance you may
download code from untrustworthy sources (e.g. the Web).
For now you can use this policy, but please avoid
dynamically loading code you cannot trust!
[email protected]
83
The java.security.policy Property
If the text file containing our security policy is called (for
example) policy.all, the original HelloClient example
might now be run as follows:
java –Djava.security.policy=policy.all HelloClient
Alternatively this property can be set inside the program
using System.setProperty().
[email protected]
84
Using Dynamic Loading
In principle, modifying your RMI application to allow
dynamic loading of stub classes is now straightforward:
– Install the stub classes in a Web Server document directory.
– Set the java.rmi.server.codebase property for the server
application, to reference that Web Server directory.
– Create a security policy file on the client.
– Set the java.security.policy property for the client application.
– Set a security manager in the client.
This also works for any classes (not just stubs) whose
serialized form may be communicated via remote method
calls. You just need to reinterpret “server” and “client”
application according to the direction the serialized object
moves—as “source” and “destination” application.
In practice. . .
[email protected]
85
Anything that Can Go Wrong Will!
Unfortunately, there are many opportunities for trivial
mistakes.
Nearly all mistakes, or combinations of mistakes, seem
to produce the same error report—a
java.rmi.UnmarshalException, with a nested
java.lang.ClassNotFoundException.
This can get pretty frustrating. Suppose
originally you make two mistakes. By
trial and error you may successfully
correct one of them. But you never know
whether you made progress, because
correcting one mistake doesn’t change
the error message!
[email protected]
86
Tips for Dynamic Class Loading
First and foremost: make sure your application works
without dynamic class loading. Try copying the stub
files manually, first.
– Be aware of firewalls.
Consider using internal registries (see the following
slides).
Consider setting properties inside the Java code (or
perhaps inside scripts that run the java command) and
be very careful about typing errors!
– Don’t forget the “/” at the end of the code-base URL.
Make sure your dynamically loadable class files really
are visible on the Web.
– Typically the files need to be world readable.
[email protected]
87
Problems with the Registry
The registry is one source of problems.
If you use the rmiregistry command in conjunction with
dynamic stub loading, you must be very careful to
ensure that the registry program CAN NOT find the stub
class files on its own CLASSPATH (or in the directory
where the rmiregistry command is run).
If the registry does find the class files on its own
CLASSPATH, it will load them from there, and ignore
the code-base annotation it receives from the
original server!
Later when the registry passes stubs on to the client,
they are no longer annotated with the original codebase, and the client cannot find the class files!
[email protected]
88
Empty CLASSPATH for the Registry
So it is usually recommended you ensure the CLASSPATH
environment variable is empty when you run the
rmiregistry command, and make sure the registry is run in
a directory that contains no class files.
However this still leaves some pitfalls.
When the registry dynamically downloads the class file
MessageWriter_Stub.class, it will also look for
MessageWriter.class in the same place, because
recreating a stub instance depends on the remote
interface.
So you will have to publish MessageWriter.class on the
Web Server as well, if only for the benefit of the registry.
– “True” clients probably already have this interface definition
available on their local CLASSPATH.
[email protected]
89
Using An “Internal” Registry
An alternative approach is to use an “internal” registry
object, created inside the server program using the
createRegistry() method, e.g.:
import java.rmi.* ;
import java.rmi.registry.* ;
public class HelloServer {
public static void main(String [] args) throws Exception {
MessageWriter server = new MessageWriterImpl() ;
Registry reg = LocateRegistry.createRegistry(4965) ;
reg.bind(“messageservice”, server) ;
}
}
Now the registry simply shares the code-base property of
the server, and correctly annotates stubs sent to the client,
even though their class came from the local CLASSPATH.
[email protected]
90
Collecting Things Together
Finally we will go through the “Hello” example again, this
time illustrating changes to support dynamic loading of
the stub class.
There is no change to the remote MessageWriter
classes themselves.
However we will change the client and server
applications, and the deployment procedure.
[email protected]
91
The Server Program
The file HelloServer.java contains the declarations:
import java.rmi.* ;
public class HelloServer {
public static void main(String [] args) throws Exception {
final int regPort = Integer.parseInt(args [0]) ;
System.setProperty(“java.rmi.server.codebase”,
“http://sirah.csit.fsu.edu/users/dbc/”) ;
MessageWriter server = new MessageWriterImpl() ;
Registry reg = LocateRegistry.createRegistry(regPort) ;
reg.bind(“messageservice”, server) ;
}
}
[email protected]
92
Remarks
The most crucial change is that the server now sets the
java.rmi.server.codebase property.
In this simplified example the Web Server URL is “hardwired” into the code.
In “production quality” code, a better strategy may be to
first check (e.g. using getProperty()) whether a property
is defined on the command-line, and, if not, read it from
some “properties” file.
Here the port number on which the registry listens is
taken from the first command-line argument. A registry
is started internally.
[email protected]
93
A Client Program
The file HelloClient.java now contains the declarations:
import java.rmi.* ;
public class HelloClient {
public static void main(String [] args) throws Exception {
System.setProperty(“java.security.policy”, “policy.all”) ;
System.setSecurityManager(new
RMISecurityManager()) ;
MessageWriter server =
(MessageWriter) Naming.lookup(
“rmi://sirah.csit.fsu.edu:4965/messageservice”) ;
server.writeMessage(“Hello, other world”) ;
}
}
[email protected]
94
Remarks
A security policy and an RMI security manager is set.
Remarks on server code, concerning “hard-wiring” of
property values, apply equally here.
This version looks for a registry on port 4965 on sirah
(again hard-wired into the code).
[email protected]
95
Deployment
On the server:
rmic MessageWriterImpl
javac HelloServer
Copy the stub class file
MessageWriterImpl_Stub.class from server to an
appropriate Web Server directory. In our example this
will be /home/httpd/html/users/dbc/ on sirah.
Make sure the class file is visible: check with a browser!
Copy the interface definition (either
MessageWriter.java or MessageWriter.class will do)
from server to client.
On the client:
javac HelloClient
Make sure the file policy.all exists.
[email protected]
96
Running FileClient/FileServer
[email protected]
97
Running FileClient/FileServer
[email protected]
98
Running FileClient/FileServer
[email protected]
99
Running FileClient/FileServer
[email protected]
100
Remarks
It probably seems a lot of work—just to avoid manually
copying one stub file from the server to the client.
But this facility for dynamically down-loading class files
has more far-reaching implications.
It is applicable not only to stubs, but any object passed
through a remote method call, where the class of the
actual object received is a specialization (subclass or
implementation class) of the type declared in the remote
interface.
One can argue this kind of polymorphism is at the heart
of object-oriented programming. In this sense dynamic
class loading is a prerequisite for doing true objectoriented programming with remote objects.
[email protected]
101
Applets
[email protected]
102
Applets
An applet is Java GUI program, embedded in a Web
page (an HTML document).
Typical browsers (Internet Explorer, Netscape)
incorporate a Java Virtual Machine.
The class file for the applet is stored in the Web Server’s
document directory, alongside normal HTML files and
other downloadable data.
An applet class will be dynamically loaded into the
browser’s JVM the first time it encounters an HTML
<APPLET> or <OBJECT> tag requiring that class.
Hence the applet is executed on the client.
These tags can appear in any normal HTML document.
[email protected]
103
Applets Today
Java originally became famous as the language that
brought Web pages to life through applets.
Since then, other technologies for making Web pages
interactive have been widely adopted.
For various commercial and technical reasons applets
have decreased in popularity on the Internet. The virtual
machines in Web browsers have not kept step with
developments in the rest of the Java world.
Applets may still have a future in the Intranet context,
where companies can control versions of browser and
Java development software deployed. But on the
Internet, where many incompatible versions abound, the
future of traditional applets is somewhat unclear.
[email protected]
104
Applets in this Course
Caveats notwithstanding, an applet is one natural way
to provide a Web front-end to a stand-alone server
program, especially if the server is implemented as an
RMI object.
We wish to illustrate the principles of applet deployment,
but avoid getting bogged down in issues about
compatibility across browsers.
Hence we will only discuss Java 2, Swing applets.
These can be displayed in standard browsers, but the
browser must have the Java Plug-in installed.
Installing the plug-in is easy for Windows.
– To view the examples in this lecture you will need access to a
browser with the plug-in installed, or the ability to install the
plug-in on a networked PC.
– Alternatively, you can use the applet viewer from the JDK.
[email protected]
105
Writing Applets is Easy (if . . .)
This assumption makes writing applets very easy, if you
followed the earlier lecture set on the Java GUI!
In a nutshell, you convert a Swing or AWT-based GUI to
an applet GUI by changing the top level JFrame to a
JApplet, perhaps moving code from the body of the
main() method to the init() method of the JApplet.
There is a fly in the ointment: the applet will only
successfully execute in a browser if it respects the
security policy imposed by the browser.
[email protected]
106
Applet Security Policies
Usually browsers will not allow an applet to:
–
–
–
–
read or write a file on the local computer (the client),
find any sensitive information about the local computer,
find any information about the current user!
make an Internet connection to any computer, except the server
from which they were download (applets “can only phone
home”), or
– execute any program on the local computer.
An exception will be thrown in the applet if it tries to do
any of these things.
Note that applets specifically are allowed to
communicate back to the host from which they were
downloaded (otherwise they wouldn’t be at all useful for
our purposes).
[email protected]
107
Example: Application to Applet
Driving code for a trivial Swing application that reads entries
from a file and displays them in a list:
import java.util.* ;
import java.io.* ;
import java.awt.event.* ;
import javax.swing.* ;
. . . declaration of classes FilePanel and FileFrame . . .
public class FileDisplayTest {
public static void main(String [] args) {
FileDisplay frame = new FileDisplay() ;
frame.addWindowListener(new TerminationListener()) ;
frame.setVisible(true) ;
}
}
. . . declaration of class TerminationListener . . .
[email protected]
108
The FileDisplay class
The associated JFrame class is:
class FileDisplay extends JFrame{
public FileDisplay() {
Vector lines = new Vector() ;
. . . Read all lines from input file to lines . . .
setTitle(“File Entries”) ;
setSize(450, 300) ;
getContentPanel().add(new FilePanel(lines)) ;
}
}
The suppressed code reads lines from a file using a
FileReader stream and saves them in the Vector called
lines.
[email protected]
109
The FilePanel class
The definition of FilePanel is just:
class FilePanel extends JPanel {
public FilePanel(Vector lines) {
JList list = new JList(lines) ;
JScrollPane scrollPane = new JScrollPane(list) ;
add(scrollPane) ;
}
}
[email protected]
110
Conversion to an Applet
In this example there is no code in the main() method
that will be needed by the Applet. We simply dispense
with FileFrameTest class (and TerminationListener).
The FileDisplay class is changed to extend JApplet
instead of JFrame.
This class is made public, and we change the source file
name to FileDisplay.java.
[email protected]
111
A First Attempt at an Applet Class
Now the FileDisplay class in FileDisplay.java is:
public class FileDisplay extends JApplet {
public FileDisplay() {
Vector lines = new Vector() ;
. . . Read all lines from input file to lines . . .
getContentPanel().add(new FilePanel(lines)) ;
}
}
Note we have violated a security restriction, by
attempting to read a file. We will return to this issue
later.
[email protected]
112
Compiling the Applet
Copy the Java source to a suitable Web directory.
I will work in the directory:
/home/http/html/users/dbc/examples/
on the host:
sirah.csit.fsu.edu
Compile the applet source file in the usual way:
javac FileDisplay.java
Remember to make the class files world readable:
chmod o+r *.class
Forgetting to do this will lead to many problems!
– To be sure, check the permissions using ls –l.
– Look at the directory using a Web browser (go to URL
http:sirah.csit.fsu.edu/users/dbc/examples/). Can you see the
class files? Will the browser let you save the class files?
[email protected]
113
Create an HTML File
This will be the document people point their browser at.
It can be an arbitrarily complicated HTML document,
with surrounding text and images. For now, we use:
<HTML>
<TITLE>A File Display</TITLE>
<BODY>
<APPLET CODE=“FileDisplay.class”
WIDTH=300 HEIGHT=450></APPLET>
</BODY>
</HTML>
Note the WIDTH and HEIGHT parameters here replace
the setsize() call in a Swing application.
We will assume this text is stored in the file:
filedisplay.html
in the same directory as the applet class files.
[email protected]
114
Convert the HTML for the Plug-in
A Java 2 applet that is to displayed through the Java
Plug-in actually needs a more complicated HTML tag.
Sun provide a converter to add the extra HTML.
The converter is a Java program. On sirah you can use
the script htmlconv installed in /usr/local/jdk1.2.2/bin/.
It is a one-line script—if you prefer, check the definition
of the script file, and invoke the java program directly.
Otherwise, if you are happy to use the script, do:
htmlconv filedisplay.html –backup BAK
– This saves the original files in a subdirectory BAK/, then
overwrites them.
– If you don’t specify the -backup option, the original files get
saved in a subdirectory of “./..”. This is a poor default—if you
don’t have write-permission in this parent directory, the
command will fail.
[email protected]
115
Make Sure the HTML is Visible!
Make sure the file displayfile.html is world readable:
chmod o+r displayfile.html
Make sure the directory itself is visible:
chmod o+r .
[email protected]
116
Installing the Java Plug-in
To view this applet, you will need a browser with the
Java plug-in installed.
If you have a suitable PC where you can install this
software, go to:
http://javasoft.com/products/plugin/
and follow the link for JRE 1.3 for Microsoft Windows.
Download the executable file into a suitable directory on
your PC, and run it.
The plug-in is now installed.
– If you are unable to install the plug-in, but have a suitable Java
2 JDK installation, you can view the applet using the
appletviewer command instead. Pass the URL on the
command line.
[email protected]
117
Attempting to View the Applet
Now go to the URL:
http://sirah.csit.fsu.edu/users/dbc/examples/filedisplay.html
If things go properly, you should see a blank panel with
a message like:
Applet Loading. . .
On the browser status line you should see a message
like:
exception: access denied (java.io FilePermission infile read).
Here “infile” is the name of the file the applet attempts
to read. Trying to read it is a security violation.
The Applet failed. Exit the browser.
– If an applet you are debugging fails, it is nearly always a good
idea to exit the browser before trying to view the applet again.
[email protected]
118
Adding a File Reading Service
What we really want is for the Applet to read file entries
from a file on the server.
In fact this limited objective could be achieved with an
HTTP GET request, but a more general approach is to
use RMI.
[email protected]
119
An Updated File Server Program
A new definition for the class FileServer (see the
“Mechanics of RMI” section), supporting dynamic class
loading:
import java.rmi.* ;
import java.rmi.registry.* ;
public class FileServer {
public static void main(String [] args) throws Exception {
final int regPort = Integer.parseInt(args [0]) ;
System.setProperty(“java.rmi.server.codebase”,
“http://sirah.csit.fsu.edu/users/dbc/examples/”) ;
FileSource server = new FileSourceImpl() ;
Registry reg = LocateRegistry.createRegistry(regPort) ;
reg.bind(“fileservice”, server) ;
}
}
[email protected]
120
An Updated Client
Change FileDisplay to read lines from a file called “infile” on
the server:
public class FileDisplay extends JApplet {
public FileDisplay() {
Vector lines = new Vector() ;
try {
FileSource server = (FileSource)
Naming.lookup(“rmi://sirah.csit.fsu.edu:4965/fileservice”) ;
lines = server.readFile(“infile”) ;
// Remote method
getContentPane().add(new FilePanel(lines)) ;
} catch (Exception e) {
}
}
}
[email protected]
121
Deploying the Service
Copy the source files FileServer.java, FileSource.java
and FileSourceImpl.java to the Web Server directory
where the applet lives.
Compile the remote object and the server:
rmic FileSourceImpl
javac FileServer.java
Remember to make the class files world readable!:
chmod o+r *.class
Make sure there is a file called “infile” in this directory!
Run the server:
java FileServer 4965
View the applet with the browser while the server
program is running.
[email protected]
122
Class Loading in the Example
The applet class itself, FileDisplay, will be loaded
dynamically by the applet class loader in the browser.
Classes it uses directly, like FilePanel, and the FileSource
remote interface definition, will also be loaded by the applet
class loader.
By default, these class files will be downloaded from the
same directory as the original HTML.
When Naming.lookup() returns a FileSourceImp_Stub
object, this will be loaded by the RMI class loader in the
browser JVM.
As usual, the class file will be downloaded from a directory
determined by the java.rmi.server.codebase property in
the server program.
In the example, both loaders read from the same directory.
[email protected]
123
Running Server Programs in UNIX
In this example, we assumed the server program is
running in a window of its own.
In UNIX, you can run a process in the background by
adding an ampersand to the command:
java FileServer 4965 &
This process can go on running even after you log out.
If you use this mechanism, make sure you know how to
kill the process when it is no longer needed, e.g.:
sirah$ ps –udbc
user dbc
PID
TTY
TIME CMD
30399 ?
00:00:00 java
30355 pts/2
00:00:00 bash
...
sirah$ kill -9 30399 [email protected]
# Processes of
# The process ID124
What Did this Achieve?
The example—displaying a file from the server—was
contrived to be very simple.
But it should be clear that now we have a general
mechanism whereby an applet running in a browser can
communicate with a program running on the server.
Through remote methods, the applet client can
effectively cause arbitrary code to be executed on the
server.
This opens many options that are not available to an
applet by itself:
– The server program can do many things an applet itself cannot
do, due to security restrictions.
– Instances of the applet, running in browsers belonging to
different clients, can share access to data or databases that
reside on the server.
[email protected]
125
Some Additional Features of Applets
Applets are not a primary focus of these lectures, but we briefly
review some of their other properties.
In the FileDisplay example, the GUI was created in the constructor
of the applet.
This is slightly unusual for an applet. It is more traditional to do
initialization in a method called init()—one of several applet “eventhandling” methods:
init()
start()
stop()
destroy()
Actually these methods are not invoked in the AWT eventdispatching thread. You should take appropriate care, especially if
start() or stop() explicitly modifies the display—see the section
“Events Revisited” in the GUI lectures.
[email protected]
126
Life Cycle of an Applet
The system invokes four methods on JApplet:
void init()
when the applet is first loaded.
void start()
after init() and whenever the browser reloads the HTML page
containing the applet.
void stop()
whenever the browser leaves the HTML page containing the
applet.
void destroy()
before the browser shuts down.
Note that an applet is apparently never unloaded once it
has been loaded (until the browser exits). This can be
awkward for debugging (and presumably means the
browser JVM eventually fills up with classes).
[email protected]
127
Applet Parameters
The APPLET element in HTML can include parameter
definitions, e.g.:
<APPLET CODE=“FileDisplay.class”
WIDTH=300 HEIGHT=450>
<PARAM NAME=“file” VALUE = “infile”>
<PARAM NAME=“port” VALUE = “4965”>
</APPLET>
Inside the applet, these parameters are read by
getParameter(), e.g.:
String file = getParameter(“file”) ;
You are not allowed to call this method in the applet
constructor. This is one reason for doing initialization in
init().
Remember to convert the HTML again if you modify the
applet element!
[email protected]
128
An Improved Applet
This version will read the port number (and file name)
from the applet parameters:
public class FileDisplay extends JApplet {
FileSource server ;
FilePanel panel = new FilePanel() ;
public void init() {
try {
getContentPane().add(panel) ;
server = (FileSource)
Naming.lookup(“rmi://sirah.csit.fsu.edu:” +
getParameter(“port”) +
“/fileservice”) ;
} catch(Exception e) { . . . }
}
...
}
[email protected]
129
An Improved Applet
. . . it also reads the file again whenever the page is
refreshed:
public class FileDisplay extends JApplet {
...
public void start() {
try {
panel.setLines(server.readFile(getParameter(“file”)))
;
} catch(Exception e) { . . . }
}
}
We added a default constructor and setLines() method
to FilePanel(). Latter uses setListData() method of
JList.
[email protected]
130
stop()
If you are writing a more general applet that allows the
file to be updated, overriding the stop() method might
be one natural place to save locally cached data back to
the server.
[email protected]
131
Other Applications of RMI
[email protected]
132
Beyond Simple Client/Server
The preceding example illustrated how to use RMI to
implement a back-end server program for a client, which
might be an applet running in a Web browser.
RMI can do this easily, but it also allows one to do much
more general things.
[email protected]
133
Example: Exploiting Dynamic Loading
We emphasized in an earlier section that the dynamic
class loading implemented in RMI goes beyond
automated delivery of stub classes.
Objects of essentially any class can be returned by a
remote method, and the code will be dynamically
downloaded. For example, this may include code for
complex graphical user interfaces.
[email protected]
134
A Generic Service
One could define a generic service remote interface:
public interface GenericService extends Remote {
public GUI getGUI() ;
}
public interface GUI extends Serializable {
public void init() ;
}
The client simply does:
GenericService service = (GenericService) Naming.lookup(url)
;
GUI gui = service.getGUI() ;
gui.init() ;
// Start GUI running locally
All the client knows in advance are the two simple
interfaces GenericService and GUI. The code for the
actual service and GUI are downloaded dynamically.
The GUI might be an arbitrary Swing interface.
[email protected]
135
Example: Jini
Sun’s Jini is a framework for spontaneous discovery of
services that exist in a LAN (for example), and for
reliable federation of these services.
It makes essential (and creative) use of aspects of RMI
like dynamic class loading and call-backs (discussed
next).
The Jini lookup services generalize the RMI registry. In
Jini an arbitrary proxy object is installed in the lookup
services.
– The proxy is not restricted to be an RMI stub. It can be any
serializable object, typically including remote references to an
actual server object.
The code for the proxy is downloaded dynamically by
the client, on lookup.
[email protected]
136
Example: Call-backs
A client can itself provide a remote interface, by creating
its own remote object.
It can then pass a reference to itself to a server. Now
the server can initiate communication by calling remote
methods on the client. These are sometimes called callbacks.
In following pages, we outline a collaboration server
example. We omit many details.
In general we see that RMI allows one to create complex
“webs” of interacting objects.
[email protected]
137
Server Remote Interface
A collaboration server allows users to register it, and to
post messages.
It keeps a list of users. When any user posts a
message, it distributes it to all the other users.
public interface ChatServer extends Remote {
public void register(Chatter c, String name)
throws RemoteException ;
// c is a remote reference to client object.
public void postMessage(Message m)
throws RemoteException ;
...
}
The Message class must implement Serializable.
[email protected]
138
Client Remote Interface
The client itself is implemented as a remote object with
interface:
public interface Chatter extends Remote {
public void notify (Message m) throws RemoteException ;
...
}
The notify() method will be called by the server when a
message has been posted.
[email protected]
139
A Group of Interacting Objects
postMessage(m)
Remote Object
(Chatter_Impl)
Remote Object
(ChatServer_Impl)
notify(m)
Server
notify(m)
Client
Remote Object
(Chatter_Impl)
Client
[email protected]
notify(m)
Remote Object
(Chatter_Impl)
Client
140
Sketch of Server Implementation
public class ChatServerImpl extends UnicastRemoteObject
implements ChatServer {
private Vector chatters = new Vector();
public ChatServerImpl() throws RemoteException {}
public void register(Chatter c, String name) {
chatters.addElement (c);
}
public void postMessage (Message m) {
for(int i = 0 ; i < chatters.size() ; i++) {
Chatter chatter = (Chatter) chatters.get(i) ;
chatter.notify(Message m) ;
}
}
public static void main(String[ ] args)
. . . set security manager, bind to registry, etc . . .
}
}
[email protected]
141
Sketch of Client Implementation
public class ChatterImpl extends UnicastRemoteObject
implements Chatter {
public ChatterImpl(ChatApplet gui, String name)
throws RemoteException() {
this.gui = gui ;
server = (ChatServer)Naming.lookup(. . .);
server.register(this, name) ;
// Register self with chat server
}
public void notify(Message m) throws RemoteException
gui.displayMessage(m) ;
}
...
}
We assume this implementation object is created by an
applet, which passes itself to the remote object
constructor. It should implement displayMessage().
[email protected]
142
{
Synchronization
Where multiple clients may interact with the same object
(this means most useful services), one needs to pay
attention to issues of interference.
Remote invocations from different clients may execute
concurrently—in different threads—in the server
program’s JVM.
It can be a good idea to declare the implementation of
remote methods (in the definition of the implementation
class) with the synchronized modifier.
This avoids the dangerous situation in which methods
are being invoked simultaneously on a remote object by
several clients.
– Other clients will have to wait until the currently executing
method has completed—they will be serviced in turn.
– But now you must be wary of possible deadlocks.
[email protected]
143
Enterprise Java Beans
Enterprise Java Beans (EJB) is a specification for a
server software framework, emphasizing transactions in
distributed environments.
It uses the RMI as the basic communication model, but
has higher level—and more specialized—support for
automatically handling issues of concurrency and
transaction processing, and automatically managing
persistence (saving state of objects to databases).
[email protected]
144
Garbage Collection
For Java, an important issue is garbage collection, which
automatically deallocates memory for local objects.
Remote objects are also garbage collected as follows:
– A remote reference layer on the server keeps a reference count for
each locally held remote object implementation.
– A remote reference layer on the client notifies the server when its
locally held references to the object are no longer in use.
– When all references from all clients have gone (i.e. when the
reference count is zero), the server object is garbage collected.
But what if a client fails to notify the server?
– A client with a remote reference must periodically renew a lease
with the run-time system on the server.
– If the client holding the reference exits prematurely (without
notifying the server) it will also stop renewing its leases. If a lease
is not renewed on time, the server assumes the client has died,
and the reference count is decremented anyway.
[email protected]
145