Nothing Special   »   [go: up one dir, main page]

Java Programming Unit 2

Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 39

UNIT -2

2.1 RMI (Remote Method Invocation)


1. Remote Method Invocation (RMI)
2. Understanding stub and skeleton
1. stub
2. skeleton
3. Requirements for the distributed applications
4. Steps to write the RMI program
5. RMI Example

The RMI (Remote Method Invocation) is an API that provides a mechanism to create
distributed application in java. The RMI allows an object to invoke methods on an
object running in another JVM.

The RMI provides remote communication between the applications using two objects
stub and skeleton.

Understanding stub and skeleton

RMI uses stub and skeleton object for communication with the remote object.

A remote object is an object whose method can be invoked from another JVM. Let's
understand the stub and skeleton objects:

stub

The stub is an object, acts as a gateway for the client side. All the outgoing requests
are routed through it. It resides at the client side and represents the remote object.
When the caller invokes method on the stub object, it does the following tasks:

1. It initiates a connection with remote Virtual Machine (JVM),


2. It writes and transmits (marshals) the parameters to the remote Virtual Machine (JVM),
3. It waits for the result
4. It reads (unmarshals) the return value or exception, and
5. It finally, returns the value to the caller.

skeleton

The skeleton is an object, acts as a gateway for the server side object. All the
incoming requests are routed through it. When the skeleton receives the incoming
request, it does the following tasks:

1. It reads the parameter for the remote method


2. It invokes the method on the actual remote object, and
3. It writes and transmits (marshals) the result to the caller.
In the Java 2 SDK, an stub protocol was introduced that eliminates
the need for skeletons.

Understanding requirements for the distributed


applications
If any application performs these tasks, it can be distributed application.

1. The application need to locate the remote method


2. It need to provide the communication with the remote objects, and
3. The application need to load the class definitions for the objects.

The RMI application have all these features, so it is called the distributed application.

Java RMI Example


The is given the 6 steps to write the RMI program.

1. Create the remote interface


2. Provide the implementation of the remote interface
3. Compile the implementation class and create the stub and skeleton objects using the rmic
tool
4. Start the registry service by rmiregistry tool
5. Create and start the remote application
6. Create and start the client application
RMI Example
In this example, we have followed all the 6 steps to create and run the rmi application.
The client application need only two files, remote interface and client application. In
the rmi application, both client and server interacts with the remote interface. The
client application invokes methods on the proxy object, RMI sends the request to the
remote JVM. The return value is sent back to the proxy object and then to the client
application.

1) create the remote interface

For creating the remote interface, extend the Remote interface and declare the
RemoteException with all the methods of the remote interface. Here, we are creating
a remote interface that extends the Remote interface. There is only one method named
add() and it declares RemoteException.

1. import java.rmi.*;  
2. public interface Adder extends Remote{  
3. public int add(int x,int y)throws RemoteException;  
4. }  
2) Provide the implementation of the remote interface

Now provide the implementation of the remote interface. For providing the
implementation of the Remote interface, we need to

 Either extend the UnicastRemoteObject class,


 or use the exportObject() method of the UnicastRemoteObject class

In case, you extend the UnicastRemoteObject class, you must define a


constructor that declares RemoteException.

1. import java.rmi.*;  
2. import java.rmi.server.*;  
3. public class AdderRemote extends UnicastRemoteObject implements Adder{  
4. AdderRemote()throws RemoteException{  
5. super();  
6. }  
7. public int add(int x,int y){return x+y;}  
8. }  

3) create the stub and skeleton objects using the rmic tool.

Next step is to create stub and skeleton objects using the rmi compiler. The rmic tool
invokes the RMI compiler and creates stub and skeleton objects.

1. rmic AdderRemote  

4) Start the registry service by the rmiregistry tool

Now start the registry service by using the rmiregistry tool. If you don't specify the
port number, it uses a default port number. In this example, we are using the port
number 5000.

1. rmiregistry 5000  

5) Create and run the server application

Now rmi services need to be hosted in a server process. The Naming class provides
methods to get and store the remote object. The Naming class provides 5 methods.
public static java.rmi.Remote
lookup(java.lang.String) throws It returns the
java.rmi.NotBoundException, reference of the
java.net.MalformedURLException, remote object.
java.rmi.RemoteException;
public static void bind(java.lang.String,
java.rmi.Remote) throws It binds the remote
java.rmi.AlreadyBoundException, object with the
java.net.MalformedURLException, given name.
java.rmi.RemoteException;
public static void unbind(java.lang.String) It destroys the
throws java.rmi.RemoteException, remote object which
java.rmi.NotBoundException, is bound with the
java.net.MalformedURLException; given name.
public static void rebind(java.lang.String, It binds the remote
java.rmi.Remote) throws java.rmi.RemoteException, object to the new
java.net.MalformedURLException; name.
It returns an array
public static java.lang.String[]
of the names of the
list(java.lang.String) throws
remote objects
java.rmi.RemoteException,
bound in the
java.net.MalformedURLException;
registry.

In this example, we are binding the remote object by the name sonoo.

1. import java.rmi.*;  
2. import java.rmi.registry.*;  
3. public class MyServer{  
4. public static void main(String args[]){  
5. try{  
6. Adder stub=new AdderRemote();  
7. Naming.rebind("rmi://localhost:5000/sonoo",stub);  
8. }catch(Exception e){System.out.println(e);}  
9. }  
10. }  

6) Create and run the client application

At the client we are getting the stub object by the lookup() method of the Naming
class and invoking the method on this object. In this example, we are running the
server and client applications, in the same machine so we are using localhost. If you
want to access the remote object from another machine, change the localhost to the
host name (or IP address) where the remote object is located.
1. import java.rmi.*;  
2. public class MyClient{  
3. public static void main(String args[]){  
4. try{  
5. Adder stub=(Adder)Naming.lookup("rmi://localhost:5000/sonoo");  
6. System.out.println(stub.add(34,4));  
7. }catch(Exception e){}  
8. }  
9. }  

download this example of rmi

1. For running this rmi example,  
2.   
3. 1) compile all the java files  
4.   
5. javac *.java  
6.   
7. 2)create stub and skeleton object by rmic tool  
8.   
9. rmic AdderRemote  
10.   
11. 3)start rmi registry in one command prompt  
12.   
13. rmiregistry 5000  
14.   
15. 4)start the server in another command prompt  
16.   
17. java MyServer  
18.   
19. 5)start the client application in another command prompt  
20.   
21. java MyClient  

Output of this RMI example


Meaningful example of RMI application with database

Consider a scenario, there are two applications running in different machines. Let's
say MachineA and MachineB, machineA is located in United States and MachineB in
India. MachineB want to get list of all the customers of MachineA application.

Let's develop the RMI application by following the steps.

1) Create the table

First of all, we need to create the table in the database. Here, we are using Oracle10
database.

2) Create Customer class and Remote interface

File: Customer.java

1. package com.javatpoint;  
2. public class Customer implements java.io.Serializable{  
3.     private int acc_no;  
4.     private String firstname,lastname,email;  
5.     private float amount;  
6. //getters and setters  
7. }  

Note: Customer class must be Serializable.

File: Bank.java
1. package com.javatpoint;  
2. import java.rmi.*;  
3. import java.util.*;  
4. interface Bank extends Remote{  
5. public List<Customer> getCustomers()throws RemoteException;  
6. }  

3) Create the class that provides the implementation of Remote


interface

File: BankImpl.java

1. package com.javatpoint;  
2. import java.rmi.*;  
3. import java.rmi.server.*;  
4. import java.sql.*;  
5. import java.util.*;  
6. class BankImpl extends UnicastRemoteObject implements Bank{  
7. BankImpl()throws RemoteException{}  
8.   
9. public List<Customer> getCustomers(){  
10. List<Customer> list=new ArrayList<Customer>();  
11. try{  
12. Class.forName("oracle.jdbc.driver.OracleDriver");  
13. Connection con=DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe","syste
m","oracle");  
14. PreparedStatement ps=con.prepareStatement("select * from customer400");  
15. ResultSet rs=ps.executeQuery();  
16.   
17. while(rs.next()){  
18. Customer c=new Customer();  
19. c.setAcc_no(rs.getInt(1));  
20. c.setFirstname(rs.getString(2));  
21. c.setLastname(rs.getString(3));  
22. c.setEmail(rs.getString(4));  
23. c.setAmount(rs.getFloat(5));  
24. list.add(c);  
25. }  
26.   
27. con.close();  
28. }catch(Exception e){System.out.println(e);}  
29. return list;  
30. }//end of getCustomers()  
31. }  
4) Compile the class rmic tool and start the registry service by
rmiregistry tool

5) Create and run the Server

File: MyServer.java

1. package com.javatpoint;  
2. import java.rmi.*;  
3. public class MyServer{  
4. public static void main(String args[])throws Exception{  
5. Remote r=new BankImpl();  
6. Naming.rebind("rmi://localhost:6666/javatpoint",r);  
7. }}  
6) Create and run the Client

File: MyClient.java

1. package com.javatpoint;  
2. import java.util.*;  
3. import java.rmi.*;  
4. public class MyClient{  
5. public static void main(String args[])throws Exception{  
6. Bank b=(Bank)Naming.lookup("rmi://localhost:6666/javatpoint");  
7.   
8. List<Customer> list=b.getCustomers();  
9. for(Customer c:list){  
10. System.out.println(c.getAcc_no()+" "+c.getFirstname()+" "+c.getLastname()  
11. +" "+c.getEmail()+" "+c.getAmount());  
12. }  
13.   
14. }}  

2.3 Creating the Stubs and


Skeletons
After you define the remote Java interface and implementation class, compile them
into Java bytecodes using a standard Java compiler. Then you use the RMI
stub/skeleton compiler, rmic , to generate the stub and skeleton interfaces that are
used at either end of the RMI communication link, as was shown in Figure 3-1. In its
simplest form, you can run rmic with the fully qualified classname of your
implementation class as the only argument. For example, once we’ve compiled the
Account and AccountImpl classes, we can generate the stubs and skeletons for the
remote Account object with the following command (Unix version):
% rmic AccountImpl

If the RMI compiler is successful, this command generates the stub and skeleton
classes, AccountImpl_Stub and AccountImpl_Skel, in the current directory. The
rmic compiler has additional arguments that let you specify where the generated
classes should be stored, whether to print warnings, etc. For example, if you want the
stub and skeleton classes to reside in the directory /usr/local/classes, you can run
the command using the -d option:

% rmic -d /usr/local/classes AccountImpl

This command generates the stub and skeleton classes in the specified directory.

2.4 Defining Remote Objects


Now that you have a basic idea of how Java RMI works, we can explore the details of
creating and using distributed objects with RMI in more detail. As mentioned earlier,
defining a remote RMI object involves specifying a remote interface for the object,
then providing a class that implements this interface. The remote interface and
implementation class are then used by RMI to generate a client stub and server
skeleton for your remote object. The communication between local objects and
remote objects is handled using these client stubs and server skeletons. The
relationships among stubs, skeletons, and the objects that use them are shown in
Figure 3-2.

Figure 3-2. Relationships among remote object, stub, and skeleton classes

When a client gets a reference to a remote object (details on how this reference is
obtained come later) and then calls methods on this object reference, there needs to be
a way for the method request to get transmitted back to the actual object on the remote
server and for the results of the method call to get transmitted back to the client. This
is what the generated stub and skeleton classes are for. They act as the communication
link between the client and your exported remote object, making it seem to the client
that the object actually exists within its Java VM.

Defining Remote Objects

Now that you have a basic idea of how Java RMI works, we can explore the details of
creating and using distributed objects with RMI in more detail. As mentioned earlier,
defining a remote RMI object involves specifying a remote interface for the object,
then providing a class that implements this interface. The remote interface and
implementation class are then used by RMI to generate a client stub and server
skeleton for your remote object. The communication between local objects and
remote objects is handled using these client stubs and server skeletons. The
relationships among stubs, skeletons, and the objects that use them are shown in
Figure 13-2.

Figure 13-2. Relationships among remote object, stub, and skeleton classes

When a client gets a reference to a remote object (details on how this reference is
obtained come later) and then calls methods on this object reference, there needs to be
a way for the method request to get transmitted back to the actual object on the remote
server and for the results of the method call to get transmitted back to the client. This
is what the generated stub and skeleton classes are for. They act as the communication
link between the client and your exported remote object, making it seem to the client
that the object actually exists within its Java VM.
Stubs and skeletons are generated from the classes that implement the remote RMI
interfaces that you've exported. The process of generating these stub and skeleton
classes is done either dynamically at runtime (in JDK 1.5 or later JVMs) or by using
the RMI compiler . Whether you use the RMI compiler or use dynamic stub and
skeleton generation, the role of these classes in the RMI runtime are the same. The
stub and skeleton classes act as go-betweens for the client application and the actual
server object, respectively. The client stub class implements the remote interface that
you defined, and its implementations of each remote method package up (marshal) the
method arguments provided by the client and transmit them to the server. The
skeleton class is responsible for receiving the method arguments from the RMI
runtime after they've been transmitted over the network. It unpackages (unmarshals)
the method arguments and makes the corresponding method call on the actual remote
object implementation on the server. Whatever response the method call generates
(return data or an exception), the results are packaged and transmitted back to the
remote client by the skeleton and the RMI runtime working together. The client stub
method (which is still executing at this point) unpackages the results and delivers
them to the client as the result of its remote method call.

As we saw earlier in the chapter, the first step in creating your remote objects is to
define the remote interfaces for the types of objects you need to use in a distributed
object context. This isn't much different from defining the public interfaces in a
nondistributed application, with the following exceptions:

Every object you want to distribute using RMI has to directly or indirectly
extend an interface that extends the java.rmi.Remote interface.

Every method in the remote interface has to declare that it throws a


java.rmi.RemoteException or one of the parent classes of
RemoteException.[*]

[*]
Note that prior to Java 1.2, the RMI
specification required that every method on a
remote interface had to throw RemoteException
specifically. In Java 1.2 and later, this
restriction was loosened to allow any superclass of
RemoteException. One reason for this change is to
make it easier to define generic interfaces that
support both local and remote objects.

RMI imposes the first requirement to allow it to differentiate quickly between objects
that are enabled for remote distribution and those that aren't. As we've already seen,
during a remote method invocation, the RMI runtime system needs to be able to
determine whether each argument to the remote method is a Remote object or not. The
Remote interface, which is simply a tag interface that marks remote objects, makes it
easy to perform this check.

The second requirement is needed to deal with errors that can happen during a remote
session. When a client makes a method call on a remote object, any number of errors
can occur, preventing the remote method call from completing. These include client-
side errors (e.g., an argument can't be marshaled), errors during the transport of data
between client and server (e.g., the network connection is dropped), and errors on the
server side (e.g., the method throws a local exception that needs to be sent back to the
remote caller). The RemoteException class is used by RMI as a base exception class
for any of the different types of problems that might occur during a remote method
call. Any method you declare in a Remote interface is assumed to be remotely
callable, so every method has to declare that it might throw a RemoteException or
one of its parent interfaces.

In Example 13-1 we saw the Account interface, a remote interface that declares six
remote methods: getName( ), getBalance( ), withdraw( ), deposit( ) and
TRansfer( ). Since we want to use this interface in an RMI setting, we've declared
that the interface extends the Remote interface. In addition, each method has
arguments and return values that are either Remote or Serializable, and each
method is declared as throwing a RemoteException.

With the remote interface defined, the next thing we need to do is write a class that
implements the interface. We saw the implementation of the Account interface, the
AccountImpl class, in Example 13-2. This class has implementations of the six
remote methods defined in the Account interface; it also has a nonremote method,
checkTransfer( ), to verify that a funds transfer between two accounts can be
made. Notice that the checkTransfer( ) method doesn't have to be declared as
throwing a RemoteException, since it isn't a remotely callable method. Only the
methods that participate in the remote method protocol of the RMI runtime need to
declare that they can throw RemoteException or one of its parent exceptions. The
methods in your implementation class that participate in the RMI runtime include
implementations of any methods declared in the remote interface (withdraw( ),
transfer( ), etc. in our example) and, if you're extending UnicastRemoteObject,
any constructors on your implementation class. Any other methods you define in your
implementation class (like checkTransfer( )) are considered nonremote (i.e., they
are callable only from within the local Java virtual machine where the object exists).

Constructors are required to have RemoteException in their throws clause because an


RMI implementation class that extends UnicastRemoteObject is "exported" and
made accessible to remote method invocations when it is constructed. The
RemoteException in the throws clause of the constructor is required to cover any
problems that may occur during the RMI export process. All of the constructors on
UnicastRemoteObject throw RemoteException because they all call the static
UnicastRemoteObject.exportObject( ) methods internally, so the Java compiler
will force you to include RemoteException in the throws clause of your
implementation class.

13.3.1. Key RMI Classes for Remote Object Implementations

As you have probably noticed, our AccountImpl class also extends the
UnicastRemoteObject class. This is a class in the java.rmi.server package that
extends java.rmi.server.RemoteServer, which itself extends
java.rmi.server.RemoteObject, the base class for all RMI remote objects. Four
key classes are related to writing server object implementations:
RemoteObject

RemoteObject implements both the Remote and Serializable interfaces.


Although the RemoteObject class is in the java.rmi.server package, it
is used by both the client and server portions of a remote object
reference. Both client stubs and server implementations are
subclassed (directly or indirectly) from RemoteObject. A RemoteObject
contains the remote reference for a particular remote object.

RemoteObject is an abstract class that reimplements the equals( ),


hashCode( ), and toString( ) methods inherited from Object in a way
that makes sense and is practical for remote objects. The equals( )
method, for example, is implemented to return true if the internal
remote references of the two RemoteObject objects are equal (i.e., if
they both point to the same server object).

RemoteServer

RemoteServer is an abstract class that extends RemoteObject. It


defines a set of static methods that are useful for implementing
server objects in RMI, and it acts as a base class for classes that
define various semantics for remote objects. In principle, a remote
object can behave according to a simple point-to-point reference
scheme; it can have replicated copies of itself scattered across the
network that need to be kept synchronized or any number of other
scenarios. JDK 1.1 supported only point-to-point, nonpersistent
remote references with the UnicastRemoteObject class. JDK 1.2
introduced the RMI activation system, so current versions of the core
Java API include another subclass of RemoteServer: Activatable.

UnicastRemoteObject

This is a concrete subclass of RemoteServer that implements point-to-


point remote references over TCP/IP networks. These references are
nonpersistent: remote references to a server object are valid only
during the lifetime of the server object. Before the server object is
created (inside a virtual machine running on the host) or after the
object has been destroyed, a client can't obtain remote references to
the object. In addition, if the virtual machine containing the object
exits (intentionally or otherwise), any existing remote references on
clients become invalid and generate RemoteException objects if used.
Activatable

This concrete subclass of RemoteServer is part of the RMI object


activation facility and can be found in the java.rmi.activation
package. It implements a server object that supports persistent
remote references. If a remote method request is received on the
server host for an Activatable object and the target object is not
executing at the time, the object can be started automatically by the
RMI activation daemon.

2.5 Remote Object Activation


 Overview
 Activation Protocol
 Implementation Model for an "Activatable" Remote Object
 Activation Interfaces

7.1 Overview
Object activation is a mechanism for providing persistent references to objects and
managing the execution of object implementations. In RMI, activation allows objects
to begin execution on an as-needed basis. When an activatable remote object is
accessed (via a method invocation) if that remote object is not currently executing, the
system initiates the object's execution inside an appropriate JVM.

7.1.1 Terminology

An active object is a remote object that is instantiated and exported in a JVM on some
system. A passive object is one that is not yet instantiated (or exported) in a JVM, but
which can be brought into an active state. Transforming a passive object into an active
object is a process known as activation. Activation requires that an object be
associated with a JVM, which may entail loading the class for that object into a JVM
and the object restoring its persistent state (if any).

In the RMI system, we use lazy activation. Lazy activation defers activating an object
until a client's first use (i.e., the first method invocation).

7.1.2 Lazy Activation

Lazy activation of remote objects is implemented using a faulting remote reference


(sometimes referred to as a fault block). A faulting remote reference to a remote
object "faults in" the active object's reference upon the first method invocation to the
object. Each faulting reference maintains both a persistent handle (an activation
identifier) and a transient remote reference to the target remote object. The remote
object's activation identifier contains enough information to engage a third party in
activating the object. The transient reference is the actual "live" reference to the active
remote object that can be used to contact the executing object.

7.2 Activation Protocol


During a remote method invocation, if the "live" reference for a target object is
unknown, the faulting reference engages in the activation protocol. The activation
protocol involves several entities: the faulting reference, the activator, an activation

An activation group (one per JVM) is the entity which receives a request to activate
an object in the JVM and returns the activated object back to the activator.

The activation protocol is as follows. A faulting reference uses an activation identifier


and calls the activator (an internal RMI interface) to activate the object associated
with the identifier. The activator looks up the object's activation descriptor (registered
previously). The object's descriptor contains:

 the object's group identifier, specifying the JVM in which it is activated,


 the object's class name,
 a URL path from which to load the object's class code, and
 object-specific initialization data in marshalled form (initialization data might be the name of
a file containing the object's persistent state, for example).

If the activation group in which this object should reside exists, the activator forwards
the activation request to that group. If the activation group does not exist, the activator
initiates a JVM executing an activation group and then forwards the activation request
to that group.

The activation group loads the class for the object and instantiates the object using a
special constructor that takes several arguments, including the activation descriptor
registered previously.

When the object is finished activating, the activation group passes back a marshalled
object reference to the activator that then records the activation identifier and active
reference pairing and returns the active (live) reference to the faulting reference. The
faulting reference (inside the stub) then forwards method invocations via the live
reference directly to the remote object.

Note: In Java 2 SDK, Standard Edition, v 1.2, RMI provides an implementation of the
activation system interfaces. In order to use activation, you must first run the
activation system daemon rmid.

7.3 Implementation Model for an "Activatable"


Remote Object
In order to make a remote object that can be accessed via an activation identifier over
time, a developer needs to:

 register an activation descriptor for the remote object, and


 include a special constructor in the object's class that the RMI system calls when it activates
the activatable object.

An activation descriptor (ActivationDesc) can be registered in one of several ways:

 via a call to the static register method of the class Activatable, or


 by creating an "activatable" object via the first or second constructor of the Activatable
class, or
 by exporting an "activatable" object explicitly via Activatable's first or second
exportObject method.

For a specific object, only one of the above methods should be used to register the
object for activation. See the section below on "Constructing an Activatable Remote
Object" for examples on how to implement activatable objects.

7.3.1 The ActivationDesc Class

An ActivationDesc contains the information necessary to activate an object. It


contains the object's activation group identifier, the class name for the object, a
codebase path (or URLs) from which the object's code can be loaded, and a
MarshalledObject that may contain object-specific initialization data used during
each activation.

A descriptor registered with the activation system is consulted (during the activation
process) to obtain information in order to re-create or activate an object. The
MarshalledObject in the object's descriptor is passed as the second argument to the
remote object's constructor for the object to use during activation.

package java.rmi.activation;
public final class ActivationDesc implements java.io.Serializable
{

public ActivationDesc(String className,


String codebase,
java.rmi.MarshalledObject data)
throws ActivationException;

public ActivationDesc(String className,


String codebase,
java.rmi.MarshalledObject data,
boolean restart)
throws ActivationException;

public ActivationDesc(ActivationGroupID groupID,


String className,
String codebase,
java.rmi.MarshalledObject data,
boolean restart);

public ActivationDesc(ActivationGroupID groupID,


String className,
String codebase,
java.rmi.MarshalledObject data);
public ActivationGroupID getGroupID();

public String getClassName();

public String getLocation();

public java.rmi.MarshalledObject getData()

public boolean getRestartMode();


}

7.3.2 The ActivationID Class

The activation protocol makes use of activation identifiers to denote remote objects
that can be activated over time. An activation identifier (an instance of the class
ActivationID) contains several pieces of information needed for activating an
object:

 a remote reference to the object's activator, and


 a unique identifier for the object.

An activation identifier for an object can be obtained by registering an object with the
activation system. Registration is accomplished in a few ways (also noted above):

 via the Activatable.register method, or


 via the first or second Activatable constructor, which both registers and exports the
object, or
 via the first or second Activatable.exportObject method, this method both
registers and exports the object.

package java.rmi.activation;
public class ActivationID implements java.io.Serializable
{
public ActivationID(Activator activator);

public Remote activate(boolean force)


throws ActivationException, UnknownObjectException,
java.rmi.RemoteException;

public boolean equals(Object obj);

public int hashCode();


}

7.3.3 The Activatable Class

The Activatable class provides support for remote objects that require persistent
access over time and that can be activated by the system. The class Activatable is
the main API that developers need to use to implement and manage activatable
objects. Note that you must first run the activation system daemon, rmid, before
objects can be registered and/or activated.

package java.rmi.activation;
public abstract class Activatable
extends java.rmi.server.RemoteServer
{
protected Activatable(String codebase,
java.rmi.MarshalledObject data,
boolean restart,
int port)
throws ActivationException, java.rmi.RemoteException;

protected Activatable(String codebase,


java.rmi.MarshalledObject data,
boolean restart, int port,
RMIClientSocketFactory csf,
RMIServerSocketFactory ssf)
throws ActivationException, java.rmi.RemoteException;

protected Activatable(ActivationID id, int port)


throws java.rmi.RemoteException;

protected Activatable(ActivationID id, int port,


RMIClientSocketFactory csf,
RMIServerSocketFactory ssf)
throws java.rmi.RemoteException;

protected ActivationID getID();

public static Remote register(ActivationDesc desc)


throws UnknownGroupException, ActivationException,
java.rmi.RemoteException;

public static boolean inactive(ActivationID id)


throws UnknownObjectException, ActivationException,
java.rmi.RemoteException;

public static void unregister(ActivationID id)


throws UnknownObjectException, ActivationException,
java.rmi.RemoteException;

public static ActivationID exportObject(Remote obj,


String codebase,
MarshalledObject
data,
boolean restart,
int port)
throws ActivationException, java.rmi.RemoteException;

public static ActivationID exportObject(Remote obj,


String codebase,
MarshalledObject
data,
boolean restart,
int port,

RMIClientSocketFactory csf,

RMIServerSocketFactory ssf)
throws ActivationException, java.rmi.RemoteException;

public static Remote exportObject(Remote obj,


ActivationID id,
int port)
throws java.rmi.RemoteException;

public static Remote exportObject(Remote obj,


ActivationID id,
int port,
RMIClientSocketFactory csf,
RMIServerSocketFactory ssf)
throws java.rmi.RemoteException;

public static boolean unexportObject(Remote obj, boolean


force)
throws java.rmi.NoSuchObjectException;
}

Registering an Activation Descriptor Without Creating the


Object

To register an activatable remote object with the activation system without first
creating the object, the programmer can simply register an activation descriptor (an
instance of the class ActivationDesc) for the object. An activation descriptor
contains all the necessary information so that the activation system can activate the
object when needed. An activation descriptor for an instance of the class
examples.ServerImpl can be registered in the following manner (exception
handling elided):

Server server;
ActivationDesc desc;
String codebase = "http://zaphod/codebase/";

MarshalledObject data = new MarshalledObject("some data");


desc = new ActivationDesc( "examples.ServerImpl", codebase, data);
server = (Server)Activatable.register(desc);

The register call returns a Remote stub that is the stub for the
examples.ServerImpl object and implements the same set of remote interfaces that
examples.ServerImpl implements (i.e, the stub implements the remote interface
Server). This stub object (above, cast and assigned to server) can be passed as a
parameter in any method call expecting an object that implements the
examples.Server remote interface.

7.4 Activation Interfaces


In the RMI activation protocol, there are two guarantees that the activator must make
for the system to function properly:

 like all system daemons, the activator should remain running while the machine is up, and
 the activator must not reactivate remote objects that are already active.

The activator maintains a database of appropriate information for the groups and
objects that it participates in activating.
7.4.1 The Activator Interface

The activator is one of the entities that participates during the activation process. As
described earlier, a faulting reference (inside a stub) calls the activator's activate
method to obtain a "live" reference to an activatable remote object. Upon receiving a
request for activation, the activator looks up the activation descriptor for the
activation identifier, id, determines the group in which the object should be activated,
and invokes the newInstance method on the activation group's instantiator (the
remote interface ActivationGroup is described below). The activator initiates the
execution of activation groups as necessary. For example, if an activation group for a
specific group descriptor is not already executing, the activator will spawn a child
JVM for the activation group to establish the group in the new JVM.

The activator is responsible for monitoring and detecting when activation groups fail
so that it can remove stale remote references from its internal tables.

package java.rmi.activation;
public interface Activator extends java.rmi.Remote
{
java.rmi.MarshalledObject activate(ActivationID id,
boolean force)
throws UnknownObjectException, ActivationException,
java.rmi.RemoteException;
}

7.4.2 The ActivationSystem Interface

The ActivationSystem provides a means for registering groups and activatable


objects to be activated within those groups. The ActivationSystem works closely
with both the Activator, which activates objects registered via the
ActivationSystem, and the ActivationMonitor, which obtains information about
active and inactive objects and inactive groups.

package java.rmi.activation;
public interface ActivationSystem extends java.rmi.Remote
{
public static final int SYSTEM_PORT = 1098;

ActivationGroupID registerGroup(ActivationGroupDesc desc)


throws ActivationException, java.rmi.RemoteException;

ActivationMonitor activeGroup(ActivationGroupID id,


ActivationInstantiator group,
long incarnation)
throws UnknownGroupException, ActivationException,
java.rmi.RemoteException;

void unregisterGroup(ActivationGroupID id)


throws ActivationException, UnknownGroupException,
java.rmi.RemoteException;

ActivationID registerObject(ActivationDesc desc)


throws ActivationException, UnknownGroupException,
java.rmi.RemoteException;
void unregisterObject(ActivationID id)
throws ActivationException, UnknownObjectException,
java.rmi.RemoteException;

void shutdown() throws java.rmi.RemoteException;


}

Note: As a security measure, all of the above methods (registerGroup,


activeGroup, unregisterGroup, registerObject, unregisterObject, and
shutdown) will throw java.rmi.AccessException, a subclass of
java.rmi.RemoteException, if called from a client that does not reside on the same
host as the activation system.

7.4.3 The ActivationMonitor Class

An ActivationMonitor is specific to an ActivationGroup and is obtained when a


group is reported via a call to ActivationSystem.activeGroup (this is done
internally by the ActivationGroup.createGroup method). An activation group is
responsible for informing its ActivationMonitor when:

1. its objects become active,


2. its objects become inactive, or
3. the group as a whole becomes inactive.

package java.rmi.activation;
public interface ActivationMonitor
extends java.rmi.Remote
{

public abstract void inactiveObject(ActivationID id)


throws UnknownObjectException, RemoteException;

public void activeObject(ActivationID id,


java.rmi.MarshalledObject mobj)
throws UnknownObjectException,
java.rmi.RemoteException;

public void inactiveGroup(ActivationGroupID id,


long incarnation)
throws UnknownGroupException,
java.rmi.RemoteException;
}

An activation group calls its monitor's inactiveObject method when an object in its
group becomes inactive (deactivates). An activation group discovers that an object
(that it participated in activating) in its JVM is no longer active via a call to the
activation group's inactiveObject method.

7.4.4 The ActivationInstantiator Class


The ActivationInstantiator is responsible for creating instances of activatable
objects. A concrete subclass of ActivationGroup implements the newInstance
method to handle creating objects within the group.

package java.rmi.activation;
public interface ActivationInstantiator
extends java.rmi.Remote
{
public MarshalledObject newInstance(ActivationID id,
ActivationDesc desc)
throws ActivationException, java.rmi.RemoteException;

The activator calls an instantiator's newInstance method in order to re-create in that


group an object with the activation identifier, id, and descriptor, desc. The instantiator
is responsible for:

 determining the class for the object using the descriptor's getClassName method,
 loading the class from the codebase path obtained from the descriptor (using the
getLocation method),
 creating an instance of the class by invoking the special "activation" constructor of the
object's class that takes two arguments: the object's ActivationID, and the
MarshalledObject containing object-specific initialization data, and
 returning a MarshalledObject containing the remote object it created.

An instantiator is also responsible for reporting when objects it creates or activates are
no longer active, so that it can make the appropriate inactiveObject call to its
ActivationMonitor (see the ActivationGroup class for more details).

If object activation fails, then the newInstance method throws


ActivationException. If the remote call fails, then the method throws
RemoteException.

7.4.5 The ActivationGroupDesc Class

An activation group descriptor (ActivationGroupDesc) contains the information


necessary to create or re-create an activation group in which to activate objects in the
same JVM.

Such a descriptor contains:

 the group's class name (a class name of null indicates the default ActivationGroup
implementation),
 the group's codebase path (the location of the group's class), and
 a "marshalled" object that can contain object-specific initialization data.

The group's class must be a concrete subclass of ActivationGroup. A subclass of


ActivationGroup is created or re-created via the ActivationGroup.createGroup
static method, which invokes a special constructor that takes two arguments:

 the group's ActivationGroupID, and


 the group's initialization data (in a java.rmi.MarshalledObject)

package java.rmi.activation;
public final class ActivationGroupDesc
implements java.io.Serializable
{

public ActivationGroupDesc(java.util.Properties props,


CommandEnvironment env);;

public ActivationGroupDesc(String className,


String codebase,
java.rmi.MarshalledObject data,
java.util.Properties props,
CommandEnvironment env);

public String getClassName();

public String getLocation();

public java.rmi.MarshalledObject getData();

public CommandEnvironment getCommandEnvironment();

public java.util.Properties getPropertiesOverrides();

The first constructor creates a group descriptor that uses system default for group
implementation and code location. Properties specify Java application environment
overrides (which will override system properties in the group implementation's JVM).
The command environment can control the exact command/options used in starting
the child JVM, or can be null to accept rmid's default. This constructor will create an
ActivationGroupDesc with a null group class name, which indicates the system's
default ActivationGroup implementation.

The second constructor is the same as the first, but allows the specification of
Properties and CommandEnvironment.

The getClassName method returns the group's class name (possibly null). A null
group class name indicates the system's default ActivationGroup implementation.

The getLocation method returns the codebase path from where the group's class can
be loaded.

The getData method returns the group's initialization data in marshalled form.

The getCommandEnvironment method returns the command environment (possibly


null).

The getPropertiesOverrides method returns the properties overrides (possibly


null) for this descriptor.
7.4.6 The ActivationGroupDesc.CommandEnvironment Class

The CommandEnvironment class allows overriding default system properties and


specifying implemention-defined options for an ActivationGroup.

public static class CommandEnvironment


implements java.io.Serializable
{
public CommandEnvironment(String cmdpath, String[] args);
public boolean equals(java.lang.Object);
public String[] getCommandOptions();
public String getCommandPath();
public int hashCode();

The constructor creates a CommandEnvironment with the given command, cmdpath,


and additional command line options, args.

The equals implements content equality for command environment objects. The
hashCode method is implemented appropriately so that a CommandEnvironment can
be stored in a hash table if necessary.

The getCommandOptions method returns the environment object's command line


options.

The getCommandPath method returns the environment object's command string.

7.4.7 The ActivationGroupID Class

The identifier for a registered activation group serves several purposes:

 it identifies the group uniquely within the activation system, and


 it contains a reference to the group's activation system so that the group can contact its
activation system when necessary.

The ActivationGroupID is returned from the call to


ActivationSystem.registerGroup and is used to identify the group within the
activation system. This group identifier is passed as one of the arguments to the
activation group's special constructor when an activation group is created or re-
created.

package java.rmi.activation;
public class ActivationGroupID implements java.io.Serializable
{
public ActivationGroupID(ActivationSystem system);

public ActivationSystem getSystem();

public boolean equals(Object obj);

public int hashCode();


}
The ActivationGroupID constructor creates a unique group identifier whose
ActivationSystem is system.

The getSystem method returns the activation system for the group.

The hashCode method returns a hashcode for the group's identifier. Two group
identifiers that refer to the same remote group will have the same hash code.

The equals method compares two group identifiers for content equality. The method
returns true if both of the following conditions are true: 1) the unique identifiers are
equivalent (by content), and 2) the activation system specified in each refers to the
same remote object.

7.4.8 The ActivationGroup Class

An ActivationGroup is responsible for creating new instances of activatable objects


in its group, informing its ActivationMonitor when:

1. its objects become active,


2. its objects become inactive, or
3. the group as a whole becomes inactive.

An ActivationGroup is initially created in one of several ways:

 as a side-effect of creating a "default" ActivationDesc for an object, or


 by an explicit call to the ActivationGroup.createGroup method, or
 as a side-effect of activating the first object in a group whose ActivationGroupDesc
was only registered.

Only the activator can re-create an ActivationGroup. The activator spawns, as


needed, a separate JVM (as a child process, for example) for each registered
activation group and directs activation requests to the appropriate group. It is
implementation specific how JVMs are spawned. An activation group is created via
the ActivationGroup.createGroup static method. The createGroup method has
two requirements on the group to be created: 1) the group must be a concrete subclass
of ActivationGroup, and 2) the group must have a constructor that takes two
arguments:

 the group's ActivationGroupID, and


 the group's initialization data (in a MarshalledObject)

When created, the default implementation of ActivationGroup will set the system
properties to the system properties in force when the ActivationGroupDesc was
created, and will set the security manager to the java.rmi.RMISecurityManager. If
your application requires some specific properties to be set when objects are activated
in the group, the application should set the properties before creating any
ActivationDescs (before the default ActivationGroupDesc is created).

package java.rmi.activation;
public abstract class ActivationGroup
extends UnicastRemoteObject
implements ActivationInstantiator
{
protected ActivationGroup(ActivationGroupID groupID)
throws java.rmi.RemoteException;

public abstract MarshalledObject newInstance(ActivationID id,


ActivationDesc
desc)
throws ActivationException, java.rmi.RemoteException;

public abstract boolean inactiveObject(ActivationID id)


throws ActivationException, UnknownObjectException,
java.rmi.RemoteException;

public static ActivationGroup createGroup(ActivationGroupID


id,
ActivationGroupDesc
desc,
long incarnation)
throws ActivationException;

public static ActivationGroupID currentGroupID();

public static void setSystem(ActivationSystem system)


throws ActivationException;

public static ActivationSystem getSystem()


throws ActivationException;

protected void activeObject(ActivationID id,


java.rmi.MarshalledObject mobj)
throws ActivationException, UnknownObjectException,
java.rmi.RemoteException;

protected void inactiveGroup()


throws UnknownGroupException,
java.rmi.RemoteException;

The activator calls an activation group's newInstance method in order to activate an


object with the activation descriptor, desc. The activation group is responsible for:

 determining the class for the object using the descriptor's getClassName method,
 loading the class from the URL path obtained from the descriptor (using the getLocation
method),
 creating an instance of the class by invoking the special constructor of the object's class that
takes two arguments: the object's ActivationID, and a MarshalledObject
containing the object's initialization data, and
 returning a serialized version of the remote object it just created to the activator.

The method throws ActivationException if the instance for the given descriptor
could not be created.

7.4.9 The MarshalledObject Class


A MarshalledObject is a container for an object that allows that object to be passed
as a parameter in an RMI call, but postpones deserializing the object at the receiver
until the application explicitly requests the object (via a call to the container object).
The Serializable object contained in the MarshalledObject is serialized and
deserialized (when requested) with the same semantics as parameters passed in RMI
calls, which means that any remote object in the MarshalledObject is represented by
a serialized instance of its stub. The object contained by the MarshalledObject may
be a remote object, a non-remote object, or an entire graph of remote and non-remote
objects.

When an object is placed inside the MarshalledObject wrapper, the serialized form
of the object is annotated with the codebase URL (where the class can be loaded);
likewise, when the contained object is retrieved from its MarshalledObject wrapper,
if the code for the object is not available locally, the URL (annotated during
serialization) is used to locate and load the bytecodes for the object's class.

package java.rmi;
public final class MarshalledObject implements java.io.Serializable
{
public MarshalledObject(Object obj)
throws java.io.IOException;

public Object get()


throws java.io.IOException, ClassNotFoundException;

public int hashCode();

public boolean equals();


}

MarshalledObject's constructor takes a serializable object, obj, as its single


argument and holds the marshalled representation of the object in a byte stream. The
marshalled representation of the object preserves the semantics of objects that are
passed in RMI calls:

 each class in the stream is annotated with its codebase URL so that when the object is
reconstructed (by a call to the get method), the bytecodes for each class can be located and
loaded, and
 remote objects are replaced with their proxy stubs.

2.6 Serialization and


Deserialization in Java
1. Serialization
2. Serializable Interface
3. Example of Serialization
4. Example of Deserialization
5. Serialization with Inheritance
6. Externalizable interface
7. Serialization and static data member

Serialization in Java is a mechanism of writing the state of an object into a byte-


stream. It is mainly used in Hibernate, RMI, JPA, EJB and JMS technologies.

The reverse operation of serialization is called deserialization where byte-stream is


converted into an object. The serialization and deserialization process is platform-
independent, it means you can serialize an object in a platform and deserialize in
different platform.

For serializing the object, we call the writeObject() method ObjectOutputStream, and
for deserialization we call the readObject() method of ObjectInputStream class.

We must have to implement the Serializable interface for serializing the object.

Advantages of Java Serialization

It is mainly used to travel object's state on the network (which is known as


marshaling).

java.io.Serializable interface
Serializable is a marker interface (has no data member and method). It is used to
"mark" Java classes so that the objects of these classes may get a certain capability.
The Cloneable and Remote are also marker interfaces.

It must be implemented by the class whose object you want to persist.

The String class and all the wrapper classes implement the java.io.Serializable
interface by default.

Let's see the example given below:

1. import java.io.Serializable;
2. public class Student implements Serializable{
3.  int id;
4.  String name;
5.  public Student(int id, String name) {
6.   this.id = id;
7.   this.name = name;
8.  }
9. }

In the above example, Student class implements Serializable interface. Now its objects
can be converted into stream.

ObjectOutputStream class

The ObjectOutputStream class is used to write primitive data types, and Java objects
to an OutputStream. Only objects that support the java.io.Serializable interface can be
written to streams.

Constructor

1) public creates an ObjectOutputStream


ObjectOutputStream(OutputStream out) that writes to the specified
throws IOException {} OutputStream.
Important Methods

Method Description
1) public final void
writes the specified object to
writeObject(Object obj) throws
the ObjectOutputStream.
IOException {}
2) public void flush() throws flushes the current output
IOException {} stream.
3) public void close() throws closes the current output
IOException {} stream.
ObjectInputStream class

An ObjectInputStream deserializes objects and primitive data written using an


ObjectOutputStream.
Constructor

1) public creates an ObjectInputStream


ObjectInputStream(InputStream in) that reads from the specified
throws IOException {} InputStream.
Important Methods

Method Description
1) public final Object readObject() throws reads an object from the
IOException, ClassNotFoundException{} input stream.
2) public void close() throws IOException
closes ObjectInputStream.
{}

Example of Java Serialization


In this example, we are going to serialize the object of Student class. The
writeObject() method of ObjectOutputStream class provides the functionality to
serialize the object. We are saving the state of the object in the file named f.txt.

1. import java.io.*;
2. class Persist{
3.  public static void main(String args[]){
4.   try{
5.   //Creating the object
6.   Student s1 =new Student(211,"ravi");
7.   //Creating stream and writing the object
8.   FileOutputStream fout=new FileOutputStream("f.txt");
9.   ObjectOutputStream out=new ObjectOutputStream(fout);
10.   out.writeObject(s1);
11.   out.flush();
12.   //closing the stream
13.   out.close();
14.   System.out.println("success");
15.   }catch(Exception e){System.out.println(e);}
16.  }
17. }

success
download this example of serialization

Example of Java Deserialization

Deserialization is the process of reconstructing the object from the serialized state. It is
the reverse operation of serialization. Let's see an example where we are reading the
data from a deserialized object.

1. import java.io.*;
2. class Depersist{
3.  public static void main(String args[]){
4.   try{
5.   //Creating stream to read the object
6.   ObjectInputStream in=new ObjectInputStream(new FileInputStream("f.txt"));
7.   Student s=(Student)in.readObject();
8.   //printing the data of the serialized object
9.   System.out.println(s.id+" "+s.name);
10.   //closing the stream
11.   in.close();
12.   }catch(Exception e){System.out.println(e);}
13.  }
14. }

211 ravi
download this example of deserialization

Java Serialization with Inheritance (IS-A


Relationship)
If a class implements serializable then all its sub classes will also be serializable. Let's
see the example given below:

1. import java.io.Serializable;
2. class Person implements Serializable{
3.  int id;
4.  String name;
5.  Person(int id, String name) {
6.   this.id = id;
7.   this.name = name;
8.  }
9. }

1. class Student extends Person{
2.  String course;
3.  int fee;
4.  public Student(int id, String name, String course, int fee) {
5.   super(id,name);
6.   this.course=course;
7.   this.fee=fee;
8.  }
9. }

Now you can serialize the Student class object that extends the Person class which is
Serializable. Parent class properties are inherited to subclasses so if parent class is
Serializable, subclass would also be.

Java Serialization with Aggregation (HAS-A


Relationship)
If a class has a reference to another class, all the references must be Serializable
otherwise serialization process will not be performed. In such case,
NotSerializableException is thrown at runtime.

1. class Address{
2.  String addressLine,city,state;
3.  public Address(String addressLine, String city, String state) {
4.   this.addressLine=addressLine;
5.   this.city=city;
6.   this.state=state;
7.  }
8. }

1. import java.io.Serializable;
2. public class Student implements Serializable{
3.  int id;
4.  String name;
5.  Address address;//HAS-A
6.  public Student(int id, String name) {
7.   this.id = id;
8.   this.name = name;
9.  }
10. }

Since Address is not Serializable, you can not serialize the instance of Student class.

Note: All the objects within an object must be Serializable.


Java Serialization with the static data member
If there is any static data member in a class, it will not be serialized because static is
the part of class not object.

1. class Employee implements Serializable{
2.  int id;
3.  String name;
4.  static String company="SSS IT Pvt Ltd";//it won't be serialized
5.  public Student(int id, String name) {
6.   this.id = id;
7.   this.name = name;
8.  }
9. }

Java Serialization with array or collection


Rule: In case of array or collection, all the objects of array or collection must be
serializable. If any object is not serialiizable, serialization will be failed.

Externalizable in java
The Externalizable interface provides the facility of writing the state of an object into a
byte stream in compress format. It is not a marker interface.

The Externalizable interface provides two methods:

 public void writeExternal(ObjectOutput out) throws IOException


 public void readExternal(ObjectInput in) throws IOException

Java Transient Keyword


If you don't want to serialize any data member of a class, you can mark it as transient.

1. class Employee implements Serializable{
2.  transient int id;
3.  String name;
4.  public Student(int id, String name) {
5.   this.id = id;
6.   this.name = name;
7.  }
8. }

Now, id will not be serialized, so when you deserialize the object after serialization,
you will not get the value of id. It will return default value always. In such case, it will
return 0 because the data type of id is an integer.

Visit next page for more details.

SerialVersionUID
The serialization process at runtime associates an id with each Serializable class which
is known as SerialVersionUID. It is used to verify the sender and receiver of the
serialized object. The sender and receiver must be the same. To verify it,
SerialVersionUID is used. The sender and receiver must have the same
SerialVersionUID, otherwise, InvalidClassException will be thrown when you
deserialize the object. We can also declare our own SerialVersionUID in the
Serializable class. To do so, you need to create a field SerialVersionUID and assign a
value to it. It must be of the long type with static and final. It is suggested to explicitly
declare the serialVersionUID field in the class and have it private also. For example:

1. private static final long serialVersionUID=1L;

Now, the Serializable class will look like this:

1. import java.io.Serializable;
2. class Employee implements Serializable{
3.  private static final long serialVersionUID=1L;
4.  int id;
5.  String name;
6.  public Student(int id, String name) {
7.   this.id = id;
8.   this.name = name;
9.  }
10. }
2.7 The JavaSpaces Technology Application Model

A JavaSpaces service holds entries, each of which is a typed group of objects


expressed in a class that implements the interface net.jini.core.entry.Entry.
Once an entry is written into a JavaSpaces service, it can be used in future look-up
operations. Looking up entries is performed using templates, which are entry objects
that have some or all of their fields set to specified values that must be matched
exactly. All remaining fields, which are not used in the lookup, are left as wildcards.

There are two look-up operations: read() and take(). The read() method returns
either an entry that matches the template or an indication that no match was found.
The take() method operates like read(), but if a match is found, the entry is
removed from the space. Distributed events can be used by requesting a JavaSpaces
service to notify you when an entry that matches the specified template is written into
the space. Note that each entry in the space can be taken at most once, but two or
more entries may have the exact same values.

Using JavaSpaces technology, distributed applications are modeled as a flow of


objects between participants, which is different from classic distributed models such
as RMIs. Figure 1 indicates what a JavaSpaces technology-based application looks
like.

Figure 1: A Typical JavaSpaces Technology Application

As you can see, a client can interact with as many JavaSpaces services as needed.
Clients perform operations that map entries to templates onto JavaSpaces services.
Such operations can be singleton or contained in a transaction so that all or none of
the operations take place. Notifications go to event catches, which can be either
clients or proxies for clients.

To get a flavor of how to implement distributed applications using a handful of


JavaSpaces operations, consider a multiuser chat system. All the messages that make
up the discussion are written to a space that acts as a chat area. Participants write
message objects into the space, while other members wait for new message objects to
appear, then read them out and display their contents. The list of participants can be
kept in the space and updated whenever someone joins or leaves the discussion.
Because the space is persistent, a new member can read and view the entire
discussion.

You can implement such a multiuser chat system in RMI by creating remote
interfaces for the interactions discussed. But by using JavaSpaces technology, you
need only one interface.

You might also like