Quick Review
Remote Method Invocation lets a method to be
'called'
on a remote object.
RMI is a Java API so the remote
object will be invoked on an object that is being hosted
on another
Java
Virtual Machine. RMI programs are
distributed
applications that will execute over different
JVMs running in different address spaces,
whether on
the same machine
or another located at a remote
location on the
net.
//
all Java method invocations between instances
// running on JVMs 'everywhere'
Distributed Object
Applications
In RMI, a client
will
use a local stub object to stand
in as a proxy or surrogate for the
remote object. The
remote object will be hosted on an RMI server. The
network transport
mechanism, that allows the client
and server to communicate is also supplied
by the
RMI system. Because RMI is an object-oriented
distributed system, programs based on RMI , are
also referred to as 'distributed object
applications'.
//
RMI is based on the Proxy pattern
The RMI Application in Detail
We can revisit our
earlier RMI example and use it to
point out the salient
aspects
of an RMI application.
First there is the RMI interface that
defines what
will
become the remote services.
The Remote Interface
The RMI remote
interface is used to declare a set of
methods that will
become
invocable services on another
Java Virtual Machine. An RMI Remote interface
always
extends directly or indirectly the Remote interface that
is defined
in the java.rmi
package. The java.rmi.Remote
interface is just a marker interface and
of itself
does not
define any methods.
The Remote interface defined in the java.rmi package
public interface Remote{ }
// The Remote interface is an empty 'marker' interface
Aside from
extending Remote, the method signatures
inside RMI's Remote interface must declare that they
throw the java.rmi.RemoteException.
In the next example
though a different interface then we
used in 'HelloRMI' it still has these hallmarks where it
extends the Remote interface and it's methods declare
that they throw java.rmi.RemoteException.
RMI Interface Example
public interface Message extends java.rmi.Remote{
The RemoteObject
Class and its Subclasses
Recall the our earlier example showed the Server's
main
method aside
from binding a name to an RMI object and
registering it with a naming service didn't really
do much.
( We did not see sockets creating or connections being
made etc.) That is because the connectivity code is
encapsulated in the UnicastRemoteObject
class which
is extended by our server.
Example
OurServer extends UnicastRemoteObject { // etc
Remote Object Hierarchy
UnicastRemoteObject is a
subclass of RemoteObject
found in the java.rmi.server package. RMI server
functions
are provided
by java.rmi.server.RemoteObject and its
subclasses,
RemoteServer &
UnicastRemoteObject,
and the java.rmi.activation.Activatable class.
Following
is a
diagram of the hierarchy of the RMI server
classes.
Hierarchy
of RMI Service Classes
java.lang.Object
|
RemoteObject
|
RemoteServer
|
Activatable
-- + -- UnicastRemoteObject
//
Server classses are in java.rmi.server package except for
// Activatable which is in the java.rmi.activation package
RemoteObject
RemoteObject provides implementations for class
Object's
methods hashCode( ),
equals( ), and toString( ), in a way
specialized
for the RMI environment. The methods
needed
to create and export remote objects are
supplied by the
descendent classes
UnicastRemoteObject and Activatable.
UnicastRemoteObject
& Activatable Classes
UnicastRemoteObject
class defines a simple
('unicast')
remote object whose references are
valid only while the
server process is
alive. The class Activatable is an
abstract class
that can be used to define a service
that
can be activated or instantiated from memory
so that
it's remote methods can
be called. Activatable is able
to activate persisted service objects as required.
We saw in our
previous RMI example that the class that
the server was
usually
an extension of UnicastRemoteObject.
Keep in mind, a remote object may also be an
extension of
another class that extends UnicastRemoteObject. Remote
objects might
also
be extensions of Activatable or an
Activatable subclass as well.
We don't cover
Activatable, however the following table
is supplied to facilitate investigation of the class.
Steps
in Running an Activatable Service Compile stages are similar to using UnicastRemoteObject ListServ Response 'Registry Persistence', Moises Lejter http://archives.java.sun.com/cgi-bin/wa?A2=ind0108&L=rmi-users&P=21673 To run an activatable service: 1. start RMI registry 2. start RMI activation daemon (RMID) 3. run server setup app 4. wait for clients. At step (3), the server setup app (i) creates an activation description, (ii) registers it with RMID and obtains a stub, (iii) registers said stub with RMI registry. Once this is all done, server is ready to activate the server instances on client demand. Sun Activation Samples 'Getting Started With RMI' http://java.sun.com/j2se/1.4.2/docs/guide/rmi/getstart.doc.html |
Method and
Interface Support in RMI Remote Servers
There is no limit
to how many Remote interfaces an
object can implement. As well, Remote objects are
not restricted to providing method implementations
for
Remote interfaces only. Implementation classes can
also define their own
methods but they are strictly for
local use and have no relationship to the remotely
accessed RMI system.
// an
implementation can host any number of remote interfaces
// an implementation can define it's
own non-RMI methods
Server Hosting a Remote
Implementation Object
In the following
server example the second variation
talked about in the preceding note is provided where
the RMI Remote implementation class is defined
separately from
the generic
server class. The server
class hosts this object.
First we create the
implementation class. First the RMI
package imports are supplied. Then we see the extension
of UnicastRemoteObject and
the
implementation of the
remote interface.
The
no-args( ) Constructor
Another key feature
to notice at this point is the no-args
constructor
that is declared
as throwing RemoteException
and invokes it's parent constructor via
a call to super( ).
This is an obligatory aspect of an remote implementation
class. If
there is any problem
creating the remote object,
the constructor will throw the RemoteException
that can
be used to process the system failure.
Finally the
abstract methods
of the remote interface must
be implemented. They are declared as having the potential
to throw RemoteException objects.
A Remote Implementation Class Code Sample
import java.util.Vector;
Now that the server has passed the RMI service duties
off to the implementation
it is obvious it has much less
work to do. Inside main(
), the implementation object
is
instantiated and bound to the rmi naming registry
via a
static call on the Naming
class method rebind( ). This
publishes
the
remote service and makes it available
for
remote invocation.
This example shows RMI's
native naming service, the
RMI
registry, called 'rmiregistry' being used. If
JNDI
was being used,
the code syntax of that registry
would
be used instead.
RMI Server Code Sample
import java.rmi.*;The Naming Class
The Naming class provides methods for storing
and
obtaining references to remote
objects in the remote
object registry. The Naming
class' methods take, as
one of
their arguments, a name that is a URL formatted
java.lang.String of the form:
URL Form of
Name Given the Naming Registry
// host:port / name
"where host is the host (remote or local) where
the registry
is located, port is the
port number on which the registry
accepts calls,
and where name is a simple string
that is
uninterpreted by the registry.
Both host and
port are optional. If host is omitted,
the
host defaults to the local host. If port
is omitted, then
the port defaults to 1099,
the well-known port that
RMI's registry
uses."
-
JDK Documentation
// if port is absent RMI's default port
is 1099
Details of Binding Names in the Registry
Binding a name for a remote object makes it available
for a lookup at a later time.
Once an object is bound in
the registry, a user
can look the object up by name,
obtain a reference on it and then invoke methods
on it.
The NotBoundException is
thrown if the name has not
been bound to an
object.
Why
rebind( )?
The bind( ) method binds the
specified name to the
remote object. It throws
AlreadyBoundException if
the name
is already bound to an object. To avoid
having the AlreadyBoundException thrown, the rebind( )
method is usually used
instead of the bind( ) method.
The rebind( )
method always binds the name to the
object even if the name is already bound. If
this is the
case, the old binding is lost.
//
rebind( ) always binds
There is also an unbind( ) method that will remove
a
binding between a name and
an object. The unbind( )
method will throw a
NotBoundException
if it finds no
binding exists.
The list( ) method can be used
to return a 'snapshot' of
the URLs that
are bound in the registry. Only the host
and
port information of the URL is needed
to contact a
registry for the list of its contents
Naming Remote Objects & The RMI Registry
Clients locate remote services by using a naming
or
directory service. RMI can be used
with a number of
directory services, including
JNDI or the Java Naming
& Directory Interface. RMI itself
includes a simple naming
service called the RMI Registry. This
program is invoked
using the executable called 'rmiregistry' at the command
line.
// the RMI registry needs to run on
the same machine as the service
JNDI
Available For Download Sun Microsystems has released JNDI as a Java Standard Extension. Sun Microsystems has also released service providers that plug in seamlessly behind JNDI for a number of naming and directory services: LDAP, NIS, CORBA (COS) Naming, and files. These and service providers produced by other vendors are available for download at the following link . http://java.sun.com/products/jndi/serviceproviders.html This enables you to use a more robust naming registry to be used with your RMI program. |
Exporting the Remote
Object
For security reasons, The RMI registry must be run on
the same machine that is hosting the
the remote service
objects. By default
the service is running on port 1099
which is
the 'well known' port for RMI. A different
port
can be specified at the command line. The
process of
binding an object and
registering it with the directory
service (which
we discussed earlier) is also referred
to
as 'exporting' the remote object. By either
description
the process involves making
the remote object available
for use by the RMI
client.
// 'exporting a remote object refers to
binding an object
// by name in a registry, so it can
be accessed remotely
We saw in the example above the rebind( ) method being
used. In the
client
the naming class is used again to lookup
services that have been registered
with
the RMI registry.
On the client side, if the standard RMI Registry
is being
accessed,
the static Naming class is again used, this time
with the method
lookup( ). This
method accepts a URL
that specifies the server host name and the name
of the
desired service. The method returns a remote reference
to the service
object.
The URL takes the form:
URL form of Remote Reference
Used in a Client Lookup
rmi://<host_name> [:<name_service_port>] / <service_name>
// where the host_name
is a name recognized on the local area
// network (LAN) or a DNS name on the Internet.
The name service
// port only needs to be specified only if the naming service is
// running on a different port to the default 1099.
Notice in the
following client example the lookup( ) method
returns a generic
object of type Remote first. This object is
then cast to the name of
the remote
interface that was used
to create it. In this case the generic remote
object is
cast to
InCoExchange. The Remote class is somewhat analogous
to the
Object
class in the regular Java hierarchy of classes.
The elegant aspect
of
the RMI system
is now evident in that
the remote object is now represented by a simple reference
variable which can be used to call the remote methods just
like any
other
object that has been defined and created locally
on the JVM.
RMI Client Setting Messages
// first client inserts message(s)
import
java.rmi.*;
public class MessageClientA{
public static void main(String[] args){
try{
Remote
remote_object=Naming.lookup("//localhost/memos");
Message messages=(Message)remote_object;
// setting some messages
String memo1 = "Dentist: 4:30, Friday July 7.";
messages.setMessage(memo1);
String memo2 = "Get construction material";
messages.setMessage(memo2);
}
catch(Exception e){
System.out.println
("Exception: looking up and accessing the
remote");
}
}
}
Second RMI Client Gets Messages
import java.rmi.*;
public class MessageClientB{
public static void main(String[] args){
try{
Remote
remote_object=Naming.lookup("//localhost/memos");
Message messages=(Message)remote_object;
// getting
messages
String reminders = messages.getMessages( );
System.out.println(reminders);
}
catch(Exception e){
System.out.println
("Exception: looking up and accessing the
remote");
}
}
}
// demonstrate at end as a reminder of process
// in context of exercise
Parameter Passing Inside The Java
Virtual Machine
Recall in a standalone Java program when a parameter
representing a primitive
value is passed to a method, a
copy of the parameter
is passed into the method.
When
passing in reference types a copy of the
address is
passed in as well (really a pointer) . A copy
of an address,
however, has the same effect
as passing in the actual
address.
Both addresses point to the same place. Passing
in reference types is called 'pass by reference'.)
is
In a regular Java
program the actual object isn't
passed
into the method. The reference suffices
because the
method can find the
object somewhere in the shared
address space.
With RMI the situation is more complicated because
the
environment is now a
distributed environment consisting
of more then
one address space. There are
three types
of parameters that are passed in
an RMI system, regular
primitives,
non-RMI objects and RMI objects.
These three
types are defined as being
serializable or
transferable over a distributed
network. RMI remote
and primitive types are treated as being naturally
serializable. Non-RMI reference
type objects must
implement the Serializable
interface in order for them
to be
treated as serializable.
Primitve Parameter Passing in RMI
With regular primitive types the procedure RMI
takes
is the same as in the
stand-alone environment. That is,
a copy of the
primitive is passed into and out
of RMI
remote method calls.
Example
int i=3.1322;
String dollars = remote.inDollars( i );
// primitive parameters are passed by
copy
Non-RMI Object Pass in RMI by Serialization
With regular non-RMI object types things are
different.
Passing the reference for
such an object is meaningless
as the remote service
does not have a representation
of
the object in it's address space. What is
required is a
copy of the whole object
needs to be passed over the
network medium to
the remote service for processing.
This requires the use of the 'serialization' process
where
the object is sent in serial
form with the information
needed to reconstruct
the object, and all the state
information as well. Non-RMI objects can
converted
into serializable objects by
implementing the Serializable
interface.
In a regular Java program that does not use RMI, the
serialization
mechanism can be used
as well. A class
is made serializable by implementing
the Serializable
interface. The
ObjectInputStream and ObjectOutputStream
have
methods that are then use to
stream the objects.
Simple example of a Serializable Object
class Memo implements Serializable{
String note="Remember the Memo";
}
Sample Code of the Serializable Object written to Stream
import java.io.*;
More
on
Serialization
Using Serialver Every serializable object is given a
version number.
The If a
new version of a serializable object needs to C:\NewCode\RMI>serialver Memo |
RMI Remote Object Passing
RMI remote objects on the other hand have
representation
on remote machines. In
the case where an object belonging
to the RMI
system is passed over the network
the whole
object does not need to be passed. When an RMI object
is sent or returned as a parameter of a method, sends a
'stub' or
'proxy' for the object to act as a reference for
the
RMI remote object.
// RMI Remote Object stubs are to be
distinguished
// from the Transport Stub
There is a
restiction on passing RMI remote objects. A
remote object passed as a parameter
can only implement
remote interfaces. All this
activity is hidden away in the
Remote
Reference Layer and so are not visible to the
RMI
developer.
Class Annotation
One of the key mechanisms used in the RMI system
is
class annotation. When an
object is sent from one virtual
machine to another in a remote method call, the
RMI
system annotates the class descriptor in
the call stream
with a URL describing
where the class can be found and
loaded from
by the receiving JVM. This is part
of the RMI
system's communication scheme where
classes can be
loaded on
demand from a variety of sources.
//
class annotation --> where the class can be found & loaded
Parameter Transmission
RMI uses a subclass
of ObjectOutputStream which is
found in the java.io package, to serialize parameters
to
stream. If a standard non-RMI object is being sent, the
writeObject(
) method
is used. Otherwise, if it is an RMI
remote object, the replaceObject(
) method is used in
conjunction with static toStub( ) method of RemoteObject
to send
the stub
for the RMI remote object.
Stubs ( & Skeletons )
Activities
Below the parameter
passing level, the Stubs (and
for older JDKs, Skeletons) make use of TCP/IP sockets
to make network connections. When a stub's
method
is
invoked, the stub takes care of the following functions.
(1) initiates a connection with the remote VM containing the
remote
object.
(2) writes
and transmits (marshals) parameters to the remote VM
(3) waits
for the result of the method invocation
(4) reads
(unmarshals) the return (value or exception)
(5) returns
the value to the caller
The stub code
encapsulates the Remote Reference layer
and the Stub &
Skeleton
transmission layers, hiding all
these details from the RMI developer
and user. The
skeleton ( or equivalent code in newer versions) receiving
an incoming method invocation will carry out the
following
steps.
(1) reads
(unmarshals) the parameters for the remote method
(2) invokes the method on the
actual remote object implementation
(3) writes and transmits
(marshals) the result (return value or
exception)
to the caller
As mentioned before the newer version of RMI contained
in
JDK1.2 and later, adds additional, stub
protocol code that
allows
the skeleton code to be eliminated
from the system.
Using
exportObject( ) method
// provides two-way remote calling
Sometimes a server
may which to make a 'return call'
or a what
is popularly
called a 'call back' to a client. In
this case the client will need
to act as an RMI server.
This would involve extending UnicastRemoteObject (or
perhaps it's relative,
Activatable)
and providing a naming
service. This may not be practical as the client
may not
be suited to
act as a server.
RMI supplies the
exportObject( ) method that the client
can use to send
the server a remote object to call
methods back on the client. This
is a static method
of
the UnicastRemoteObject class that may be called
as shown in the example
below.
Example
UnicastRemoteObject.exportObject (remote_object);
// a client may use
the static exportObject( ) to
// make an
object available for a Server to call
To run an RMI application, the supporting class files must
be placed
in locations that
can be found by the server and
the clients. For the server, the following
classes must
be
available to its class loader:
Server Class Requirements
For the client, the following classes must be available to its
class
loader:
Client Class Requirements
Automatic
Distribution of Classes
& the RMIClassLoader
Different
Class Loaders
Java has different
class loaders. Which works depends on
the environment in which they are used.
The standard
Java
class loader is called simply ClassLoader. Applets use
AppletClassLoader.
RMI has
special requirements for it's
class loader, being required to load
classes from
different
locations distributed over an network. The RMI class
loader
is called
the RMIClassLoader.
As noted above, RMI
requires all the class definitions
it needs to work
to be
available. The RMIClassLoader
works with the SecurityManager class
object
to create
a complex and dynamic class loading system. The
design permits
loading
classes from different types of
servers such as FTP and HTTP servers.
The design allows
classes to be centrally located in
one or two places for an entire
distributed system.
The
RMIClassLoader can be controlled by using a set of
properties
that are
specified when the Java Virtual
Machine is started. Following is the
format that is
followed to specify the properties.
Form to Set Codebase for JVM
java[ -D<PropertyName>=<PropertyValue>
] + <ClassFile>
The 'java.rmi.server.codebase' Property
The 'java.rmi.server.codebase' property can be used
to
specify a URL for servers
from which class files can be
loaded. Such servers may be using the File, FTP or
HTTP protocols. RMI does not
send class files
along
with the serialized objects.
Instead the JVM looks for
the embedded URL that
describes the class that was
annotated in the serialization stream. (This
is the same
class annotation process
we mentioned earlier.) The
JVM will then obtain
the file from the server that is
described by the annotated URL.
The 'java.rmi.server.useCodebaseOnly' Property
The property 'java.rmi.server.useCodebaseOnly' can
be
set to true to restrict the
JVM to loading classes from
either a location specified by the CLASSPATH variable
or a URL specified in this property. By using
different
combinations of the available system properties, a number
of different
RMI
system configurations can be created as
described in the RMI tutorial found
at the Sun site entitled,
'Fundamentals
of RMI tutorial', written by Govind Seshadri.
//
URL active as of June 2006
Variations on Class Distribution
Schemes for
RMI
Systems
Closed - A
Closed System describes the scenario when
all the classes used by
clients and the server are located
on the Java Virtual Machine and
are referenced
by the
CLASSPATH environment variable. No dynamic class
loading is
supported
in this configuration.
//
all classes available locally, no dynamic loading
Server Based -
In
a server based system Java applets
are used in conjunction
with RMI. This allows the applet
code and all associated RMI classes
to be loaded
from
the server's CODEBASE.
// all classes
loaded from server's codebase
Client Dynamic
- The tutorial above uses the 'Client
Dynamic' term to describe a system
where the primary
classes are loaded by
referencing the client JVM,
CLASSPATH
environment variable. Other supporting
classes are loaded by the
server's
RMIClassLoader at
a location specified by the
server, either from an HTTP
or
FTP network location. ( Classes are
loaded at the
Client
and otherwise undergo dynamic loading at the
server. )
//
class loading is initiated on the client and further
// loading is done by server from a
network location
Server Dynamic
- In the Server
Dynamic model the
primary classes are loaded
by referencing the CLASSPATH
environment variable of the JVM for the
server.
Supporting
classes are loaded by the java.rmi.server.RMIClassLoader
from an
HTTP or FTP server on the network at a location
specified by the client.
//
the server initiates class loading and the client loads
// additional classes dynamically
from a network location
Bootstrap Client - In the
BootStrap
configuration, all of
the client code is loaded
from an HTTP or FTP server
across the network. All the client stores
is a short
program
called the 'bootstrap loader' has enough code to make a
call
that starts the
loading process.
//
all client code is loaded from a network server
Bootstrap Server - In BootStrap
Server configuration,
all of the server code
is loaded from an HTTP or FTP
server located on the network. The only
code
residing
on the server machine is a small bootstrap loader.
//
a small bootstrap loader initiates loading all server
// code from a network server
The JDK documentation gives an insight into how RMI
systems go about making
connections. Inside the stub
code a Java class called the RMISocketFactory
is
used
to obtain client and server sockets for needed to make
RMI remote method calls.
By default
the socket factory
has a three-tiered strategy to making client socket
connections.
First, a
conventional socket connection to the remote
JVM is attempted.
If action is impeded by the presence
of a firewall, then the RMI runtime uses
the Hypertext
Transfer Protocol to try to connect to the server. If the
firewall
does not allow this
type of communication, then
a CGI-bin script via HTTP
is used to
POST the RMI call.
This 'HTTP tunneling' comes with a significant
performance
loss.
//
three tiered connection strategy starts with a regular RMI
// connection, then trys a HTTP
connection and finally an
// HTTP POST via a cgi-script
Using the Java-RMI CGI script exposes
a security risk as
the script can
be used to redirect any incoming request to
any
port bypassing the firewall entirely.
For these and other
reasons it may be better to disable the HTTP tunneling
feature
or the socket factory. This is done by setting the
java.rmi.server.disableHttp
property
to 'true' as is shown
in the following example.
Example
java.rmi.server.disableHttp=true
1) Which of the following statements is not correct?
a) The rmi Remote interface declares a single
method that must be implemented.
b) To use rmi begins with implementing the Remote
interface
c) The Remote interface is in the java.rmi package
d) Methods declared inside the remote method
be declared as throwing
rmi's RemoteException
exception.
2) Which is the top level class of the following?
a) UnicastRemoteObject
b) Activatable
c) RemoteServer
d) RemoteObject
3) Which of the following is not a key feature of the rmi implementation class.
a) A no-args constructor that throws RemoteException
b) an extension of a RemoteServer subclass
c) The implementation class will implement Remote
interface.
d) Remote methods are defined in the
implementation
class
4) The RMI Server does all but one of the following.
a) may set the
SecurityManager
b) Instantiates the remote object implementation
c) binds the object to a name in the the naming service
d) catches for RemoteException
5) RMI cannot pass which of the following objects?
a) primitive types
b) remote objects
c) class objects that are not extensions or implementations of any
interfaces
d) class objects that implement serializable
6) The codebase
attributes of different JVMs can be used
in combination
with
loading classes in the regular CLASSPATH
to create various paths to
classes
needed by the RMI system.
Which of the following systems as described
in
G. Seshadri's
RMI tutorial represents the least
dynamic system regarding
class
loading.
a) Closed
b) Server Based
c) Client Dynamic
d) Client Bootstrap
1) Create a Remote Interface with Three
Methods
Using the RMI
examples from the previous two notes,
create a complete RMI program that will provide three
functions that describe the services of three floors of
a department store. Alternatively a brief description
can be provided for three departments of a company.
These three methods
will be first provided as abstract
methods in an RMI Remote interface. Name the RMI
Remote interface appropriately to relate to the service
provided by the methods.
//
provide RMI extensions and have methods throws RMI exceptions.
2)
Create a Remote Implementation class
Create an RMI
implementation class that supplies
implementations for the three methods. Again keep
the name of the implementation related to the service.
The methods should each return a String that holds
the description of the floor or department.
//
provide the appropriate RMI extensions and implement
// the RMI Remote defined above
3) Create a server that hosts the
implementation class
Create a Server
named appropriately that instantiates
the implementation class you have created and binds
the service to a name in the RMI naming service.
4) Create a Client that acessesses the
Remote Services
// will do
appropriate lookups
5) Compile with
javac and rmic as needed. Run the system
and submit code and screen shots of the service output from
the client's DOS window.
OR
Do all the above
however create a Service that relates to
a topic of interest to you.
OR
Use the
exportObject( ) method to send a server an object
that can be used to get info back to the server from the
object. Location and Time information would be candidates
to return to a server that has been accessed.